我在ASP.NET MVC应用程序中使用Fluent nHibernate
作为持久层,我遇到了一些问题。
我有一种情况需要使用抽象来将对象存储到集合中,在这种情况下,如果您正在查看纯C#透视图,那么接口是最合理的选择。
基本上,对象(Item
)可以有Requirements
。要求可以是很多东西。在本地C#情况下,我只需使用以下代码完成此操作。
interface IRequirement
{
// methods and properties neccessary for evaluation
}
class Item
{
virtual int Id { get; set; }
virtual IList<IRequirement> Requirements { get; set; }
}
一个粗略的例子。这在本机C#中工作正常 - 但是因为对象必须存储在数据库中,所以它变得有点复杂。实现IRequirement
的每个对象可以是完全不同的种对象。由于nHibernate(或我发现的任何其他ORM)无法真正理解如何序列化接口,因此在我的生活中,我无法想到如何处理这种情况。我的意思是,我理解这个问题。
这对数据库/ orm没有意义。我完全理解为什么。
class SomeKindOfObject
{
virtual int Id { get; set; }
// ... some other methods relative to this base type
}
class OneRequirement : SomeKindOfObject, IRequirement
{
virtual string Name { get; set; }
// some more methods and properties
}
class AnotherKindOfObject
{
virtual int Id { get; set; }
// ... more methods and properties, different from SomeKindOfObject
}
class AnotherRequirement : AnotherKindOfObject, IRequirement
{
// yet more methods and properties relative to AnotherKindOfObject's intentive hierarchy
}
class OneRequirementMap : ClassMap<OneRequirement>
{
// etc
Table("OneRequirement");
}
class AnotherRequirementMap : ClassMap<AnotherRequirement>
{
//
Table("OtherRequirements");
}
class ItemMap : ClassMap<Item>
{
// ... Now we have a problem.
Map( x => x.Requirements ) // does not compute...
// additional mapping
}
那么,有没有人有任何想法?我似乎也无法使用泛型,因此建立一个基本的Requirement<T>
类似乎。我的意思是代码工作和运行,但ORM无法掌握它。我意识到我在这里问的可能是不可能的,但我能做的就是问。
我还想补充一点,我对nHibernate
没有太多经验,只有Fluent nHibernate
,但我已经意识到两个社区都非常好,因此我将这两个标记为。但我目前的映射是100%'流利'。
我实际上发现Programming to interfaces while mapping with Fluent NHibernate触及了这一点,但我仍然不确定它是否适用于我的场景。任何帮助表示赞赏。
我正在添加此更新以回复发布的部分答案,因为我的结果有点尴尬。
接受建议并做更多研究,我设计了一个基本界面。
interface IRequirement
{
// ... Same as it always was
}
现在我建立了我的类映射..
class IRequirementMap : ClassMap<IRequirement>
{
public IRequirementMap()
{
Id( x => x.Id );
UseUnionSubclassForInheritanceMapping();
Table("Requirements");
}
}
然后我映射实现它的东西。这就是非常怪异的地方。
class ObjectThatImplementsRequirementMap : ClassMap<ObjectThatImplementsRequirement>
{
ObjectThatImplementsRequirementMap()
{
Id(x => x.Id); // Yes, I am base-class mapping it.
// other properties
Table("ObjectImplementingRequirement");
}
}
class AnotherObjectThatHasRequirementMap : ClassMap<AnotherObjectThatHasRequirement>
{
AnotherObjectThatHasRequirementMap ()
{
Id(x => x.Id); // Yes, I am base-class mapping it.
// other properties
Table("AnotheObjectImplementingRequirement");
}
}
这不是人们的建议,但这是我的第一个方法。虽然我这样做是因为我得到了一些非常怪异的结果。结果确实让我感到没有。
它实际上有效... 排序 运行以下代码会产生意外结果。
// setup ISession
// setup Transaction
var requirements = new <IRequirement>
{
new ObjectThatImplementsRequirement
{
// properties, etc..
},
new AnotherObjectThatHasRequirement
{
// other properties.
}
}
// add to session.
// commit transaction.
// close writing block.
// setup new session
// setup new transaction
var requireables = session.Query<IRequirable>();
foreach(var requireable in requireables)
Console.WriteLine( requireable.Id );
现在事情变得怪异了。我得到了结果......
1
1
这对我没有意义。它应该不起作用。我甚至可以查询每个对象的各个属性,并且它们保留了它们的类型。即使我运行插入,关闭应用程序,然后运行检索(以避免缓存的可能性),他们仍然有正确的类型。但以下不有效。
class SomethingThatHasRequireables
{
// ...
public virtual IList<IRequirement> Requirements { get; set; }
}
尝试添加到该集合失败(正如我所期望的那样)。这就是为什么我感到困惑。
IList<IRequirement>
,为什么不在对象中添加?有人可以向我解释一下世界上发生了什么吗?
建议的方法是使用
SubclassMap<T>
,但问题是身份的数量和表的大小。如果多个对象(最多约8个)引用一个表中的标识,我会担心可伸缩性和性能。有人可以给我一些具体的见解吗?
答案 0 :(得分:3)
请参阅参考文档中的Inheritance mapping一章。在章节Limitations中,您可以看到哪种映射策略可行。
据我所知,你已经选择了“每个具体课程表”的策略之一。您可能需要<one-to-many>
与inverse=true
或<many-to-any>
进行映射。
如果要避免这种情况,则需要将IRequirement
作为基类映射到表中,然后可以为该表提供外键。这样做可以将其转换为“每个类层次表”或“每个子类表”映射。如果已经映射了另一个基类,这当然是不可能的。例如。 SomeKindOfObject
。
修改:<one-to-many>
与inverse=true
和<many-to-any>
的更多信息。
当您使用<one-to-many>
时,外键实际上位于指向Item的需求表中。到目前为止,这种方法效果很好,NH联合所有需求表来查找列表中的所有项目。反向是必需的,因为它强制您从需求引用项目,NH使用它来构建外键。
<many-to-any>
更灵活。它将列表存储在附加链接表中。该表有三列:
当NH读取此表时,它从类型信息(以及相应的需求映射)中了解需求所在的其他表。这就是任何类型的工作方式。
它实际上是一个多对多的关系不应该打扰你,它只意味着它将关系存储在一个额外的表中,该表在技术上能够将需求链接到多个项目。
编辑2 :怪异的结果:
您映射了3个表:IRequirement
,ObjectThatImplementsRequirement
,AnotherObjectThatHasRequirement
。他们都是完全独立的。你仍然在“具有隐式多态性的每个具体类的表”。您刚刚添加了包含IRequirements
的另一个表,当NH尝试查找正确的表时,这也可能导致一些歧义。
当然,你得到的结果为1,1。有独立的表,因此独立的ID都以1开头。
有效的部分:当你查询它时,NHibernate能够找到在整个数据库中实现接口的所有对象。尝试session.CreateQuery("from object")
,即可获得整个数据库。
不起作用的部分:另一方面,您无法仅通过ID和界面或object
获取对象。所以session.Get<object>(1)
不起作用,因为有许多id为1的对象。同样的问题在于列表。还有一些问题,例如,使用隐式多态,没有指定外键,从实现IRequirement
的每个类型指向Item。
任何类型:这是任何类型映射的来源。任何类型都与数据库中的其他类型信息一起存储,并且由存储外部的<many-to-any>
映射完成附加表中的密钥和类型信息。通过这种附加类型信息,NH能够找到存储记录的表。
怪异的结果:考虑到NH需要找到两种方式,从对象到单个表,从记录到单个类。因此,在独立映射接口和具体类时要小心。 NH可能会使用一个或另一个表,具体取决于您访问数据的方式。这可能是原因或您的怪异结果。
另一个解决方案:使用任何其他继承映射策略,您可以定义一个NH可以开始读取并查找类型的表。
ID范围:如果您使用Int32
作为ID,则可以在68年内每秒创建1条记录,直到用完ID为止。如果这还不够,只需切换到long
,你就会得到......可能更多,然后数据库能在未来几千年内存储......