Domanda

I have a class with a few properties and some methods

public class Content
{
    public int Id { get; set; }
    public string Application { get; set; }
    public string Property1 { get; set; }
    public string Property2 { get; set; }

    public override bool Equals(object obj) {...}
    public override int GetHashCode() {...}
}

With this Fluent NHibernate mapping:

public class ContentMapping : ClassMap<Content>
{ 
   public ContentMapping()
   {
      Table("vw_all_contents");

      CompositeId()
                .KeyProperty(x => x.Id, "id")
                .KeyProperty(x => x.Application, "application");

      Map(x => x.Property1, "property1");
      Map(x => x.Property2, "property2");
   }
}

Up to here everything works fine.

I now want to populate the same object but with a table a federated table that connects to another database.

So I have:

public class ContentOnProductionDatabase : Content { }

With a mapping:

public class ContenOnProductionDatabasetMapping : ClassMap<ContentOnProductionDatabase>
{ 
   public ContentOnProductionDatabaseMapping()
   {
      Table("vw_federated_all_contents");

      CompositeId()
                .KeyProperty(x => x.Id, "id")
                .KeyProperty(x => x.Application, "application");

      Map(x => x.Property1, "property1");
      Map(x => x.Property2, "property2");
   }
}

And here is where NHibernate gets really confused and the queries return mixed results from both databases.

The problem goes away if my ContentOnProductionDatabase does not extend Content but instead is a duplicate class like this:

public class ContentOnProductionDatabaseMapping
{
    public int Id { get; set; }
    public string Application { get; set; }
    public string Property1 { get; set; }
    public string Property2 { get; set; }

    public override bool Equals(object obj) {...}
    public override int GetHashCode() {...}
}

So now everything is fine but I don't like the fact that there is so much code duplication and it seems to me there must be some sort of Mapping configuration out there to force NHibernate to ignore the inheritance and differentiate the two, especially since they map to different databases.

The repository framework is an inbuilt one handles the session and the queries.

public class ContentRepository : NHibernateRepositoryBase, IContentRepository
{
    public ContentRepository(INHibernateContext context, ISettingsManager settingsManager): base(context){   }

    public Content ReadContent(int id, string application)
    {
        using (ISessionContainer container = Context.GetSessionContainer())
        {
            return
                container.AsQueryable<Content>()
                    .FirstOrDefault(c => c.Id == id && c.Application == application);
                    // All queries using <Content> return the correct results
        }
    }

    public ContentOnProductionDataBase ReadFederatedContent(int id, string application)
    {
        using (ISessionContainer container = Context.GetSessionContainer())
        {
            return
                container.AsQueryable<ContentOnProductionDataBase>()
                    .FirstOrDefault(c => c.Id == id && c.Application == application);
                    // All queries using <ContentOnProductionDataBase> return the combined results of <Content> and <ContentOnProductionDataBase>
        }
    }
}

Internally the container.AsQueryable works by invoking this:

public IQueryable<TEntity> AsQueryable<TEntity>() where TEntity : class
{
  return LinqExtensionMethods.Query<TEntity>(this.Session);
}

Any ideas how to get rid of the code duplication?

È stato utile?

Soluzione

To define the class mapping and the properties only once, you have to define a base class and define the mapping with UseUnionSubclassForInheritanceMapping which will allow you to use independent tables per entity which is derived from that base class.

You don't have to but you should declare your base class as abstract, because it will not have a database representation. So persisting the base class will fail! Meaning, you don't want anyone to use it as an entity, instead use your derived classes...

To do so, create one base, and 2 derived classes which should be stored in one table per class.

public abstract class ContentBase
{
    public virtual int Id { get; set; }
    public virtual string Application { get; set; }
    public virtual string Property1 { get; set; }
    public virtual string Property2 { get; set; }
    public override bool Equals(object obj)
    {
        [..]
    }
    public override int GetHashCode()
    {
        [..]
    }
}

public class Content : ContentBase
{
}

public class ContentOnProductionDatabaset : ContentBase
{
}

The mapping of the base class must call UseUnionSubclassForInheritanceMapping, otherwise nHibernate would combine the classes.

public class ContentBaseMapping : ClassMap<ContentBase>
{
    public ContentBaseMapping()
    {
        UseUnionSubclassForInheritanceMapping();

        CompositeId()
                  .KeyProperty(x => x.Id, "id")
                  .KeyProperty(x => x.Application, "application");

        Map(x => x.Property1, "property1");
        Map(x => x.Property2, "property2");
    }
}

The subclass mappings just have to define that the base is abstract. Here you can also define each table name the entity should use.

public class ContentMapping : SubclassMap<Content>
{
    public ContentMapping()
    {
        Table("vw_all_contents");

        Abstract();
    }
}

public class ContentOnProductionDatabaseMapping : SubclassMap<ContentOnProductionDatabaset>
{
    public ContentOnProductionDatabaseMapping()
    {
        Table("vw_federated_all_contents");

        Abstract();
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top