流畅的NHibernate返回具有多对多映射的重复项

Joe*_*oeG 2 c# nhibernate fluent-nhibernate

我正在使用NHibernate与Fluent映射,并且当我加入多对多关系时遇到重复条目的问题.我下面的简单示例有两个类,PurchaseOrder和Product.PurchaseOrder可以有许多产品,而产品可以是许多PurchaseOrders的一部分.

当我尝试检索PurchaseOrder及其产品时,我会为每个产品重复相同的PurchaseOrder.(因此,如果PurchaseOrder有5个产品,我将在结果中看到相同的PurchaseOrder 5次.每个产品都包含所有5个产品.)

这是我的设置:

PurchaseOrder
    OrderID  OrderDate
    1        2013-01-01
    2        2013-01-02

Product
    ProductID   Name
    1           Widget
    2           Thing

OrderProducts
    OrderID ProductID
    1       1
    1       2
    2       1
    2       2
Run Code Online (Sandbox Code Playgroud)

public class PurchaseOrder
{
    public virtual int OrderID { get; set; }
    public virtual DateTime? OrderDate { get; set; }
    public virtual IList<Product> Products { get; set; }
}

public class Product
{
    public virtual int ProductID { get; set; }
    public virtual string Name { get; set;  }
    public virtual IList<PurchaseOrder> Orders { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

映射

public class PurchaseOrderMapping : ClassMap<PurchaseOrder>
{
    public PurchaseOrderMapping()
    {
        Id(x => x.OrderID, "OrderID");
        Map(x => x.OrderDate, "OrderDate");

        HasManyToMany(x => x.Products)
            .Table("OrderProducts")
            .Schema("dbo")
            .ParentKeyColumn("OrderID")
            .ChildKeyColumn("ProductID");

        Schema("dbo");
        Table("PurchaseOrder");
    }
}

public class ProductMapping : ClassMap<Product>
{
    public ProductMapping ()
    {
        Id(x => x.ProductID, "ProductID");
        Map(x => x.Name, "Name");

        HasManyToMany(x => x.Orders)
            .Table("OrderProducts")
            .Schema("dbo")
            .ParentKeyColumn("OrderID")
            .ChildKeyColumn("ProductID")
            .Inverse();

        Schema("dbo");
        Table("Product");
    }
}
Run Code Online (Sandbox Code Playgroud)

QueryOver

var orderList = session.QueryOver<PurchaseOrder>()
               .JoinQueryOver<Product>(o => o.Products)
               .List();
Run Code Online (Sandbox Code Playgroud)

我希望orderList有2个PurchaseOrders,但实际上有4个.重复对应于OrderID = 1的对象,OrderID = 2

foreach(var o in orderList) { Console.WriteLine(o.OrderID); }

Output:
1
1
2
2
Run Code Online (Sandbox Code Playgroud)

更进一步,如果我比较具有相同ID的对象,它们是同一个对象.

System.Console.WriteLine(Object.ReferenceEquals(orderList[0], orderList[1]));
System.Console.WriteLine(Object.ReferenceEquals(orderList[2], orderList[3]));

Output:
True
True
Run Code Online (Sandbox Code Playgroud)

为什么NHibernate会复制结果中的对象?如何排除它们并获得我的2个订单列表,每个订单包含相应的2个产品?

Mic*_*haC 5

由于某种原因session.QueryOver<T>,不会立即返回明显的结果,您必须通过结果转换器或Linq显式定义它.Distinct()

var orderList = session.QueryOver<PurchaseOrder>()
    .Fetch(p => p.Products).Eager
    .List()
    .Distinct();
Run Code Online (Sandbox Code Playgroud)

要么

var orderListFetch = session.QueryOver<PurchaseOrder>()
    .Fetch(p => p.Products).Eager
    .TransformUsing(Transformers.DistinctRootEntity)
    .List();
Run Code Online (Sandbox Code Playgroud)

或者你也可以使用Nhibernate.Linq:session.Query<T>接口,这个接口确实会默认返回一个不同的结果:

var linqQuery = session.Query<PurchaseOrder>()
    .Fetch(p => p.Products).ToList();
Run Code Online (Sandbox Code Playgroud)

所有3个查询都会生成几乎完全相同的SQL语句,这些语句都将返回4行,因为它使用左外连接 ...

结果将始终转换为内存中的不同结果集!

测试设置

我稍微更改了你的代码,切换了父键和子键.对于插入/更新/删除子记录,您可能还希望保留级联

public class PurchaseOrder
{
    public virtual int OrderID { get; set; }
    public virtual DateTime? OrderDate { get; set; }
    public virtual IList<Product> Products { get; set; }
}

public class Product
{
    public virtual int ProductID { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<PurchaseOrder> Orders { get; set; }
}

public class PurchaseOrderMapping : ClassMap<PurchaseOrder>
{
    public PurchaseOrderMapping()
    {
        Id(x => x.OrderID, "OrderID");
        Map(x => x.OrderDate, "OrderDate");

        HasManyToMany(x => x.Products)
            .Table("OrderProducts")
            .Schema("dbo")
            .ParentKeyColumn("ProductID")
            .ChildKeyColumn("OrderID")
            .Cascade.All();

        Schema("dbo");
        Table("PurchaseOrder");
    }
}

public class ProductMapping : ClassMap<Product>
{
    public ProductMapping()
    {
        Id(x => x.ProductID, "ProductID");
        Map(x => x.Name, "Name");

        HasManyToMany(x => x.Orders)
            .Table("OrderProducts")
            .Schema("dbo")
            .ParentKeyColumn("OrderID")
            .ChildKeyColumn("ProductID")
            .Inverse();

        Schema("dbo");
        Table("Product");
    }
}
Run Code Online (Sandbox Code Playgroud)


Jam*_*Ide 5

正如其他人指出的那样,您在 ProductMapping 中的父键和子键是错误的。由于连接,您的查询返回多个结果。您需要使用转换器来仅返回不同的根实体:

var orderList = session.QueryOver<PurchaseOrder>()
           .JoinQueryOver<Product>(o => o.Products)
           .TransformUsing(Transformers.DistinctRootEntity)
           .List();
Run Code Online (Sandbox Code Playgroud)

请注意,如果您只想预先获取产品集合,则可以使用 Fetch 指定:

var orderList = session.QueryOver<PurchaseOrder>()
           .Fetch(o => o.Orders).Eager
           .TransformUsing(Transformers.DistinctRootEntity)
           .List();
Run Code Online (Sandbox Code Playgroud)