Programming to interfaces while mapping with Fluent NHibernate

后端 未结 5 1155
闹比i
闹比i 2020-12-01 01:22

I have been whipped into submission and have started learning Fluent NHibernate (no previous NHibernate experience). In my project, I am programming to interfaces to reduce

相关标签:
5条回答
  • 2020-12-01 01:59

    UPDATE: using union-subclass is not supported via the fluent interface fluent-nhibernate provides. You'll have to use a regular hbm mapping file and add it.

    I too I'm trying do this with fluent NHibernate. I don't think it should be a problem mapping interfaces. You want to use an inheritance strategy, specifically the table-per-concrete-class strategy.

    Essentially, you create a mapping definition for the base class (in this case your interface) and specify how to NHibernate should deal with implementers by using union-subclass.

    So, for example, this should allow you to make polymorphic associations:

    <class name="IAccountManager"
                    abstract="true"
                    table="IAccountManager">
    
            <id name="Id">
                    <generator class="hilo"/>
            </id>
    
            <union-subclass
                    table="DefaultAccountManager"
                    name="DefaultAccountManager">
                    <property name="FirstName"/>
            </union-subclass>
    
            <union-subclass
                    table="AnotherAccountManagerImplementation"
                    name="AnotherAccountManagerImplementation">
                    <property name="FirstName"/>
            </union-subclass>
            ...
    </class> 
    

    Note how the Id is the same for all concrete implementers. NHibernate required this. Also, IAccountManager table doesn't actually exist.

    You can also try and leverage NHibernate's Implicit Polymorphism (documented below the table-per-concrete-class strategy) - but it has tons of limitations.

    0 讨论(0)
  • 2020-12-01 01:59

    Looks like I don't have enough reputation to comment on other peoples answers yet as such I'm going to have to make this an answer in it's own right.

    References now has a generic overload to allow the cast that theGecko was looking for in his answer.

    0 讨论(0)
  • 2020-12-01 02:01

    I am having exactly the same issue. Unfortunately I have a valid reason for using entity interfaces; the entity model will be implemented in different ways and with different mappings per customer.

    The entire model needs to be read-only, so interfaces are of the style:

    public interface IAccount
    {
        long AccountId { get; }
        IHouse House { get; }
    }
    
    public interface IHouse
    {
        long HouseId { get; }
        HouseStatus Status { get; }
        IList<IAccount> Accounts { get; }
    }
    

    Concrete implementations then implement these with internal setters:

    public class Account: IAccount
    {
        public virtual long AccountId { get; internal set; }
        public virtual IHouse House { get; internal set; }
    }
    
    public class House: IHouse
    {
        public virtual long HouseId { get; internal set; }
        public virtual HouseStatus Status { get; internal set; }
        public virtual IList<IAccount> Accounts { get; internal set; }
    }
    

    I have gone down the route of mapping to the concrete classes. All is fine until you create relations which return interfaces and need to be cast to concrete implementations.

    HasMany(x => x.Accounts)
    

    can become

    HasMany<Account>(x => x.Accounts)
    

    But there is no equivalent 'cast' for

    References(x => x.House)
    

    Mapping to the interfaces (the neater solution) throws up the problem mentioned above in that the Id must exist on the topmost class for setting and requires a setter on the interface.

    public sealed class AccountMap : ClassMap<IAccount>
    {
        public PokerPlayerMap()
        {
            Id(x => x.AccountId, "account_id");
    
            DiscriminateSubClassesOnColumn("Type").SubClass<Account>(s =>  
            {
                References(x => x.House);       
            });
        }
    }
    

    For now, my only solution is to add setters to all of the interface Id fields. Its a shame the Id can't exist inside a subclass or have its type cast from the interface.

    0 讨论(0)
  • 2020-12-01 02:05

    I realise this is a diversion, and not an answer to your question (although I think mookid has got that covered).

    You should really evaluate whether interfaces on your domain entities are actually providing anything of worth; it's rare to find a situation where you actually need to do this.

    For example: How is relying on IMessage any less coupled than relying on Message, when they both (almost) undoubtedly share identical signatures? You shouldn't need to mock an entity, because it's rare that it has enough behavior to require being mocked.

    0 讨论(0)
  • 2020-12-01 02:21

    You can adjust your interface to contain only a getter:

    public interface ISomeEntity
    {
        int Id { get; }
    }
    

    Your concrete class can still implement a setter as well, and since you are programming to your interfaces you will never call the setter "by accident".

    If you want to disallow setting the id even when you hold a reference to a concrete instance, you can refrain from implementing a setter, and then let NHibernate access the field instead of the property - that's right, NHibernate can use some nifty reflection trickery to set your id field directly instead of invoking the property. Then you might map the id like this:

    Id(e => e.Id).Access.AsCamelCaseField();
    

    in which case your Id property must be backed by a corresponding id field. There are more naming conventions, e.g. if you prefer underscores as private field prefix.

    0 讨论(0)
提交回复
热议问题