Конвенции за автоматично съпоставяне, когато Свойството и резервното поле нямат НИЩО общо?

Използвайки Fluent NHibernate, не мога да измисля необходимите конвенции за автоматично картографиране за следния (привидно прост и често срещан) случай на употреба:

public class MyClass
{
    private int _specialIdentityField
    private string _firstname;
    public Id { get { return _specialIdentityField; }; }
    public virtual string Firstname
    {
        get
        {
            return _firstname;
        }
        set
        {
            _firstname = value;
        }
    }
}

public class OtherClass
{
    private int _specialIdentityField
    private string _lastname;
    public Id { get { return _specialIdentityField; }; }
    public virtual string Lastname
    {
        get
        {
            return _lastname;
        }
        set
        {
            _lastname = value;
        }
    }
}

Желаните съпоставяния са така:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="field.camelcase-underscore" auto-import="true" default-cascade="none" default-lazy="true">
    <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="MyClass" table="`MyClass`">
        <id name="_specialIdentityField" type="System.Int32" access=field>
          <column name="Id" />
          <generator class="identity" />
        </id>
        <property name="Firstname" type="System.String">
          <column name="Firstname" />
        </property>
    </class>
    <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="OtherClass" table="`OtherClass`">
        <id name="_specialIdentityField" type="System.Int32" access=field>
          <column name="Id" />
          <generator class="identity" />
        </id>
        <property name="Lastname" type="System.String">
          <column name="Lastname" />
        </property>
    </class>
</hibernate-mapping>

По принцип правилата са:

  • всичко е field-camelcase-underscore като тип достъп ОСВЕН самоличност
  • identity е поле с фиксирано име във всеки клас (name=_someSpecialIdentityField)
  • достъпът до самоличност е винаги само до полето и няма никаква връзка с името на свойството RO, което го заобикаля

Частта от това, която напълно ме спъва, е конвенционалното картографиране на елементите, свързани с идентичността (очевидно конвенционалното картографиране на свойствата е напълно стандартна цена). Проблемът, който имам, е как да кажа на конвенциите на FNH, че моето поле за самоличност е фиксирано име. Всички замени на конвенцията, които мога да намеря, изглежда предполагат, че винаги ще има някаква връзка между свойството, което представлява самоличността, и името на основното поле за поддръжка (напр. мога да задам „персонализиран префикс“ за опорното поле, но не виждам как мога просто да кажа „това винаги е името на опорното поле“).

За мен е очевидно как да постигна това с изрично картографиране (и по този въпрос с XML файлове за картографиране), но изобщо не ми е очевидно как да постигна това с базирано на конвенция (автоматично картографиране) картографиране във FNH.

Това не може да е нетипичен случай на употреба, така че трябва просто да пренебрегна нещо ужасно очевидно. Оценяват се мисли от всички гурута на FNH!


person sbohlen    schedule 06.02.2010    source източник


Отговори (1)


РЕДАКТИРАНЕ: Разгледайте интерфейса на IAutomappingConfiguration. Създайте своя собствена реализация или заменете класа DefaultAutomappingConfiguration.

public virtual bool IsId(Member member)
    {
        return member.Name.Equals("id", StringComparison.InvariantCultureIgnoreCase);
    }

след това го добавяте към инициализацията:

Fluently.Configure( Configuration )
                .Mappings( cfg =>
                { cfg.AutoMappings.Add( IAutomappingConfigurationInstance )}

===============================================

Здравей Стив, мисля, че знам какво се опитваш да направиш. Използвам Proteus и FNH automapping. За да направя трика с идентификатора, създадох обвивка около Proteus, която прави две неща:

1) Картографира ID

2) Скрива сетера за id

public abstract class EntityObject<TEntity> : IdentityPersistenceBase<TEntity, Guid>, IEntity
    where TEntity : class, IEntity
{
    public virtual Guid Id
    {
        get { return _persistenceId; }
    }

    Guid IIdentifiedEntity<Guid>.Id
    {
        get { return _persistenceId; }
        set { _persistenceId = value; }
    }

    public virtual int Version
    {
        get { return _persistenceVersion; }
        set { _persistenceVersion = value; }
    }
}

И за да избегнете запазването на свойството IsTransient + други неща, можете да създадете MappingAlternation:

public class EntityAlteration : IAutoMappingAlteration
{

    public void Alter( AutoPersistenceModel model )
    {
        model.OverrideAll( map =>
        {

            Type recordType = map.GetType().GetGenericArguments().Single();
            if( recordType.BaseType.Name == "EntityObject`1" )
            {
                Type changeType = typeof( Change<> ).MakeGenericType( recordType );
                var change = ( IChange )Activator.CreateInstance( changeType );
                change.Go( map );
            }
        } );
    }

}

interface IChange
{
    void Go( object mapObject );
}

class Change<TRecord> : IChange where TRecord : EntityObject<TRecord>
{
    void IChange.Go( object mapObject )
    {
        var map = ( AutoMapping<TRecord> )mapObject;
        map.Id( x => x.Id ).GeneratedBy.Guid().Access.Property();
        map.IgnoreProperty( x => x.IsTransient );
    }
}

PS: Много ми липсват моментите, когато бяхте активни в онлайн пространството. Беше вълнуващо лято и есен преди 2 години...

person mynkow    schedule 13.07.2010
comment
уау -- перфектно, ако не и малко прекалено сложно! Относно моето напускане на онлайн пространството, за съжаление местната общност пое голяма част от моя фокус/внимание, но скоро ще видите завръщането ми към активен онлайн живот; останете на линия! - person sbohlen; 14.07.2010