接受接口到集合(协方差)与nHibernate的麻烦 [英] Accept Interface into Collection (Covariance) troubles with nHibernate

查看:195
本文介绍了接受接口到集合(协方差)与nHibernate的麻烦的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在ASP.NET MVC应用程序中使用 Fluent nHibernate 作为我的持久层,我遇到了一些问题。



我有一种情况,我需要使用抽象来将对象存储到集合中,在这种情况下,如果您正在查看纯C#透视图,接口是最合理的选择。



基本上,一个对象( Item )可以有需求。一个要求可以是许多事情。在原生的C#情况下,我只需要用下面的代码实现这一点。

  interface IRequirement 
{
//用于赋值的方法和属性
}

类项目
{
virtual int Id {get;组; }
virtual IList< IRequirement>需求{get;组; }
}

一个粗略的例子。这在本机C#中工作良好 - 但是因为对象必须存储在数据库中,所以它变得有点复杂。实现 IRequirement 的每个对象可以是完全不同的种类对象。因为nHibernate(或者我发现的任何其他ORM)不能真正理解如何序列化接口,我不能想象,在我的生活中,如何处理这种情况。我的意思是,我明白问题。



这对数据库/ orm没有意义。我完全理解为什么。

  class SomeKindOfObject 
{
virtual int Id {get;组; }
// ...相对于这个基类型的一些其他方法
}
class OneRequirement:SomeKindOfObject,IRequirement
{
virtual string Name {get;组; }
//一些更多的方法和属性
}
类AnotherKindOfObject
{
virtual int Id {get;组; }
// ...更多的方法和属性,不同于SomeKindOfObject
}
类AnotherRequirement:AnotherKindOfObject,IRequirement
{
//更多的方法和属性相对到AnotherKindOfObject的意图层次结构
}

class OneRequirementMap:ClassMap< OneRequirement>
{
// etc
表(OneRequirement);
}
class AnotherRequirementMap:ClassMap< AnotherRequirement>
{
//
表(OtherRequirements);
}
class ItemMap:ClassMap< Item>
{
// ...现在我们有一个问题。
Map(x => x.Requirements)//不计算...
//其他映射
}

那么,有没有任何想法?我似乎不能使用泛型,所以使一个基本的需求< T> 类似乎出来。我的意思是代码工作和运行,但是ORM无法抓住它。我知道我在这里要求的可能是不可能的,但我所能做的是问。



我也想补充一点,我没有太多的经验与< c $ c> nHibernate ,只有流利的nHibernate ,但我已经知道这两个社区都很好,所以我标记这两个中。

我实际发现了 http://stackoverflow.com/questions/845536/programming-to-interfaces-while-mapping-with-fluent-nhibernate\">在使用Fluent NHibernate进行映射时对接口进行编程,有关这一点,但我仍然不确定它是否适用于我的情况。欢迎任何帮助。



UPDATE(02/02/2011)





根据建议,做更多的研究,我设计了一个基本的界面。

 接口IRequirement 
{
// ...总是相同
}

现在我建立了类映射..

  class IRequirementMap:ClassMap< IRequirement> 
{
public IRequirementMap()
{
Id(x => x.Id);
UseUnionSubclassForInheritanceMapping();
表(要求);
}
}

然后我映射一些实现它的东西。

  class ObjectThatImplementsRequirementMap:ClassMap< ObjectThatImplementsRequirement> 
{
ObjectThatImplementsRequirementMap()
{
Id(x => x.Id); //是的,我是基类映射它。
//其他属性
表(ObjectImplementingRequirement);
}
}

class AnotherObjectThatHasRequirementMap:ClassMap< AnotherObjectThatHasRequirement>
{
AnotherObjectThatHasRequirementMap()
{
Id(x => x.Id); //是的,我是基类映射它。
//其他属性
表(AnotheObjectImplementingRequirement);
}
}

这不是人们建议,但这是我的第一个方法。虽然我这样做,因为我得到一些非常怪异的结果。



它实际上工作... / strong>
运行以下代码会产生意想不到的结果。

  //设置ISession 
// setup事务
var requirements = new< IRequirement>
{
new ObjectThatImplementsRequirement
{
// property,etc ..
},
new AnotherObjectThatHasRequirement
{
//其他性能。
}
}
//添加到会话。
//提交事务。
//关闭写块。

//设置新会话
//设置新事务
var requireables = session.Query< IRequirable>();
foreach(var requireable in requireables)
Console.WriteLine(requireable.Id);

现在事情变得怪异。我得到结果...



1
1



这对我来说没有意义。它不应该工作。我甚至可以查询每个对象的各个属性,并且它们保留了它们的类型。即使我运行插入,关闭应用程序,然后运行检索(以避免缓存的可能性),它们仍然具有正确的类型。

  class SomethingThatHasRequireables 
{
/ / ...
public virtual IList< IRequirement>需求{get;组; }
}

尝试添加到该集合失败(如我所料)。这就是为什么我很困惑。




  • 如果我可以在我的会话中添加到通用 IList< IRequirement> 为什么不在对象中?

  • nHibernate如何理解两个实体之间的区别,即同一个Id,
    如果它们都映射为同一种对象,在一个场景,而不是其他?



有人可以向我解释世界上正在发生什么吗?


建议的方法是使用 SubclassMap< T> ,但是问题是身份数,和表的大小。如果多个对象(最多约8个)引用来自一个表的标识,我关心可伸缩性和性能。有人能给我一些具体的洞察力吗?



解决方案

请参阅继承映射。在限制一章中,您可以了解哪些映射策略有可能。



你已经选择了一个每个具体类的表策略,就我所见。您可能需要< one-to-many> inverse = true



如果你想避免这种情况,你需要映射 IRequirement 作为一个基类放入一个表中,那么可以拥有该表的外键。这样做,你把它变成一个每个类层次结构或每个子类表映射。这当然是不可能的,如果另一个基类已经映射。例如。 SomeKindOfObject






关于<一对多> 与 inverse = true < ; many-to-any>



当您使用< one-to-many> ,外键实际上在需求表中指向该项。这到目前为止工作得很好,NH联合所有需求表以找到列表中的所有项目。反向是必需的,因为它强制您从需求到项目的引用,由NH用来构建外键。



< many-to-any> 更加灵活。它将列表存储在其他链接表中。此表格有三列:




  • 项目的外键,

  • 实际需求类型(.NET类型或实体名称)

  • 和需求的主键(不能是外键,因为它可以指向不同的表)。



当NH读取此表时,它根据类型信息(和相应的需求映射)知道其他表中的需求。这是任何类型的工作原理。



它实际上是一个多对多的关系不应该打扰你,它只意味着它存储的关系在






编辑2 :freaky results:



您映射了3个表: IRequirement ObjectThatImplementsRequirement AnotherObjectThatHasRequirement 。他们都是完全独立的。你仍然在具有隐式多态性的具体类的表。您刚刚添加了包含 IRequirements 的另一个表格 ,当NH尝试找到正确的表格时,这可能会导致一些歧义。



当然,你得到1,1的结果。有独立的表,因此独立的id都以1开始。



部分工作: NHibernate能够找到所有实现的对象一个接口在整个数据库中查询它时。尝试 session.CreateQuery(from object)并获得整个数据库。



不起作用:另一方面,你不能通过id和interface或 object 获取对象。所以 session.Get< object>(1)不工作,因为有很多id为1的对象。同样的问题是列表。并且还有一些其他问题,例如,使用隐式多态性,没有外键指定从每个类型实现 IRequirement 到项目。



任何类型:这是任何类型映射的来源。任何类型都与数据库中的附加类型信息一起存储, code>< many-to-any> 映射,将外键和类型信息存储在附加表中。使用这种附加类型信息,NH能够找到存储记录的表。



奇怪的结果:考虑NH需要找到两种方式,从对象到单个表,从记录到单个类。因此,在独立映射接口和具体类时要小心。可能发生NH使用一个或另一个表,取决于您访问数据的方式。



其他解决方案:使用任何其他继承映射策略,您可以定义单个表,其中NH可以开始读取和查找类型。



Id范围:如果您将 Int32 用作ID,每秒1条记录,持续68年,直到你用完了ID。如果这还不够,只需切换到 long ,你会得到...可能更多的数据库能够存储在未来几千年...


I am using Fluent nHibernate for my persistence layer in an ASP.NET MVC application, and I have come across a bit of a quandry.

I have a situation where I need to use an abstraction to store objects into a collection, in this situation, an interface is the most logical choice if you are looking at a pure C# perspective.

Basically, an object (Item) can have Requirements. A requirement can be many things. In a native C# situation, I would merely accomplish this with the following code.

interface IRequirement
{
 // methods and properties neccessary for evaluation
}

class Item
{
 virtual int Id { get; set; }
 virtual IList<IRequirement> Requirements { get; set; }
}

A crude example. This works fine in native C# - however because the objects have to be stored in a database, it becomes a bit more complicated than that. Each object that implements IRequirement could be a completely different kind of object. Since nHibernate (or any other ORM that I have discovered) cannot really understand how to serialize an interface, I cannot think of, for the life of me, how to approach this scenario. I mean, I understand the problem.

This makes no sense to the database/orm. I understand completely why, too.

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
}

So, does anyone have any ideas? I cannot seem to use generics, either, so making a basic Requirement<T> type seems out. I mean the code works and runs, but the ORM cannot grasp it. I realize what I am asking here is probably impossible, but all I can do is ask.

I would also like to add, I do not have much experience with nHibernate, only Fluent nHibernate, but I have been made aware that both communities are very good and so I am tagging this as both. But my mapping at present is 100% 'fluent'.

Edit

I actually discovered Programming to interfaces while mapping with Fluent NHibernate that touches on this a bit, but I'm still not sure it is applicable to my scenario. Any help is appreciated.

UPDATE (02/02/2011)

I'm adding this update in response to some of the answers posted, as my results are ... a little awkward.

Taking the advice, and doing more research, I've designed a basic interface.

interface IRequirement
{
 // ... Same as it always was
}

and now I establish my class mapping..

class IRequirementMap : ClassMap<IRequirement>
{
 public IRequirementMap()
 {
    Id( x => x.Id );
    UseUnionSubclassForInheritanceMapping();
    Table("Requirements");
 }
}

And then I map something that implements it. This is where it gets very freaky.

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");
     }
}

This is not what people have suggested, but it was my first approach. Though I did it because I got some very freaky results. Results that really make no sense to me.

It Actually Works... Sort Of Running the following code yields unanticipated results.

// 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 );

Now things get freaky. I get the results...

1 1

This makes no sense to me. It shouldn't work. I can even query the individual properties of each object, and they have retained their type. Even if I run the insertion, close the application, then run the retrieval (so as to avoid the possibility of caching), they still have the right types. But the following does not work.

class SomethingThatHasRequireables
{ 
 // ...
 public virtual IList<IRequirement> Requirements { get; set; }
}

Trying to add to that collection fails (as I expect it to). Here is why I am confused.

  • If I can add to the generic IList<IRequirement> in my session, why not in an object?
  • How is nHibernate understanding the difference between two entities with the same Id, if they are both mapped as the same kind of object, in one scenario, and not the other?

Can someone explain to me what in the world is going on here?

The suggested approach is to use SubclassMap<T>, however the problem with that is the number of identities, and the size of the table. I am concerned about scalability and performance if multiple objects (up to about 8) are referencing identities from one table. Can someone give me some insight on this one specifically?

解决方案

Take a look at the chapter Inheritance mapping in the reference documentation. In the chapter Limitations you can see what's possible with which mapping strategy.

You've chose one of the "table per concrete class" strategies, as far as I can see. You may need <one-to-many> with inverse=true or <many-to-any> to map it.

If you want to avoid this, you need to map IRequirement as a base class into a table, then it is possible to have foreign keys to that table. Doing so you turn it into a "table per class-hierarchy" or "table per subclass" mapping. This is of course not possible if another base class is already mapped. E.g. SomeKindOfObject.


Edit: some more information about <one-to-many> with inverse=true and <many-to-any>.

When you use <one-to-many>, the foreign key is actually in the requirement tables pointing back to the Item. This works well so far, NH unions all the requirement tables to find all the items in the list. Inverse is required because it forces you to have a reference from the requirement to the Item, which is used by NH to build the foreign key.

<many-to-any> is even more flexible. It stores the list in an additional link table. This table has three columns:

  • the foreign key to the Item,
  • the name of the actual requirement type (.NET type or entity name)
  • and the primary key of the requirement (which can't be a foreign key, because it could point to different tables).

When NH reads this table, it knows from the type information (and the corresponding requirement mapping) in which other tables the requirements are. This is how any-types work.

That it is actually a many-to-many relation shouldn't bother you, it only means that it stores the relation in an additional table which is technically able to link a requirement to more then one item.


Edit 2: freaky results:

You mapped 3 tables: IRequirement, ObjectThatImplementsRequirement, AnotherObjectThatHasRequirement. They are all completely independent. You are still on "table per concrete class with implicit polymorphism". You just added another table with containing IRequirements, which may also result in some ambiguity when NH tries to find the correct table.

Of course you get 1, 1 as result. The have independent tables and therefore independent ids which both start with 1.

The part that works: NHibernate is able to find all the objects implementing an interface in the whole database when you query for it. Try session.CreateQuery("from object") and you get the whole database.

The part that doesn't work: On the other side, you can't get an object just by id and interface or object. So session.Get<object>(1) doesn't work, because there are many objects with id 1. The same problem is with the list. And there are some more problems there, for instance the fact that with implicit polymorphism, there is no foreign key specified which points from every type implementing IRequirement to the Item.

The any types: This is where the any type mapping comes in. Any types are stored with additional type information in the database and that's done by the <many-to-any> mapping which stores the foreign key and type information in an additional table. With this additional type information NH is able to find the table where the record is stored in.

The freaky results: Consider that NH needs to find both ways, from the object to a single table and from the record to a single class. So be careful when mapping both the interface and the concrete classes independently. It could happen that NH uses one or the other table depending on which way you access the data. This may have been the cause or your freaky results.

The other solution: Using any of the other inheritance mapping strategies, you define a single table where NH can start reading and finding the type.

The Id Scope: If you are using Int32 as id, you can create 1 record each second for 68 years until you run out of ids. If this is not enough, just switch to long, you'll get ... probably more then the database is able to store in the next few thousand years...

这篇关于接受接口到集合(协方差)与nHibernate的麻烦的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆