通过Convention for Fluent-nHibernate进行复合主键声明

时间:2011-08-05 11:27:15

标签: nhibernate fluent-nhibernate azure

我需要对具有复合主键的表使用Fluent-nHibernate(Azure表,主键是(PartitionKey,RowKey),我想将它们映射到实体上的相应属性(或使用组件属性,如果更容易)

我的桌子看起来像是:

{
  PartitionKey PK,
  RowKey PK,
  [..]
}

和实体

public class MyRecord
{
  public virtual string PartitionKey{get;set;}
  public virtual string RowKey{get;set;}

  [...]
}

我目前的项目使用针对AzureTable的自定义nHibernate驱动程序。

我设法使其适用于ClassMap或XML映射。因此我确信司机正在工作。此外,使用类映射或XML声明,azure表HTTP请求是正确的。

但是我确实需要约定,所以这不是一个可接受的解决方案。

最后,总是可以选择仅将RowKey映射为PK,即使数据存储区使用(PartitionKey,RowKey)也是如此。它也可以工作,但它并不令人满意,因为它引入了nHibernate与底层数据存储区之间处理不匹配的单一性。

更新

我试图建立一个自定义的IIdentityConvention。 IIdentityInstance.Column()方法仅考虑第一次调用。 但是,如果我使用反射将两列添加到基础映射字段,则配置构建将失败,并带有XML验证异常(需要属性'class')

1 个答案:

答案 0 :(得分:1)

我今天得到了它,但它并不漂亮。它也没有使用惯例。据我了解惯例,它们真正意味着在主映射发生后调整事物。我认为添加映射被认为超出了约定的范围。

在我的项目中,我有一个基于自动化的通用初始化过程,它不知道任何类型,但具有依赖注入的映射覆盖复合键。不完全是您的情况,但这是一个类似的问题。

我通过反射工作的方式是获取适当的AutoPersistenceModel对象。如果您的代码如下所示:

Fluently.Configure().Mappings(m => ...

AutoPersistenceModel对象将是m.AutoMappings.First()

从这里开始,它是非常严肃的反射工作,最终在FluentNHibernate中调用受保护的方法。这是我正在使用的代码:

    private void Override(AutoPersistenceModel container, 
                          Type type, 
                          IEnumerable<KeyValuePair<string,string>> compositeKeys)
    {
        // We need to call container.Override<T>(Action<Automapping<T>> populateMap)
        // Through reflection...yikes
        var overrideMethod = typeof(AutoPersistenceModel)
                               .GetMethod("Override")
                               .MakeGenericMethod(type);
        var actionFactoryMethod = typeof(FluentNHibernateInitializer)
                                    .GetMethod("CompositeMapperFactory",
                                       BindingFlags.Instance | BindingFlags.NonPublic)
                                    .MakeGenericMethod(type);
        var actionMethod = actionFactoryMethod
                             .Invoke(this, new object[] { compositeKeys });
        overrideMethod.Invoke(container, new object[] {actionMethod});
    }

    private Action<AutoMapping<T>> CompositeMapperFactory<T>
           (IEnumerable<KeyValuePair<string, string>> compositeKeys)
    {
        return new Action<AutoMapping<T>>(m =>
            {
                var compositeId = m.CompositeId();
                foreach (var kvp in compositeKeys) 
                    compositeId = 
                      AddKeyProperty(
                        compositeId, 
                        typeof(T).GetProperty(kvp.Key), 
                        kvp.Value);
            }
        );
    }

    /// <summary>
    /// Uses reflection to invoke private and protected members!
    /// </summary>
    /// <param name="compositeId"></param>
    /// <param name="propertyInfo"></param>
    /// <returns></returns>
    private CompositeIdentityPart<T> AddKeyProperty<T>
      (CompositeIdentityPart<T> compositeId, 
       PropertyInfo propertyInfo, 
       string column)
    {
        var member = FluentNHibernate.MemberExtensions.ToMember(propertyInfo);
        var keyPropertyMethod = typeof(CompositeIdentityPart<T>)
                                  .GetMethod("KeyProperty", 
                                     BindingFlags.Instance | BindingFlags.NonPublic);
        return (CompositeIdentityPart<T>)
                 keyPropertyMethod
                   .Invoke(compositeId, new object[] { member, column, null });
    }