如何避免Fluent NHibernate中的子更新多对多关系?

时间:2013-08-27 17:30:05

标签: c# nhibernate fluent-nhibernate

我使用Fluent NHibernate有一个简单的多对多关系,它工作得非常好。

这是我的第一个类映射:

    public LobMapping()
    {
        ...
        HasManyToMany(x => x.Commodities)
            .Table("PPBSYS.LOB_COMMODITY")
            .ParentKeyColumn("LOB_ID")
            .ChildKeyColumn("COMMODITY_ID")
            .Cascade.SaveUpdate()
            .LazyLoad();
        ...
    }

这是我的第二个类映射:

    public CommodityMapping()
    {
        ...
        HasManyToMany(x => x.Lobs)
            .Table("PPBSYS.LOB_COMMODITY")
            .ParentKeyColumn("COMMODITY_ID")
            .ChildKeyColumn("LOB_ID")
            .Inverse()
            .Cascade.All()
            .LazyLoad();
        ...
    }

最后,我的Lob对象有一个商品清单:

public class Lob
{
    ...
    public virtual IEnumerable<Commodity> Commodities { get; set; }
    ...
}

但是我不满意我必须在Lob类中引用整个商品。我真的很想做:

var lob = new Lob();
lob.Commodities = new [] { new Commodity { CommodityId = 1 }}; 
repository.SaveLob(lob);

但是如果我运行上面的代码,NHibernate将尝试更新商品表,将ID设置为1的商品的列为空。

所以实际上我必须在保存Lob之前得到整个对象:

var lob = new Lob();
lob.Commodities = new [] { repostitory.GetCommodit(1) };
repository.SaveLob(lob);

知道商品存在,因为用户只是选择了它们。

有可能完成我的任务吗?

2 个答案:

答案 0 :(得分:3)

我假设您的存储库正在调用session.Get<T>(id)。还有session.Load<T>(id)

lob.Commodities = new [] { session.Load<Commodity>(1) };

来自NHibernate documentation on Load() ...

  

如果使用代理映射类,则Load()将返回一个未初始化的代理对象,并且在调用对象的方法之前不会实际访问数据库。如果您希望创建与对象的关联而不实际从数据库加载它,则此行为非常有用。

答案 1 :(得分:0)

丹尼尔的回答听起来很有希望,请告诉我如果在保存代理中不会命中数据库来填充实体的所有属性。

另一个答案是对Nhibernate有点强有力,我测试了它并且它正在工作, 工作代码:

public class Com
{
    public virtual Guid ID { get; set; }
    public virtual string Name { get; set; }

    public virtual IList<Lob> Lobs { get; set; }
}

public class Lob
{
    public virtual Guid ID { get; set; }
    public virtual string Name { get; set; }

    public virtual IList<Com> Coms { get; set; }
}

class LobMap : ClassMap<Lob>
{
    public LobMap()
    {
        Id(x => x.ID).GeneratedBy.GuidComb();

        Map(x => x.Name);

        HasManyToMany(x => x.Coms)
        .Table("LOB_COM")
        .ParentKeyColumn("LOB_ID")
        .ChildKeyColumn("COM_ID")
        .Cascade.SaveUpdate()
        .LazyLoad();
    }
}

class ComMap : ClassMap<Com>
{
    public ComMap()
    {
        Id(x => x.ID).GeneratedBy.GuidComb();

        Map(x => x.Name);

        HasManyToMany(x => x.Lobs)
        .Table("LOB_COM")
        .ParentKeyColumn("COM_ID")
        .ChildKeyColumn("LOB_ID")
        .Inverse()
        .Cascade.All()
        .LazyLoad();
    }
}

现在 - 我设置了一个虚拟实体来模仿连接表并映射它:

public class ComLobConnection
{
    public virtual Guid ComID { get; set; }
    public virtual Guid LobID { get; set; }
}

public class ComLobConnectionMap : ClassMap<ComLobConnection>
{
    public ComLobConnectionMap()
    {
        Table("LOB_COM");

        Id();

        Map(x => x.ComID,"COM_ID");
        Map(x => x.LobID,"LOB_ID");
    }
}

请注意,我正在映射到与ManyToMany完全相同的字段和表,没有设置ID(这是空的Id()调用)

现在剩下的就是保存一个ComLobConnection,它将被添加到Com.Lobs和Lob.Coms

保存了一些测试Com,Lob

        var testCom = new Com() { Name = "TestCom" };
        var testLob = new Lob() { Name = "TestLob" };
        session.SaveOrUpdate(testCom);
        session.SaveOrUpdate(testLob);

保存后 - 获取其ID并保存连接以进行测试

        var testConnection = new ComLobConnection()
        {
            ComID = Guid.Parse("D3559F53-8871-45E9-901D-A22800806567"),
            LobID = Guid.Parse("D372D430-2E61-44F2-BA89-A228008065F1")
        };
        session.SaveOrUpdate(testConnection);

这在很多人的桌子上取得了新纪录,并且在获得Lob和Com之后工作。

我不推荐这个:),它只是让我感兴趣,如果它可以做到,它可以。 这样,您就不会点击数据库来加载您知道存在的Coms或Lobs来保存它们的连接。

希望Load()也不会因此而命中DB:)

编辑:可以使用任何类型的ID

完成