How to override the mapping of generic class?
I have this class:
public abstract class TranslatableEntity<TTranslation, TEntity> : Entity
{
public virtual String Name { get;set; }
// ...
}
And I whant to do this:
public class TranslatableEntityMap<T> : IAutoMappingOverride<TranslatableEntity<T>>
{
public void Override(AutoMapping<TranslatableEntityMap<T>> mapping)
{
mapping.IgnoreProperty(x => x.Name);
}
}
How can I do this?
Thank you!
AFAIK It is not possible. More over it has no sense to map generic class sice mapping requires the real class with implementation.
I don't think it is possible to map generic classes with NHibernate.
Related
I am having some problems mapping the following scenario in Fluent Nhibernate using Table Per Concrete class:
Let's say I have the following class definitions:
public class Reading { .... }
public class CarReading : Reading { .... }
public class TruckReading : Reading { .... }
public class Alert
{
....
public virtual Reading AReading { get; set; }
}
So my question is how to create the mapping class for Alert, if it has a one to one relationship with reading class (could be either truck reading or car reading) and instruct nhibernate to know which table to load the data from (TruckReading table or CarReading Table)
public class AlertMap : ClassMap<Alert>
{
....
HasOne(x => x.AReading);
}
If anyone could point me in the right direction that would be much appreciated.
Thanks.
public class AlertMap : ClassMap<Alert>
{
....
ReferenceAny(x => x.AReading)
.EntityIdentifierColumn("readingid")
.EntityTypeColumn("readingtype")
.IdentityType<int>()
.AddMetaValue<CarReading>("car")
.AddMetaValue<TruckReading>("truck");
}
i have the following scenario
public abstract class BaseClass
{
public virtual int Id {get; set};
public virtual string Name {get; set;}
}
public class FirstSubClass : BaseClass
{
//properties and behaviour here
}
public class SecondSubClass : BaseClass
{
//properties of SecondSubclass Here
}
public class ProcessStep
{
public virtual IList<BaseClass> ContentElements {get; set;}
}
for mapping i have used following code snippet :-
this._sessionFactory =
Fluently.Configure().Database(SQLiteConfiguration.Standard
.ConnectionString(#"Data Source=SqliteTestSqlDataAccess.s3db; Version=3; New=True; Pooling=True; Max Pool Size=1;"))
.Mappings(m => m.AutoMappings.Add(AutoMap.Assembly(assemblyWithDomainClasses).Conventions.Add(DefaultCascade.All())))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
By default fluent will ignore the abstract base class that is BaseClass.
But as in the class ProcessStep there is property ContentElements which returns IList , i am getting an exception:-
NHibernate.MappingException : Association references unmapped class: BaseClass
If i include the base class using the IncludeBase(typeof(BaseClass)) then it works fine but it creates a table for BaseClass and Derived classes and the records are linked with FK-PK relationship(table per subclass).
What i want to achieve is table per concrete class. that is each derive class will have it's own table where there will all properties of derived class + properties in the base class.
Any idea how to achieve it?
Since I haven't seen your mapping, let me provide mine. You could achieve this by doing like this
public class BaseClassMap:ClassMap<BaseClass>
{
public BaseClassMap()
{
/*
* Identity generator can't be native because subclass objects should be unique
* So use HiLo or Guid or other generators which will generate unique id on the child tables
*/
Id(x => x.Id).GeneratedBy.Guid();
Map(x => x.Name);
UseUnionSubclassForInheritanceMapping(); // This is important - uses union-subclass mappings for the derived classes
}
}
public class FirstSubClassMap : SubclassMap<FirstSubClass>
{
public FirstSubClassMap()
{
Table("FirstSubClassTable");
// Map properties for FirstSubClass
}
}
public class SecondSubClassMap : SubclassMap<SecondSubClass>
{
public SecondSubClassMap()
{
Table("SecondSubClassTable");
// Map properties for SecondSubClass
}
}
It caused me headache to implement the "Table per Concrete Class" inheritance strategy with an abstract base class with nhibernate automapping. But I think, I've finally found a solution and want to share it with you. I also think, it's not added to the automapping docs, because it's maybe considered as a "weak" database design.
First here are some resources I found about this topic:
https://www.codeproject.com/Articles/232034/Inheritance-mapping-strategies-in-Fluent-Nhibernat
Example implementation of inheritance strategies in fluent nhibernate (!automapping).
https://github.com/jagregory/fluent-nhibernate/wiki/Automapping-inheritance
Documentation of inheritance strategies in fluent nhibernate with automapping.
(can't add another link) https : // github . com /jagregory/fluent-nhibernate/pull/25/commits/2984c8c4e89aa4cec8625538f763c5931121a4e7
Bug-Fix Union-SubClass implementation (Table per Concrete Class)
These resources basically describe how you need to do it:
As you already mentioned fluent nhibernate ignores abstract base classes. So you need to add them explicitly.
// abstractBaseTypes is just a simple enumeration of base types
// model is the AutoPersistenceModel
abstractBaseTypes.ForEach(m => model = model.IncludeBase(m));
a) If you know the abstract base types at compile time you can use
//sets the union subclass strategy for the known base model
model.Override<SuperType>(m => m.UseUnionSubclassForInheritanceMapping()))
b) If you don't know the concrete types you can create a mapping override for each base type:
public class AbstractRightEntryMappingOverride : IAutoMappingOverride<AbstractRightEntry>
{
public void Override(AutoMapping<AbstractRightEntry> mapping)
{
mapping.UseUnionSubclassForInheritanceMapping();
}
}
// You need to tell nhibernate where to find the overriden mappings.
// You simply can add the assemblies again.
modelAssemblies.ForEach(a => model = model.UseOverridesFromAssembly(a));
Is it possible to create a simple convention to modify the polymorphism mode of a class, if there is a joined-subclass ?
Doing this :
public class EntityMap : ClassMap<EntityBase>
{
public EntityMap()
{
Polymorphism.Explicit();
}
}
but inside a convention. Using IClassConvention doesn't work, as the Polymorphism property is read only :
public class TestConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
// read only property !
instance.Polymorphism = Polymorphism.Explicit;
}
}
Try
instance.Polymorphism.Explicit();
I'm trying to use the automap functionality in fluent to generate a
DDL for the following model and program, but somehow I keep getting
the error "Association references unmapped class: IRole" when I call
the GenerateSchemaCreationScript method in NHibernate. When I replace
the type of the ILists with the implementation of the interfaces (User
and Role) everything works fine. What am I doing wrong here? How can I
make fluent use the implemented versions of IUser and IRole as defined
in Unity?
public interface IRole
{
string Title { get; set; }
IList<IUser> Users { get; set; }
}
public interface IUser
{
string Email { get; set; }
IList<IRole> Roles { get; set; }
}
public class Role : IRole
{
public virtual string Title { get; set; }
public virtual IList<IUser> Users { get; set; }
}
public class User : IUser
{
public virtual string Email { get; set; }
public virtual IList<IRole> Roles { get; set; }
}
I use the following program to generate the DDL using the
GenerateSchemaCreationScript in NHibernate:
class Program
{
static void Main(string[] args)
{
var ddl = new NHibernateSessionManager();
ddl.BuildConfiguration();
}
}
public class NHibernateSessionManager
{
private ISessionFactory _sessionFactory;
private static IUnityContainer _container;
private static void InitContainer()
{
_container = new UnityContainer();
_container.RegisterType(typeof(IUser), typeof(User));
_container.RegisterType(typeof(IRole), typeof(Role));
}
public ISessionFactory BuildConfiguration()
{
InitContainer();
return
Fluently.Configure().Database(MsSqlConfiguration.MsSql2008
.ConnectionString("ConnectionString"))
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<IUser>()))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private void BuildSchema(Configuration cfg)
{
var ddl = cfg.GenerateSchemaCreationScript(new
NHibernate.Dialect.MsSql2008Dialect());
System.IO.File.WriteAllLines("Filename", ddl);
}
}
I am in the same situation as you. Having used the ClassMap before I know you can do this with Fluent but I had never used the AutoMapping feature before. I have successfully been able to do a one to one mapping with the AutoMapper using an IReferenceConvention (see previous SO post).
I have now hit the same problem as you where I have a one to many mapping which I am now having a problem with. There is an IHasManyConvention interface which I have started to look at but have had no luck as of yet.
Just because some thing is hard to do it doesn't make it wrong, mapping to interfaces defiantly has value and can easily be done in the raw nHibernate mapping files or by using Fluents ClassMap mapping files. I think once people start do more with AutoMapping feature there will be more blog posts.
EDIT
I have found an interim solution using an IAutoMappingOverride. Below is a rough example of what you need.
public class RoleAutoMappingOverride : IAutoMappingOverride<Role>
{
public void Override(AutoMapping<Role> mapping)
{
mapping.HasMany<User>( x => x.Users ).KeyColumn( "User_id" );
}
}
EDIT
A college of mine has worked out a better solution that uses conventions instead of the override. This covers how to do a single class but if you look at the SO post I mentioned before you can see how this could be made generic.
public class Foo : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
if (instance.ChildType == typeof(Role))
{
instance.Relationship.CustomClass<User>();
}
}
}
EDIT
I have now turned this and my other post into a blog post:
http://bronumski.blogspot.com/2011/01/making-fluent-nhibernate-automapper.html
You can't provide an interface as the type T in AssemblyOf<T>, you need to provide a concrete type. Or you could use the method that accepts an assemply:
.Mappings(m => m.AutoMappings.Add(
AutoMap.Assembly(myAssembly)))
Edit: The problem is that your classes contain collections of interface types instead of class type. I don't know if it's possible to automap interfaces in this manner. Also, I think there's rarely any value in using interfaces to specify domain objects.
I'm fairly new to NHibernate and have run into a strange inheritance chaining issue with my repository classes. I've been using Gabriel Schenker's FAQ as a reference, and following his examples I've been creating interfaces to define contracts for DAO operations in "repository" classes. The data schema I'm working with is rather extensive, and after a little while I found myself duplicating a lot of code. Specifically, the Add, Update, Delete, and "GetByID" methods were exactly the same after I added a generic "EntityType" parameter to the base interface. So, for example, this would be the most basic interface for repository operations:
public interface IBasicRepository<EntityType> where EntityType : class
{
void Add(EntityType entity);
void Remove(EntityType entity);
void Update(EntityType entity);
EntityType GetByID<IDType>(IDType id);
}
I'll just talk about the Add method from now on, for the sake of brevity. With the generic EntityType, the implementations were all the same:
public void Add(EntityType entity)
{
using (ISession session = NHUtility.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(entity);
transaction.Commit();
}
}
}
Obviously, typing this same method body repeatedly (with the slight change of type) isn't only annoying, it's bad design in my book. So I created an abstract base class I'll call RepositoryBase which provides the implementation for Add(). Since I'm using an abstract instead of an interface, I "break the interface chain" for classes inheriting from RepositoryBase and am forced to make any derivation abstract as well, even though it seems more "correct" to use an interface. Using this crappy little entity example....
public class Entity1
{
public Guid ID { get; set; }
public String Name { get; set; }
}
...one can't do this...
public interface IEntity1Repository : RepositoryBase<Entity1>
{
//Illegal!!!! Bad, naughty programmer!
}
...but this is fine....
public abstract class Entity1RepositoryBase : RepositoryBase<Entity1>
{
public abstract ICollection<Entity1> GetByName(string name);
}
This just bothers me. It works, but it rubs me the wrong way, especially as the chain of inheritance/implementation with this particular schema could go quite deep. So I guess my questions are:
Am I just being stupid and anal retentive about this?
Is there a different/better design that I should be looking at here? I've looked at some other examples (notably Billy McCafferty's) and Schenker's approach seems simplest for novice NHibernating.
Thanks in advance.
One option could be:
public interface IRepository<T> where T: class
{
void Add(T entity);
void Remove(T entity);
void Update(T entity);
T GetByID<IDType>(IDType id);
}
With a base class that implements that interface. Ie:
public abstract class RepositoryBase<T> : IRepository<T> where T: class
{
...
}
Which is then extended for each type of entity if necessary:
public interface IProductRepository : IRepository<Product>
{
// Add extra methods
}
public class ProductRepository : RepositoryBase<Product>, IProductRepository
{
// Implement extra methods
}