通过 Fluent-nHibernate 约定的复合主键声明

发布于 2024-11-28 05:32:52 字数 768 浏览 1 评论 0原文

我需要对具有复合主键的表使用 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 驱动程序,

因此我确信该驱动程序正在工作。使用类映射或 XML 声明是正确的。

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

最后,即使数据存储区使用 (​​PartitionKey,RowKey),也始终可以选择仅将 RowKey 映射为 PK。然而,它并不太令人满意,因为它引入了 nHibernate 和底层数据存储之间的唯一性处理不匹配。

更新:

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

I need to use Fluent-nHibernate against a table with a composite primary key (Azure Table, primary keys being (PartitionKey,RowKey) and I would like to map them with corresponding properties on the entity (or with a component property, if easier)

my table would look like:

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

and the entity

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

  [...]
}

My current projet uses a custom nHibernate Driver targeting AzureTable.

I managed to make it work with ClassMap or XML mappings. Therefore I am sure that the driver is working. Furthermore, the azure table HTTP requests are correct using classmaps or XML declarations.

However I really need conventions, so this isn't an acceptable solution.

Finally, there is always the option to map only RowKey as a PK, even if the Datastore use (PartitionKey,RowKey). It works too, However it's not really satisfying as it introduces an unicity handling mismatch between nHibernate and the underlying datastore.

UPDATE:

I tried to build a custom IIdentityConvention. The IIdentityInstance.Column() method takes into account only the first call.
However, if I use reflection to add both columns to the underlying mapping field, the configuration build fails with an XML validation exception (attribute 'class' required)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

静水深流 2024-12-05 05:32:52

我今天成功了,但是不太好。它也不使用约定。据我了解,约定实际上是为了在主映射发生后进行调整。我认为添加映射被认为超出了约定的范围。

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

我通过反射实现此目的的方法是获取适当的 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 });
    }

I got it working today, but it's not pretty. It also doesn't use a convention. As I understand conventions, they're really meant for tweaking things after the main mapping has occurred. Adding mappings I believe is considered out of scope for conventions.

In my project I have a generic automapping-based initialization procedure that knows nothing of types, but has dependency-injected mapping overrides for composite keys. Not exactly your scenario, but it's a similar problem.

The way I got this to work through reflection was to get hold of the appropriate AutoPersistenceModel object. If you have code looking like this:

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

The AutoPersistenceModel object would be m.AutoMappings.First()

From here, it's pretty serious reflection work, culminating in a call to a protected method inside FluentNHibernate. Here's the code I'm using:

    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 });
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文