当属性和支持字段没有任何共同点时的自动映射约定?

发布于 2024-08-20 16:05:07 字数 2390 浏览 2 评论 0原文

使用 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 作为访问类型,除了身份
  • 身份是每个类中的固定名称字段 (name=_someSpecialIdentityField)
  • 身份访问始终仅限于字段,并且与没有关系它周围的 RO 属性的名称

完全让我困惑的部分是身份相关元素的约定映射(显然,属性的约定映射完全是标准费用)。我遇到的问题是如何告诉 FNH 约定我的身份字段是固定名称。我能找到的所有约定覆盖似乎都假设表示身份的属性与其底层支持字段的名称之间始终存在某种关系(例如,我可以为支持字段,但似乎不明白我如何可以说“这始终是支持字段的名称”)。

对我来说,如何通过显式映射(并且就此而言,使用 XML 映射文件)来完成此任务是显而易见的,但对我来说,如何通过 FNH 中基于约定(自动映射)映射来完成此任务则一点也不明显。

这不可能是一个非典型的用例,所以我一定是忽略了一些非常明显的东西。感谢任何 FNH 专家的想法!

Using Fluent NHibernate, I cannot seem to devise the necessary automapping conventions for the following (seemingly simple and common) use-case:

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

The desired mappings are like so:

<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>

Basically the rules are:

  • everything is field-camelcase-underscore as an access type EXCEPT identity
  • identity is a fixed-name field in every class (name=_someSpecialIdentityField)
  • identity access is always field-only and bears no relation to the name of the RO property that surrounds it

The part of this that is completely tripping me up is the convention-mapping of the identity-related elements (clearly the convention-mapping of the properties is completely standard fare). The issue I am having is how to tell FNH conventions that my identity field is a fixed-name. All of the convention overrides that I can find seem to assume there will always be some relationship between the property that represents identity and the name of its underlying backing field (e.g. I can set a 'custom prefix' for the backing field, but cannot seem to see how I can just say "this is always the name of the backing field").

Its obvious to me how to accomplish this with explicit mapping (and for that matter, with XML mapping files) but not obvious at all to me how to accomplish this with convention-based (Automapping) mapping in FNH.

This can't be an atypical use-case so I must just be overlooking something terribly obvious. Thoughts from any FNH gurus appreciated!

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

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

发布评论

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

评论(1

指尖微凉心微凉 2024-08-27 16:05:07

编辑:看看 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 自动映射。为了用 id 来实现这一点,我在 Proteus 周围创建了一个包装器,它做了两件事:

1)映射 ID

2)隐藏 id 的 Setter

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:我真的很怀念你活跃在网络空间的时光。两年前的夏天和秋天是令人兴奋的......

EDIT: Take a look at the IAutomappingConfiguration interface. Create you own implementation or override the DefaultAutomappingConfiguration class.

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

then you add it to the initialization:

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

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

Hi Steve, I think I know what you are trying to do. I use Proteus and FNH automapping. To do the trick with the id I created a wrapper around Proteus which do two things:

1) Maps the ID

2) Hides the Setter for the 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; }
    }
}

And to avoid the property IsTransient to be persisted + other stuff you can create 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: I am really missing the times when you were active in the online space. It was exciting Summer and Authumn 2 years ago...

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