天道酬勤,学无止境

Programming to interfaces while mapping with Fluent NHibernate

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 coupling etc. That means pretty much "everything" refers to the interface instead of the concrete type (IMessage instead of Message). The thought behind this is to help make it more testable by being able to mock dependencies.

However, (Fluent) NHibernate doesn't love it when I try to map to interfaces instead of concrete classes. The issue is simple - according to the Fluent Wiki, it is smart to define the ID field of my class as for instance

int Id { get; private set; }

to get a typical auto-generated primary key. However, that only works with concrete classes - I can't specify an access level on an interface, where the same line has to be

int Id { get; set; }

and I guess that negates making the setter private in the concrete class (the idea being that only NHibernate should ever set the ID as assigned by the DB).

For now, I guess I will just make the setter public and try to avoid the temptation of writing to it.. But does anyone have an idea of what would be the "proper", best-practice way to create a proper primary-key field that only NHibernate can write to while still only programming to interfaces?

UPDATED

From what I understand after the two answers below from mookid and James Gregory, I may well be on the wrong track - there shouldn't be a reason for me to have an interface per entity as I have now. That's all well and good. I guess my question then becomes - is there no reason to program 100% against an interface for any entities? And if there is even a single situation where this could be justified, is it possible to do this with (Fluent) NHibernate?

I ask because I don't know, not to be critical. Thanks for the responses. :)

评论

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.

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.

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.

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.

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.

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • Fluent NHibernate, working with interfaces
    I just switched to Fluent NHibernate and I've encountered an issue and did not find any information about it. Here's the case : public class Field : DomainObject, IField { public Field() { } public virtual string Name { get; set; } public virtual string ContactPerson { get; set; } public virtual bool Private { get; set; } public virtual IAddress Address { get; set; } } IAddress is an interface implemented by a class named Address public class Address : DomainObject, IAddress { public Address() { } public virtual string City { get; set; } public virtual string Country { get; set; } public
  • How to tell Fluent NHibernate not to map a class property
    I have a class that is mapped in fluent nhibernate but I want one of the classes properties to be ignored by the mapping. With class and mapping below I get this error: The following types may not be used as proxies: iMasterengine.Data.Model.Calendar: method get_HasEvents should be virtual //my class public class Calendar : IEntity { public virtual int Id { get; private set; } public virtual string Name { get; set; } public virtual string SiteId { get; set; } public virtual IList<CalendarEvent> Events { get; set; } //ignore this property public bool HasEvents { get { return Events.Count > 0; }
  • How do you map an enum as an int value with fluent NHibernate?
    Question says it all really, the default is for it to map as a string but I need it to map as an int. I'm currently using PersistenceModel for setting my conventions if that makes any difference. Thanks in advance. Update Found that getting onto the latest version of the code from the trunk resolved my woes.
  • Generate XML mappings from fluent Nhibernate
    How do I generate xml mappings files as part of my tests in MappingIntegrationTests I need to manually check if the fluent mappings correlate to the mappings in the leagcy project.
  • Syntax to define a NHibernate Filter with Fluent Nhibernate?
    It seems I can't find the correct syntax to define a nhibernate filter using fluent Nhibernate. I'm trying to follow this ayende's blogpost: http://ayende.com/Blog/archive/2006/12/26/LocalizingNHibernateContextualParameters.aspx I defined the formula on my property with .FormulaIs() method but can't find on google how to translate this definition to fluent nhibernate: < filter-def name='CultureFilter'> < filter-param name='CultureId' type='System.Int32'/> < /filter-def>
  • Unable to cast object of type NHibernate.Collection.Generic.PersistentGenericBag to List
    I have a class called ReportRequest as: public class ReportRequest { Int32 templateId; List<Int32> entityIds; public virtual Int32? Id { get; set; } public virtual Int32 TemplateId { get { return templateId; } set { templateId = value; } } public virtual List<Int32> EntityIds { get { return entityIds; } set { entityIds = value; } } public ReportRequest(int templateId, List<Int32> entityIds) { this.TemplateId = templateId; this.EntityIds = entityIds; } } It is mapped using Fluent Hibernate as: public class ReportRequestMap : ClassMap<ReportRequest> { public ReportRequestMap() { Id(x => x.Id)
  • Circular reference causing stack overflow with Automapper
    I'm using Automapper to map my NHibernate proxy objects (DTO) to my CSLA business objects I'm using Fluent NHibernate to create the mappings - this is working fine The problem I have is that the Order has a collection of OrderLines and each of these has a reference to Order. public class OrderMapping : ClassMap<OrderDTO> { public OrderMapping() { // Standard properties Id(x => x.OrderId); Map(x => x.OrderDate); Map(x => x.Address); HasMany<OrderLineDTO>(x => x.OrderLines).KeyColumn("OrderId").Inverse(); Table("`Order`"); } } public class OrderDTO { // Standard properties public virtual int
  • Binary Blob truncated to 8000 bytes - SQL Server 2008 / varbinary(max)
    I have upgraded from Fluent Nhibernate 1.0 with Nhibernate 2.1 to pre- release 1.x with NHibernate 3.0 GA and have hit what I think is a regression, but I want to hear if that's indeed the case. I am using SQL Server Express 2008 and the MSSQL 2008 dialect and have an Image property of type System.Drawing.Image and I have mapped it like this: Map (food => food.Image) .Length (int.MaxValue) .Nullable (); The Image column in the table is of type varbinary(MAX). The generated hbm for the property is: <property name="Image" type="System.Drawing.Image, System.Drawing, Version=2.0.0.0, Culture
  • How to save a child with assigned id in nhibernate
    I have two classes: public class Parent { public virtual long? ID { get; set; } // native public virtual IList<Child> Children { get; set; } public virtual string Name { get; set; } } public class Child { public virtual long ID { get; set; } // assigned public virtual string Name { get; set; } } Instantiating and saving parent and child: child = new Child() { ID = 1, Name = "SomeName" }; parent = new Parent() { Children = new List() { child } }; session.Save(parent); Which gives me: NHibernate.StaleStateException: Unexpected row count: 0; expected: 1. I think the problem is with the assigned
  • Are there good reasons not to use an ORM? [closed]
    As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance. Closed 8 years ago. During my apprenticeship, I have used NHibernate for some smaller projects which I mostly coded and designed on my own. Now, before starting some bigger project, the discussion arose how to design data access and whether or not to use an
  • Fluent Interfaces - Method Chaining
    Method chaining is the only way I know to build fluent interfaces. Here's an example in C#: John john = new JohnBuilder() .AddSmartCode("c#") .WithfluentInterface("Please") .ButHow("Dunno"); Assert.IsNotNull(john); [Test] public void Should_Assign_Due_Date_With_7DayTermsVia_Invoice_Builder() { DateTime now = DateTime.Now; IInvoice invoice = new InvoiceBuilder() .IssuedOn(now) .WithInvoiceNumber(40) .WithPaymentTerms(PaymentTerms.SevenDays) .Generate(); Assert.IsTrue(invoice.DateDue == now.AddDays(7)); } So how do others create fluent interfaces. How do you create it? What language/platform
  • NHibernate How do I query against an IList<string> property?
    I am trying to query against an IList<string> property on one of my domain classes using NHibernate. Here is a simple example to demonstrate: public class Demo { public Demo() { this.Tags = new List<string>(); } public virtual int Id { get; set; } public virtual string Name { get; set; } public virtual IList<string> Tags { get; set; } } Mapped like this: <class name="Demo"> <id name="Id" /> <property name="Name" /> <bag name="Tags"> <key column="DemoId"/> <element column="Tag" type="String" /> </bag> And I am able to save and retrieve just fine. Now to query for instances of my domain class
  • Minimal and correct way to map one-to-many with NHibernate
    I am new to NHibernate and C#, so please be gentle! I have the following two NHibernate entities: Employee { private long _id; private String _name; private String _empNumber; private IList<Address> _addresses; //Properties... } and Address { private long _id; private String _addrLine1; private String _addrLine2; private String _city; private String _country; private String _postalCode; //Properties } and they have a one-to-many relationship from Employee to Address (each employee can have multiple addresses in their record). Conveniently ignoring the fact that more than one employee may
  • Should I transform Entity (Persistent) objects to DTO objects?
    My project is layered as follows:- DAL (Entity) --> BLL (DTO) --> ApplicationComponent (ViewModel). There will be multiple components of application (ApplicationComponent) which will access BLL. Components include windows services, web services, web API and MVC controller. I am transforming NHibernate Entity objects to DTO objects while passing them from DAL to BLL. While passing this state to ApplicationComponent, BLL again converts it to ViewModel. This helps me separate the concerns and how data is handled in each layer. I am not in favor of returning NHibernate Entity object to view for
  • Fluent interfaces and inheritance in C#
    I'll show a problem by example. There is a base class with fluent interface: class FluentPerson { private string _FirstName = String.Empty; private string _LastName = String.Empty; public FluentPerson WithFirstName(string firstName) { _FirstName = firstName; return this; } public FluentPerson WithLastName(string lastName) { _LastName = lastName; return this; } public override string ToString() { return String.Format("First name: {0} last name: {1}", _FirstName, _LastName); } } and a child class: class FluentCustomer : FluentPerson { private long _Id; private string _AccountNumber = String
  • Many-to-many mapping table
    From examples that I have seen online and in a Programming Entity Framework CodeFirst book, when you have a collection on both classes EF would create a mapping table such as MembersRecipes and the primary key from each class would link to this table. However when I do the below, I instead get a new field in the Recipes table called Member_Id and a Recipe_Id in the Members table. Which only creates two one-to-many relationships, but not a many-to-many so I could have Member 3 linked to Recipes (4,5,6) and Recipe 4 linked to Members (1,2,3) etc. Is there a way to create this mapping table? and
  • Method chaining - why is it a good practice, or not?
    Method chaining is the practice of object methods returning the object itself in order for the result to be called for another method. Like this: participant.addSchedule(events[1]).addSchedule(events[2]).setStatus('attending').save() This seems to be considered a good practice, since it produces readable code, or a "fluent interface". However, to me it instead seems to break the object calling notation implied by the object orientation itself - the resulting code does not represent performing actions to the result of a previous method, which is how object oriented code is generally expected to
  • Mapping collection of strings with NHibernate
    I have a domain class with a property IList<string> that I want to map to a table with a single data value (i.e. it has an ID, a foreign key ID to the domain entity table, and a varchar data column). I keep getting the error: Association references unmapped class: System.String How can I map a table to a collection of strings?
  • IndexOutOfRangeException Deep in the bowels of NHibernate
    I have the following mappings: public class SecurityMap : ClassMap<Security> { public SecurityMap() { Table("Security"); CompositeId().KeyProperty(k => k.Id, "SecurityId").KeyProperty(k => k.EndDate); Map(x => x.LastUpdateUser); References(x => x.Company).Columns("CompanyId", "EndDate"); References(x => x.PrimaryListing).Columns("PrimaryListingId", "EndDate"); } } public class ListingMap : ClassMap<Listing> { public ListingMap() { Table("Listing"); CompositeId().KeyProperty(k => k.Id, "ListingID").KeyProperty(k => k.EndDate); References(x => x.Security).Columns("SecurityId","EndDate"); } }
  • NHibernate configuration for uni-directional one-to-many relation
    I'm trying to set up a relationship as follows. Each Master item has one or more Detail items: public class Detail { public virtual Guid DetailId { get; set; } public virtual string Name { get; set; } } public class Master { public virtual Guid MasterId { get; set; } public virtual string Name { get; set; } public virtual IList<Detail> Details { get; set; } } And Mappings: public class MasterMap : ClassMap<Master> { public MasterMap() { Id(x => x.MasterId); Map(x => x.Name); HasMany(x => x.Details).Not.KeyNullable.Cascade.All(); } } public class DetailMap : ClassMap<Detail> { public DetailMap(