复合 Id 和 Restrictions.IdEq 或 Linq 中的比较未按预期工作
我有一个使用复合 id 的实体。我更改为代码以利用将复合 id 包装在单独的键类中。我期望使用 Linq 可以对关键对象进行比较,并使用 Criteria API 来使用 Restrictions.IdEq,但两者都失败了。我需要明确比较关键值才能使其发挥作用。
如果这应该有效,我找不到任何文档,所以目前我只能进行直接比较,但这意味着当我更改密钥时,我还需要更新查询代码,这显然不是我想要的。
作为旁注,我用 NHibernate 3.0.0 Alpha 2 和 3 尝试了这一点。
域
映射
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Cwc.Pulse.Dal"
namespace="Cwc.Pulse.Dal">
<class name="AddonStatus">
<composite-id name="Id">
<key-many-to-one name="Context" column="Context_Id" class="Context" />
<key-property name="AddonType" column="Addon_Id"/>
</composite-id>
<property name="Status" />
</class>
</hibernate-mapping>
类
public class AddonStatus
{
public virtual string Status { get; set; }
public virtual Key Id { get; protected set; }
public AddonStatus()
{
Id = new Key();
}
public class Key
{
public virtual Context Context { get; set; }
public virtual AddonType AddonType { get; set; }
public override int GetHashCode()
{
return ContextId.GetHashCode() ^ AddonType.GetHashCode();
}
public override bool Equals(object obj)
{
if (this == obj) return true;
var o = obj as Key;
if (null == o) return false;
return Context == o.Context && AddonType == o.AddonType;
}
}
}
工作查询
下面的查询有效,正如您所看到的,我明确地比较了键值。我不比较关键对象。
Linq
from status
in session.Query<AddonStatus>()
where status.Id.Context == context && status.Id.AddonType == addonType
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.Eq("Id.Context", context))
.Add(Restrictions.Eq("Id.AddonType", addonType))
预计可以工作,但
我不希望以下查询能够工作。要么在内存中而不是在数据库中有效地进行 linq,但我希望标准 api 足够智能来处理查询中的此类复合 id。
linq 和 criteria api 查询都使用 Key 对象比较。
var key = new AddonStatus.Key
{
Context = context,
AddonType = addonType
};
Linq
from status
in session.Query<AddonStatus>()
where status.Id == key
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.IdEq(key))
那么,如果有人有这样的场景,那么我做错了什么?
I have an entity where a composite id is used. I changed to code to make use of wrapping the composite id in a seperate key class. I expected that with Linq I could do a comparison on key object and with the Criteria API to use Restrictions.IdEq but both fail. I need to explicitly compare the key values to make it work.
I cannot find any documentation if this should work so for the moment I am stuck with direct comparisons but this means that when I alter the key that I also need to update the query code which is obviously not what I would want.
As a side note, I tried this with NHibernate 3.0.0 Alpha 2 and 3.
Domain
Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Cwc.Pulse.Dal"
namespace="Cwc.Pulse.Dal">
<class name="AddonStatus">
<composite-id name="Id">
<key-many-to-one name="Context" column="Context_Id" class="Context" />
<key-property name="AddonType" column="Addon_Id"/>
</composite-id>
<property name="Status" />
</class>
</hibernate-mapping>
Class
public class AddonStatus
{
public virtual string Status { get; set; }
public virtual Key Id { get; protected set; }
public AddonStatus()
{
Id = new Key();
}
public class Key
{
public virtual Context Context { get; set; }
public virtual AddonType AddonType { get; set; }
public override int GetHashCode()
{
return ContextId.GetHashCode() ^ AddonType.GetHashCode();
}
public override bool Equals(object obj)
{
if (this == obj) return true;
var o = obj as Key;
if (null == o) return false;
return Context == o.Context && AddonType == o.AddonType;
}
}
}
Working queries
The queries below work and as you can see I compare the key values explicitly. I do not compare the key object.
Linq
from status
in session.Query<AddonStatus>()
where status.Id.Context == context && status.Id.AddonType == addonType
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.Eq("Id.Context", context))
.Add(Restrictions.Eq("Id.AddonType", addonType))
Expected to work but dont
I expect the following queries to work. Either in efficiently for linq in memory instead of the database but I expect the criteria api to be smart enough to handle such composite id´s in queries.
Both linq and criteria api queries make use of a Key object comparison.
var key = new AddonStatus.Key
{
Context = context,
AddonType = addonType
};
Linq
from status
in session.Query<AddonStatus>()
where status.Id == key
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.IdEq(key))
So if anyone has such a scenario working then what am I doing wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不是直接回答您的问题,但无论如何它可能对您有用。您可以通过将 AddonStatus 映射为所有者(最有可能是 Context)上的复合元素来避免(显式)复合键:
在类中
Context
看起来像这样:这结果和数据库非常相同结构,但使用起来有所不同。我不能说这是否是您真正想要的,但它只是看起来像这样。
Not directly an answer to your question, but it may be useful to you anyway. You could avoid the (explicit) composite key by mapping the AddonStatus as composite-element on the owner (most probably the Context):
In the class
Context
is looks like this:This results and pretty the same database structure, but it is different to work with. I can't say if this is what you actually want, but it just looks like it.
有趣的是,我在 2.1.2 中得到的行为几乎与此相反。
我的映射(简化):
转到:
有效的东西:
无效的东西:
我认为这是它们的各种 ID 相等查询形式之间的不一致。当我有时间时,我将构建一个最小的单元测试以作为错误示例提交。我对任何人对此可能有的任何/所有想法感兴趣...
编辑:嘿,我想通了!
既然我已经阅读了 this
这将为 User 键创建一个代理对象,而不访问数据库(除非必要)。如果将
Load
替换为Get
,您将立即访问数据库来填充对象,而不是遵循延迟加载属性。使用负载。像这样的事情正是人们建议使用 (type)Repository 模式的原因 - 我可以在幕后执行此操作:
Get<>(new SK(){User=Load<>(key.User.Id) }
,并且仍然通过单个键Get(key)
,与所有其他对象相同。Interestingly, I'm getting almost the exact opposite of this behavior in 2.1.2.
My mapping (simplified):
goes to:
Things which work:
Things which don't work:
I'm thinking this is an inconsistency between their various ID-equaling query forms. When I get time, I'll be building a minimal unit test to submit as a bug example. I'd be interested in any / all thoughts anyone might have on this...
edit: Heeey, I figured it out!
Things which do work, now that I've read this
This will create a proxy object for the User key, without hitting the database (unless necessary). If you swap
Load<User>
forGet<User>
, you'll immediately hit the database to populate the object, rather than respecting your lazy-loading properties. Use Load.And things like this are precisely why people suggest the (type)Repository pattern - I can do this behind the scenes:
Get<>(new SK(){User=Load<>(key.User.Id)}
, and stillGet(key)
by a single key, identical to every other object.