开发者

Composite primary key declaration through Convention for fluent-nHibernate

I need to use Fluent-nHibernate a开发者_运维百科gainst 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)


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 });
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜