Fluent NHibernate: Mapping HasManyToMany by convention - fluent-nhibernate

I'm using Fluent NHibernate's AutoMap feature to map my entities. Most of my entities inherit from a base class Entity which has a property public IList<Tag> Tags.
The tags are in a separate table in the database, so I use a many-to-many relation. But Fluent NHibernate creates mappings for a one-to-many relation.
I'd like to write a convention to override these mappings to use HasManyToMany(...) if the class inherits from Entity. Is this possible and how?
The convention could either rely on the property's type or its name.
Some code for illustration:
// entities
public class Entity
{
public virtual int Id { get; set; }
// ... some other properties
public virtual IList<Tag> { get; set; }
}
public class Tag
{
public virtual int Id { get; set; }
public virtual string TagName { get; set; }
}
public class Event : Entity
{
// ... some properties
}
// Fluent NHibernate configuration
public static ISessionFactory CreateSessionFactory()
{
var config = new CustomAutomappingConfiguration();
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("Sql")))
.Mappings(m =>
{
m.AutoMappings.Add(AutoMap.AssemblyOf<Event>(config)
.IgnoreBase<Entity>()
.Conventions.Add<CustomForeignKeyConvention>()
.Conventions.Add<CustomManyToManyTableNameConvention>();
})
.BuildSessionFactory();
}

I don't think you can accomplish the mapping with conventions. However, if you want to keep one linking table between the entities and tags, you can do the following:
m.AutoMappings.Add(AutoMap.AssemblyOf<Event>(config)
.IncludeBase<Entity>()
.Override<Entity>(map =>
map.HasManyToMany(e => e.Tags)
.Inverse()
.Cascade.SaveUpdate()));
Notice that I changed IgnoreBase<Entity>() to IncludeBase<Entity>(). This will add an Entity table, but will keep one linking table. With this mapping, you will get the following table DDL:
create table [Entity] (
Id INT IDENTITY NOT NULL,
primary key (Id)
)
create table TagToEntity (
Entity_id INT not null,
Tag_id INT not null
)
create table Event (
Entity_id INT not null,
primary key (Entity_id)
)
create table [Tag] (
Id INT IDENTITY NOT NULL,
TagName NVARCHAR(255) null,
primary key (Id)
)
alter table TagToEntity
add constraint FKD7554554A8C4CA9
foreign key (Tag_id)
references [Tag]
alter table TagToEntity
add constraint FKD75545564C9EC79
foreign key (Entity_id)
references [Entity]
alter table Event
add constraint FKA2FD7DF664C9EC79
foreign key (Entity_id)
references [Entity]
If you choose to do an Override<> per subclass, you will have a linking table per subclass.

In my case, I wanted to use an attribute to indicate a property that should participate in a many-to-many relationship where only one side of the relationship is declared. You could easily modify this to map by other conventions.
Many-to-many relationships are handled by FluentNHibernate.Automapping.Steps.HasManyToManyStep, an IAutomappingStep returned by the DefaultAutomappingConfiguration. This step will only map a property if it discovers a corresponding property of the related type (so both ends of the many-to-many relationship have to be declared).
The approach I've taken is to:
Create a decorator class for HasManyToManyStep that supports detecting and mapping many-to-many properties based on the presence of an attribute (or some other convention)
Create a class derived from DefaultAutomappingConfiguration to when automapping and override GetMappingSteps, wrapping any instance of HasManyToManyStep with the decorator
Here's the decorator, which tries to use the default HasManyToManyStep functionality first. Otherwise, if HasManyToManyAttribute is defined for the member, it will also create the relationship. The code used to create the relationship is nearly identical to the code used by HasManyToManyStep - just without reference to the other side of the relationship.
class ExplicitHasManyToManyStep : IAutomappingStep
{
readonly IAutomappingConfiguration Configuration;
readonly IAutomappingStep DefaultManyToManyStep;
public ExplicitHasManyToManyStep(IAutomappingConfiguration configuration, IAutomappingStep defaultManyToManyStep)
{
Configuration = configuration;
DefaultManyToManyStep = defaultManyToManyStep;
}
#region Implementation of IAutomappingStep
public bool ShouldMap(Member member)
{
if (DefaultManyToManyStep.ShouldMap(member))
{
return true;
}
//modify this statement to check for other attributes or conventions
return member.MemberInfo.IsDefined(typeof(HasManyToManyAttribute), true);
}
public void Map(ClassMappingBase classMap, Member member)
{
if (DefaultManyToManyStep.ShouldMap(member))
{
DefaultManyToManyStep.Map(classMap, member);
return;
}
var Collection = CreateManyToMany(classMap, member);
classMap.AddCollection(Collection);
}
#endregion
CollectionMapping CreateManyToMany(ClassMappingBase classMap, Member member)
{
var ParentType = classMap.Type;
var ChildType = member.PropertyType.GetGenericArguments()[0];
var Collection = CollectionMapping.For(CollectionTypeResolver.Resolve(member));
Collection.ContainingEntityType = ParentType;
Collection.Set(x => x.Name, Layer.Defaults, member.Name);
Collection.Set(x => x.Relationship, Layer.Defaults, CreateManyToMany(member, ParentType, ChildType));
Collection.Set(x => x.ChildType, Layer.Defaults, ChildType);
Collection.Member = member;
SetDefaultAccess(member, Collection);
SetKey(member, classMap, Collection);
return Collection;
}
void SetDefaultAccess(Member member, CollectionMapping mapping)
{
var ResolvedAccess = MemberAccessResolver.Resolve(member);
if (ResolvedAccess != Access.Property && ResolvedAccess != Access.Unset)
{
mapping.Set(x => x.Access, Layer.Defaults, ResolvedAccess.ToString());
}
if (member.IsProperty && !member.CanWrite)
{
mapping.Set(x => x.Access, Layer.Defaults, Configuration.GetAccessStrategyForReadOnlyProperty(member).ToString());
}
}
static ICollectionRelationshipMapping CreateManyToMany(Member member, Type parentType, Type childType)
{
var ColumnMapping = new ColumnMapping();
ColumnMapping.Set(x => x.Name, Layer.Defaults, childType.Name + "_id");
var Mapping = new ManyToManyMapping {ContainingEntityType = parentType};
Mapping.Set(x => x.Class, Layer.Defaults, new FluentNHibernate.MappingModel.TypeReference(childType));
Mapping.Set(x => x.ParentType, Layer.Defaults, parentType);
Mapping.Set(x => x.ChildType, Layer.Defaults, childType);
Mapping.AddColumn(Layer.Defaults, ColumnMapping);
return Mapping;
}
static void SetKey(Member property, ClassMappingBase classMap, CollectionMapping mapping)
{
var ColumnName = property.DeclaringType.Name + "_id";
var ColumnMapping = new ColumnMapping();
ColumnMapping.Set(x => x.Name, Layer.Defaults, ColumnName);
var Key = new KeyMapping {ContainingEntityType = classMap.Type};
Key.AddColumn(Layer.Defaults, ColumnMapping);
mapping.Set(x => x.Key, Layer.Defaults, Key);
}
}
HasManyToManyAttribute class, because there is no other convention I can easily rely on in my case:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class HasManyToManyAttribute : Attribute
{
}
Configuration class derived from DefaultMappingConfiguration class:
class AutomappingConfiguration : DefaultAutomappingConfiguration
{
public override IEnumerable<IAutomappingStep> GetMappingSteps(AutoMapper mapper, IConventionFinder conventionFinder)
{
return base.GetMappingSteps(mapper, conventionFinder).Select(GetDecoratedStep);
}
IAutomappingStep GetDecoratedStep(IAutomappingStep step)
{
if (step is HasManyToManyStep)
{
return new ExplicitHasManyToManyStep(this, step);
}
return step;
}
}

Related

What is the correct way to find and set the primary key generically with EF Core

I am trying to access the primary key from a generic parameter on my entity class which is what all of my DB object inhert from.
The following code works. But is it bad practise to access the DBcontext from within the Entity class? If so, how else can I do this? Is there something like IObjectContextAdapter from EF6?
public abstract class Entity
{
private DBContext _context = new DBContext(null);
[NotMapped]
public int ID
{
get
{
return _context.PrimaryKeyValueInt(this);
}
set
{
_context.PrimaryKeyProperty(this).PropertyInfo.SetValue(this, value);
}
}
}
So, I assume you have a bunch of entity classes with various names for their primary keys, like DoodadId, WidgetId, etc. and want them all to extend a base class with a single property to access the primary key? If so, I've done something like that with Attributes and Reflection. Create an Attribute to use to identify Primary Key properties:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class EntityIdAttribute : Attribute
{
}
Then create the base class and add the property to it. In my case I use Guid for primary keys:
public abstract class Entity
{
[NotMapped]
public virtual Guid EntityId
{
get
{
return (Guid)EntityIdProperty.GetValue(this);
}
set
{
EntityIdProperty.SetValue(this, value);
}
}
private PropertyInfo EntityIdProperty => this.GetType().GetProperties()
.Where(prop => Attribute.IsDefined(prop, typeof(EntityIdAttribute)))
.FirstOrDefault();
}
Then in your entity classes, extend Entity and decorate the primary key property with [EntityId]:
public class Widget : Entity
{
[EntityId]
public Guid WidgetId { get; set; }
// ......
}

DDD - updating nested collection of value objects throws NHibernate exception

TLDR version: I'm having trouble getting my DDD domain model to work with NHibernate. If my value object itself contains a collection of value objects, I can't assign a new value without getting an NHibernate exception, and want to know what the best practice is in this situation.
Longer version:
Say I have an entity which contains a value object as a property, ValueObjectA, which itself contains a set of a different value objects of type ValueObjectB.
ValueObjectB only exists meaningfully as a property of ValueObjectA, i.e. if myEntity.ValueObjectA == null, it doesn't make sense for ValueObjectB to exist either.
I've written some example code to illustrate what I mean, with simplifications for brevity.
public class Entity
{
public int Id { get; private set; }
public ValueObjectA ValueObjectA { get; set; }
// Constructor: public Entity(ValueObjectA valueObjectA)
}
public class ValueObjectA : IEquatable<ValueObjectA>
{
public string X { get; private set; }
public ISet<ValueObjectB> ValueObjectBs { get; private set; }
// Constructor: public ValueObjectA(string x, ISet<ValueObjectB> valueObjectBs)
// Implementation of Equals/GetHahcode
}
public class ValueObjectB : IEquatable<ValueObjectB>
{
public int Y { get; private set; }
public int Z { get; private set; }
// Constructor: public ValueObjectB(int y, int z)
// Implementation of Equals/GetHahcode
}
I have a corresponding mapping class using mapping by code:
public class EntityMap : ClassMapping<Entity>
{
public EntityMap()
{
Table("Entity");
Id(x => x.Id, map => map.Generator(Generators.Identity));
Component(x => x.ValueObjectA, c =>
{
c.Property(x => x.X);
// Component relation is equilavent to <composite-element> in xml mappings
c.Set(x => x.ValueObjectBs, map =>
{
map.Table("ValueObjectB");
map.Inverse(true);
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
map.Key(k => k.Column("Id"));
}, r => r.Component(ce =>
{
ce.Property(x => x.Y);
ce.Property(x => x.Z);
}));
});
}
}
The properties of ValueObjectA are mapped to the Entity table, but the properties of ValueObjectA.ValueObjectB are mapped to another table, since it is a one to many relationship. When a ValueObjectB is removed, I want that row to be deleted in the ValueObjectB table.
Since value objects are immutable, when I change the properties of entity.ValueObjectA, I should create a new instance of ValueObjectA. The problem is that the set of ValueObjectBs is a reference type, so when I try to save the entity with a different ValueObjectA, NHibernate will throw an exception because the original set that NHibernate is tracking is no longer referenced:
A collection with cascade="all-delete-orphan" was no longer referenced
by the owning entity instance.
Consider the following code:
var valueObjectBs_1 = new HashSet<ValueObjectB>
{
new ValueObjectB(1, 2),
new ValueObjectB(3, 4)
};
var valueObjectA_1 = new ValueObjectA("first", valueObjectBs_1);
var entity = new Entity(valueObjectA_1);
// Save entity, reload entity
var valueObjectBs_2 = new HashSet<ValueObjectB>
{
new ValueObjectB(1, 2)
};
var valueObjectA_2 = new ValueObjectA("second", valueObjectBs_2);
entity.ValueObjectA = valueObjectA_2;
// Save entity again
// NHIBERNATE EXCEPTION
I've managed to get around this by creating another ValueObjectA in order to preserve the reference to the set, e.g.
valueObjectA_1.ValueObjectBs.Remove(new ValueObjectB(3, 4));
entity.ValueObjectA = new ValueObjectA(valueObjectA_2.X, valueObjectA_1.ValueObjectBs);
However... that feels like a code smell - even if I wrote a custom setter for Entity.ValueObjectA, the implementation is starting to get complicated where the design is supposed to be simple.
public class Entity
{
// ...
private ValueObjectA valueObjectA;
public ValueObjectA ValueObjectA
{
// get
set
{
// Add/Remove relevant values from ValueObjectA.ValueObjectBs
valueObjectA = new ValueObjectA(value.X, ValueObjectA.ValueObjectBs);
}
}
}
What is the best practice in this type of situation? Or is this a sign that I'm trying to do something which violates the principles of DDD?
What you have is an anemic domain model.
You should replace public setters of the entity with methods that have meaningful names from the Ubiquitous language, that check the invariants and that do all the necessary cleanup in case of value objects replacements.
Although it may seem that things are more complicated this is payed back by the fact the now the entity is in full control about what happens with its internals. You now have full encapsulation.

NHibernate table per type persist child from existing parent

I have 2 classes, Member.cs and Customer.cs and using table-per-type inheritance mapping described here.
This question poses the same problem, but with no answer.
Customer.cs
public class Customer
{
}
Member.cs
public class Member : Customer
{
public Member(Customer customer)
{
CreateFromCustomer(customer);
}
private void CreateFromCustomer(Customer customer)
{
// Here I assume I'll assign the Id so NHibernate wouldn't have to create a new Customer and know what Customer to be referred
Id = customer.Id;
}
}
CustomerMap.cs
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.Id)
.GeneratedBy.GuidComb();
}
}
MemberMap.cs
public class MemberMap : SubclassMap<Member>
{
public MemberMap()
{
KeyColumn("Id");
}
}
I tried several test case :
Test1.cs
[Test]
public void CanAddCustomer()
{
var customerRepo = /* blablabla */;
using (var tx = NHibernateSessionManager.GetSession().BeginTransaction())
{
var customer = new Customer()
customerRepo.RegisterCustomer(customer);
tx.Commit();
}
using (var tx = NHibernateSessionManager.GetSession().BeginTransaction())
{
/* Get the persisted customer */
var customer = customerRepo.GetCustomerByWhatever();
var member = customerRepo.RegisterMember(new Member(customer));
tx.Commit();
}
}
I'm expecting to have :
1 customer and 1 member which is a child of that customer
Instead I have :
2 customers (1 that is what was correctly created and 1 with all null columns) and 1 member that Id referred to all null columns Customer.
Is it the expected behavior?
I understand if we were wanted to create a child object from a transient parent object, this is a correct behavior.
But what if we were to create a child object that refers to an existing parent object?
The link I provided doesn't cover any persistence example, neither does googling.
Short Answer
No, it is not possible to "upgrade" an already persisted object to its subclass. Nhibernate simply doesn't support this. That's why you see 2 customers and one member entry. This is actually the expected behavior because Nhibernate simply creates a copy with a new ID of the object instead of creating the reference to Member...
So basically you could do either
Copy the data of Customer into Member, delete customer and save Member
Use a different object structure without subclasses where Member is a different table with it's own ID and a reference to Customer
Use native sql to insert the row into Member...
Some example:
Your classes could look like this
public class Customer
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
public class Member : Customer
{
public virtual string MemberSpecificProperty { get; set; }
}
Basically, Member could have additional properties, but will have the same properties as Customer of cause, too.
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.Id)
.GeneratedBy.GuidComb();
Map(x => x.Name);
}
}
and for the sub class, you have to map additional properties only!
public class MemberMap : SubclassMap<Member>
{
public MemberMap()
{
Map(x => x.MemberSpecificProperty);
}
}
testing it
{
session.Save(new Customer()
{
Name ="Customer A"
});
session.Save(new Member()
{
Name = "Customer B",
MemberSpecificProperty = "something else"
});
session.Flush();
}
This will create 2 entries in the customer table and one row into Member table. So this is as expected, because we created one customer and one member...
Now the "upgrade" from Customer A to Member:
using (var session = NHibernateSessionFactory.Current.OpenSession())
{
session.Save(new Customer()
{
Name ="Customer A"
});
session.Flush();
}
using (var session = NHibernateSessionFactory.Current.OpenSession())
{
var customer = session.Query<Customer>().FirstOrDefault();
//var member = customer as Member;
var member = new Member()
{
Name = customer.Name,
MemberSpecificProperty = "something else"
};
session.Delete(customer);
session.Save(member);
session.Flush();
}

Set identity seed in fluentnhibernate

Using NHibernate you can set an identity seed like so:
<column name="Column1" not-null="true" sql-type="int IDENTITY(1,1000)"/>
The FluentNHibernate IdentityPart has CustomType and SqlCustomType methods, neither does it for me though. Is there a way to fluently set an identity seed?
More info:
When I do this: Map(x => x.Id).Column("CustomerId").CustomSqlType("int IDENTITY(1,1000)");
I get this error: The entity 'Customer' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id).
When I do this: Id(x => x.Id).Column("CustomerId").CustomSqlType("int IDENTITY(1,1000)");
I get this error: More than one column IDENTITY constraint specified for column 'CustomerId', table 'Customer'
Using FluentNHibernate 1.2.0.712.
I was able to duplicate that xml by doing something like this:
Map(x => x.LoginName, "Column1").CustomSqlType("int IDENTITY(1,1000)");
Edit:
If you can't achieve what you are wanting maybe you should explicitly map this using xml for now.
There is the article at the link below about implementing custom identity generator (see: Part 1: Inheriting from TableGenerator class) but the example throws the exception for SQLite database ("SQLite errorr no such table: hibernate_unique_key"). Thus as regard SQLite there is no possibility to gain current id key from a table. It uses class TableGenerator from NHibernate API (NHibernate.Id);
http://nhforge.org/wikis/howtonh/creating-a-custom-id-generator-for-nhibernate.aspx
To avoid the exception I implemented another solution (especially the way of getting current Id). It takes advantage of Fluent-NHibernate API (GeneratedBy.Custom()). Look at the following source code:
public class MyAutoincrement<T> : IIdentifierGenerator where T : IId
{
#region IIdentifierGenerator Members
public object Generate(ISessionImplementor session, object obj)
{
NHibernate.ISession s = (NHibernate.ISession)session;
int seedValue = 1000;
int maxId = -1;//start autoincrement from zero! (fluent nhibernate start from 1 as default)
List<T> recs = s.Query<T>().ToList<T>();
if (recs.Count > 0)
{
maxId = recs.Max(x => x.getId());
}
return seedValue + maxId + 1;
}
#endregion
}
//Interface for access to current Id of table
public interface IId
{
int getId();
}
//Entity
public class MyEntity : IId
{
public virtual int Id { get; protected set; }
public virtual string MyField1 { get; set; }
public virtual string MyField2 { get; set; }
#region IId Members
public virtual int getId()
{
return this.Id;
}
#endregion
}
//Entity Mapping
public class MyEntityMap : ClassMap<MyEntity>
{
public MyEntityMap()
{
Id(x => x.Id).GeneratedBy.Custom<MyAutoincrement<MyEntity>>();
Map(x => x.MyField1);
Map(x => x.MyField1);
}
}
It works with SQLite database and involves custom identity seed.
Regards
Bronek

dynamic-component fluent automapping

Does anyone know how can we automatically map dynamic components using Fluent Automapping in NHibernate?
I know that we can map normal classes as components, but couldn't figure out how to map dictionaries as dynamic-components using fluent automapping.
Thanks
We've used the following approach successfully (with FluentNH 1.2.0.712):
public class SomeClass
{
public int Id { get; set; }
public IDictionary Properties { get; set; }
}
public class SomeClassMapping : ClassMap<SomeClass>
{
public SomeClassMapping()
{
Id(x => x.Id);
// Maps the MyEnum members to separate int columns.
DynamicComponent(x => x.Properties,
c =>
{
foreach (var name in Enum.GetNames(typeof(MyEnum)))
c.Map<int>(name);
});
}
}
Here we've mapped all members of some Enum to separate columns where all of them are of type int. Right now I'm working on a scenario where we use different types for the dynamic columns which looks like this instead:
// ExtendedProperties contains custom objects with Name and Type members
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Map(prop.Name).CustomType(prop.Type);
}
This also works very well.
What I'm still about to figure out is how to use References instead of Map for referencing other types that have their own mapping...
UPDATE:
The case with References is unfortunately more complicated, please refer to this Google Groups thread. In short:
// This won't work
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Reference(dict => dict[part.Name]);
}
// This works but is not very dynamic
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Reference<PropertyType>(dict => dict["MyProperty"]);
}
That's all for now.
I got struggle with exactly the same problem. With fluent nHibernate we cannot map this but on my own I somehow was able to solve this. My solution is to build lambda expression on the fly and the assign this into object. For instance, lets say that:
Let my copy part of the site that Oliver refer:
DynamicComponent(
x => x.Properties,
part =>
{
// Works
part.Map("Size").CustomType(typeof(string));
// Works
var keySize = "Size";
part.Map(keySize).CustomType(typeof(string));
// Does not work
part.Map(d => d[keySize]).CustomType(typeof(string));
// Works
part.References<Picture>(d => d["Picture"]);
// Does not work
var key = "Picture";
part.References<Picture>(d => d[key]);
});
And we have this problem that we need to hardcode "Picture" in mapping. But somehow after some research I created following solution:
var someExternalColumnNames = GetFromSomewhereDynamicColumns();
'x' is a DynamicComponent callback in fluent Nhibernate e.g. (DynamicColumns): DynamicComponent(a => a.DynamicColumns, x => (...content of method below...))
foreach(var x in someExternalColumnNames)
{
if (x.IsReferenceToPerson == true)
{
var param = Expression.Parameter(typeof(IDictionary), "paramFirst");
var key = Expression.Constant(x.Name);
var me = MemberExpression.Call(param, typeof(IDictionary).GetMethod("get_Item"), new[] { key });
var r = Expression.Lambda<Func<IDictionary, object>>(me, param);
m.References<Person>(r, x.Name);
}
else
{
m.Map(x.Name)
}
}
//
// Some class that we want to reference, just an example of Fluent Nhibernate mapping
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Person");
Id(x => x.PersonId, "PersonId");
Map(x => x.Name);
}
}
public class Person
{
public virtual Guid PersonId { get; set; }
public virtual string Name { get; set; }
public Person()
{ }
}
Maybe it would be helpful