是否可以在不获取所有链接的情况下获取链接表?

时间:2016-10-21 14:53:23

标签: c# nhibernate

好的,首先我想说我在我的项目中使用NHibernate,在这个项目中,我们有(除其他外)一个同步功能(从中央MSSQL数据库同步到本地SQLite的)。现在我知道NHibernate并不是为了同步数据库,但我还是愿意这样做。

我有一个中等大小的数据库模型,所以我不能在这里添加它,但问题是我有两个数据表,还有一个链接表来链接它们。

数据库模型:

| Product            | | ProductLinkProducer | | Producer            |
|--------------------| |---------------------| |---------------------|
| Id                 | | LinkId              | | Id                  |
| Name               | | Product             | | Name                |
| ProductLinkProducer| | Producer            | | ProductLinkProducer |

数据库:

| Product | | ProductLinkProducer | | Producer |
|---------| |---------------------| |----------|
| Id      | | LinkId              | | Id       |
| Name    | | ProductId           | | Name     |
|         | | ProducerId          | |          |

因此,在同步过程中,我首先从Product表中复制所有数据,然后从Producer表复制(基本上是var products = session.Query<Products>().ToList())。这是由NHibernate在一个语句中完成的:

select
    product0_.id as id2_,
    product0_.name as name2_
from
    Product product0_

现在我必须逐出第一个会话中的所有项目(products.ForEach(x => session.Evict(x));

然后保存(products.ForEach(x => syncSession.save(x));)每行一次插入(如预期的那样)。

因此,当在链接表中保存数据时,我希望只有一个选择。然而事实并非如此。因为首先它如上所述select ...。但是现在在 insert 的每一行之前,它为产品和生产者做了更多的选择

所以它看起来像:

产品:

  • 选择
  • insert(id 1)
  • insert(id 2)

制片:

  • 选择
  • insert(id 101)
  • insert(id 102)

ProdLinkProducer:

  • 选择
  • 从产品
  • 中选择ID 1
  • 从产品
  • 中选择ID 1
  • 从制作人
  • 中选择ID 101
  • 从商品
  • 中选择ID 2
  • 从商品
  • 中选择ID 2
  • 从制作人
  • 中选择ID 102
  • 从制作人
  • 中选择ID 102
  • 插入
  • 插入

那么无论如何都要避免这种行为吗?

修改

为了更好地解释我所做的事情,我创建了一个小型测试项目。它可以在这里找到:https://github.com/tb2johm/NHibernateSync (我本来希望只添加一个ghist,但我认为它可能遗漏了很多数据,对不起......)

EDIT2

我找到了一种让它起作用的方法,但我不喜欢它。 此解决方案的工作方式是在数据库模型中创建一个ProductLinkProducerSync表,它不包含任何链接,只包含值,并避免同步普通链接表,而只是同步&#34;同步&#34;表。但正如我所说,我不喜欢这个想法,因为如果我改变了数据库中的任何内容,我在两个地方都需要更新相同的数据。

1 个答案:

答案 0 :(得分:1)

我无法找到NHibernate开箱即用的方式来做你想问的事。

然而,通过手动将FK引用(代理类)重新绑定到新会话,我能够获得所需的行为(我猜想有什么比没有好:):

var links = session.Query<ProductLinkProducer>().ToList(); 
links.ForEach(x => session.Evict(x));
foreach (var link in links)
{
    link.Product = syncSession.Get<Product>(link.Product.Id);
    link.Producer = syncSession.Get<Producer>(link.Producer.Id);
    syncSession.Save(link);
}
syncSession.Flush();

这是使用NHibernate元数据服务的通用版本:

static IEnumerable<Action<ISession, T>> GetRefBindActions<T>(ISessionFactory sessionFactory)
{
    var classMeta = sessionFactory.GetClassMetadata(typeof(T));
    var propertyNames = classMeta.PropertyNames;
    var propertyTypes = classMeta.PropertyTypes;
    for (int i = 0; i < propertyTypes.Length; i++)
    {
        var propertyType = propertyTypes[i];
        if (propertyType.IsAssociationType && !propertyType.IsCollectionType)
        {
            var propertyName = propertyNames[i];
            var propertyClass = propertyType.ReturnedClass;
            var propertyClassMeta = sessionFactory.GetClassMetadata(propertyClass);
            yield return (session, target) =>
            {
                var oldValue = classMeta.GetPropertyValue(target, propertyName, EntityMode.Poco);
                var id = propertyClassMeta.GetIdentifier(oldValue, EntityMode.Poco);
                var newValue = session.Get(propertyClass, id);
                classMeta.SetPropertyValue(target, propertyName, newValue, EntityMode.Poco);
            };
        }
    }
}

并将其应用于您的Sync方法:

private static void Sync<T>(string tableName, ISession session, ISession syncSession)
{
    Console.WriteLine("Fetching data for ####{0}####...", tableName);
    var sqlLinks = session.Query<T>();
    var links = sqlLinks.ToList();
    Console.WriteLine("...Done");

    Console.WriteLine("Evicting data...");
    links.ForEach(x => session.Evict(x));
    Console.WriteLine("...Done");

    Console.WriteLine("Saving data...");
    var bindRefs = GetRefBindActions<T>(syncSession.SessionFactory).ToList();
    foreach (var link in links)
    {
        foreach (var action in bindRefs) action(syncSession, link);
        syncSession.Save(link);
    }
    Console.WriteLine("...Flushing data...");
    syncSession.Flush();
    Console.WriteLine("...Done");
    Console.WriteLine("\n\n\n");
}