NHibernate Exception while building SessionFactory if column name is same for association property - nhibernate

I have Master and Detail tables. Name of Primary Key column in both the tables is same: Id.
While building session factory, I am getting following exception:
NHibernate.MappingException: Unable to build the insert statement for class ........Detail1Entity: a failure occured when adding the Id of the class ---> System.ArgumentException: The column 'Id' has already been added in this SQL builder
Parameter name: columnName
at NHibernate.SqlCommand.SqlInsertBuilder.AddColumnWithValueOrType(String columnName, Object valueOrType)
at NHibernate.SqlCommand.SqlInsertBuilder.AddColumns(String[] columnNames, Boolean[] insertable, IType propertyType)
at NHibernate.Persister.Entity.AbstractEntityPersister.GenerateInsertString(Boolean identityInsert, Boolean[] includeProperty, Int32 j)
--- End of inner exception stack trace ---
at NHibernate.Persister.Entity.AbstractEntityPersister.GenerateInsertString(Boolean identityInsert, Boolean[] includeProperty, Int32 j)
at NHibernate.Persister.Entity.AbstractEntityPersister.GenerateInsertString(Boolean[] includeProperty, Int32 j)
at NHibernate.Persister.Entity.AbstractEntityPersister.PostInstantiate()
at NHibernate.Persister.Entity.SingleTableEntityPersister.PostInstantiate()
at NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
at NHibernate.Cfg.Configuration.BuildSessionFactory()
at ...........
I have following tables:
CREATE Table MasterTable1
(
[Id] [INT] IDENTITY (1,1) NOT NULL CONSTRAINT MasterTable1PK PRIMARY KEY,
[MasterData] [VARCHAR] (100)
)
CREATE Table DetailTable1
(
[Id] [INT] IDENTITY (1,1) NOT NULL CONSTRAINT DetailTable1PK PRIMARY KEY,
[MasterId] [INT] NOT NULL CONSTRAINT DetailTable1_MasterTable1_FK FOREIGN KEY REFERENCES MasterTable(MasterId),
[DetailData] [VARCHAR] (100)
)
Following are entity definitions:
public class Master1Entity
{
public virtual int Id { get; set; }
public virtual string MasterData { get; set; }
}
public class Detail1Entity
{
public virtual int Id { get; set; }
public virtual string DetailData { get; set; }
public virtual Master1Entity Master1 { get; set; }
}
Following are the mappings:
internal class Master1Map : ClassMapping<Master1Entity>
{
public Master1Map()
{
Table("MasterTable1");
Id(x => x.Id, im =>
{
im.Column("Id");
im.Generator(Generators.Identity);
});
Property(x => x.MasterData);
}
}
internal class Detail1Map : ClassMapping<Detail1Entity>
{
public Detail1Map()
{
Table("DetailTable1");
Id(x => x.Id, im =>
{
im.Column("Id");
im.Generator(Generators.Identity);
});
Property(x => x.DetailData);
ManyToOne(x => x.Master1, map => { map.Column("Id"); });
}
}
As explained here, I know that if I rename [MasterTable1].[Id] to [MasterTable1].[MasterId] and modify the entities and mappings accordingly, this will work well.
But, I cannot change the underlying database; this is legacy database I have to take on.
Also, this is Many-to-One relationship. One Master will have multiple Detail entries.
Is there any way to make this work without changing column name in database?

Don't confuse collection mapping (expects column from association table) with entity mapping (expects column from owner table). ManyToOne is entity mapping and it expects column from owner table. In your case owner table for Master1 is DetailTable1 and column you need to map is MasterId:
internal class Detail1Map : ClassMapping<Detail1Entity>
{
...
ManyToOne(x => x.Master1, map => { map.Column("MasterId"); });

Related

Fluent NHibernate automapping composite id with component

I have this complex situation: a database of countries/regions/states/cities which primary key is composed by a code (nvarchar(3)) in a column called "Id" plus all key columns of "ancestors" (regions/states/cities).
So the table country has only one key coumn (Id) while cities has 4 key columns (Id, StateId,regionId,CountryId). Obviously they're all related, so each ancestor column is a foreign key to the related table.
I have Entities in my Model that map this relationships. But they all derive from one type called Entity<T> where T may be a simple type (string, in etc) or a complex one (a component implementing the key).
Entity<T> implements a single property called Id of type T.
For each db table, if it has a comlex key, I implement it in a separate component, which oveerides also Equals and GetHashCode() Methods (in future I'll implement those in the Entity base class).
So I have a RegionKey componet that has 2 properties (Id and CountryId).
I have conventions for Foreign Key and primary key naming and type and that is ok.
I have also Mapping ovverrides for each complex Entity.
For simplicity, lets concentrate only on Countries and Regions table. Here they are:
public class Country: Entity<string>
{
public virtual string Name { get; set; }
public virtual IList<Region> Regions { get; set; }
}
public class Region: Entity<RegionKey>
{
public virtual string Name { get; set; }
public virtual Country Country { get; set; }
}
and the RegionKey component:
namespace Hell.RealHellState.Api.Entities.Keys
{
[Serializable]
public class RegionKey
{
public virtual string Id { get; set; }
public virtual string CountryId { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
var t = obj as RegionKey;
if (t == null)
return false;
return Id == t.Id && CountryId == t.CountryId;
}
public override int GetHashCode()
{
return (Id + "|" + CountryId).GetHashCode();
}
}
}
Here is the configuration of AutoPersistenceModel:
public ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
MsSqlCeConfiguration.Standard
.ConnectionString(x=>x.Is(_connectionString))
)
.Mappings(m => m.AutoMappings.Add(AutoMappings))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private AutoPersistenceModel AutoMappings()
{
return AutoMap.Assembly(typeof (Country).Assembly)
.IgnoreBase(typeof(Entity<>))
.Conventions.AddFromAssemblyOf<DataFacility>()
.UseOverridesFromAssembly(GetType().Assembly)
.Where(type => type.Namespace.EndsWith("Entities"));
}
private static void BuildSchema(Configuration config)
{
//Creates database structure
new SchemaExport(config).Create(false, true);
//new SchemaUpdate(config).Execute(false, true);
}
Here is the Regions entity overrides
public class RegionMappingOverride : IAutoMappingOverride<Region>
{
public void Override(AutoMapping<Region> mapping)
{
mapping.CompositeId(x=>x.Id)
.KeyProperty(x => x.Id, x=> x.ColumnName("Id").Length(3).Type(typeof(string)))
.KeyProperty(x => x.CountryId, x => x.ColumnName("CountryId").Length(3).Type(typeof(string)));
}
}
Ok now when I test this mapping I got an error saying: The data types of the columns in the relationship do not match.
I have also tried this override:
public void Override(AutoMapping<Region> mapping)
{
mapping.CompositeId()
.ComponentCompositeIdentifier(x=>x.Id)
.KeyProperty(x => x.Id.Id, x=> x.ColumnName("Id").Length(3).Type(typeof(string)))
.KeyProperty(x => x.Id.CountryId, x => x.ColumnName("CountryId").Length(3).Type(typeof(string)));
}
And it almost work but it creates a Regions table with a single column key of varbinary(8000) which is not what I want:
CREATE TABLE [hell_Regions] (
[Id] varbinary(8000) NOT NULL
, [Name] nvarchar(50) NULL
, [CountryId] nvarchar(3) NULL
);
GO
ALTER TABLE [hell_Regions] ADD CONSTRAINT [PK__hell_Regions__0000000000000153] PRIMARY KEY ([Id]);
GO
ALTER TABLE [hell_Regions] ADD CONSTRAINT [FK_Regions_Country] FOREIGN KEY ([CountryId]) REFERENCES [hell_Countries]([Id]) ON DELETE NO ACTION ON UPDATE NO ACTION;
GO
I don't have a clue of how to deal with it since it seems to me everythin is ok.
Thanks in advance for your answers
Ok I menaged to solve it: I had to sign the CompositeId class as MAPPED, since it is a component. So this is my new RegionMappingOverride:
public class RegionMappingOverride : IAutoMappingOverride<Region>
{
public void Override(AutoMapping<Region> mapping)
{
mapping.CompositeId(x=>x.Id)
.Mapped()
.KeyProperty(x =>x.Id,x=>x.Length(3))
.KeyProperty(x => x.CountryId, x=>x.Length(3));
}
}
Now the sql created is correct:
create table hell_Countries (
Id NVARCHAR(3) not null,
Name NVARCHAR(50) null,
primary key (Id)
)
create table hell_Regions (
Id NVARCHAR(3) not null,
CountryId NVARCHAR(3) not null,
Name NVARCHAR(50) null,
primary key (Id, CountryId)
)
alter table hell_Regions
add constraint FK_Region_Country
foreign key (CountryId)
references hell_Countries

Fluent NHibernate one-to-many relationship setting foreign key to null

I have a simple Fluent NHibernate model with two related classes:
public class Applicant
{
public Applicant()
{
Tags = new List<Tag>();
}
public virtual int Id { get; set; }
//other fields removed for sake of example
public virtual IList<Tag> Tags { get; protected set; }
public virtual void AddTag(Tag tag)
{
tag.Applicant = this;
Tags.Add(tag);
}
}
public class Tag
{
public virtual int Id { get; protected set; }
public virtual string TagName { get; set; }
public virtual Applicant Applicant { get; set; }
}
My fluent mapping is the following:
public class ApplicantMap : ClassMap<Applicant>
{
public ApplicantMap()
{
Id(x => x.Id);
HasMany(x => x.Tags).Cascade.All();
}
}
public class TagMap : ClassMap<Tag>
{
public TagMap()
{
Id(x => x.Id);
Map(x => x.TagName);
References(x => x.Applicant).Not.Nullable();
}
}
Whenever I try to update an applicant (inserting a new one works fine), it fails and I see the following SQL exception in the logs:
11:50:52.695 [6] DEBUG NHibernate.SQL - UPDATE [Tag] SET Applicant_id = null WHERE Applicant_id = #p0;#p0 = 37 [Type: Int32 (0)]
11:50:52.699 [6] ERROR NHibernate.AdoNet.AbstractBatcher - Could not execute command: UPDATE [Tag] SET Applicant_id = null WHERE Applicant_id = #p0 System.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Applicant_id', table 'RecruitmentApp.dbo.Tag'; column does not allow nulls. UPDATE fails.
Why is NHibernate trying to update the tag table and set Applicant_id to null? I'm at a loss on this one.
Set Applicant.Tags to Inverse will instruct NHibernate to save Tags after the Applicant.
public class ApplicantMap : ClassMap<Applicant>
{
public ApplicantMap()
{
Id(x => x.Id);
HasMany(x => x.Tags).Cascade.All().Inverse();
}
}
More detail:
Inverse (as opposed to .Not.Inverse()) means the other side of the relationship (in this case, each Tag) is responsible for maintaining the relationship. Therefore, NHibernate knows that the Applicant must be saved first so that Tag has a valid foreign key for its Applicant.
Rule of thumb: The entity containing the foreign key is usually the owner, so the other table should have Inverse

Mapping self-referencing IDictionary<string, Entity> with Fluent NHibernate

I have the following entities in my domain model:
class Entity
{
public int Id { get; set; }
}
class Foo : Entity
{
public IDictionary<string, Attribute> Attributes { get; set; }
}
class Bar : Entity
{
public IDictionary<string, Attribute> Attributes { get; set; }
}
class Attribute : Entity
{
public string Key { get; set; }
public string Value { get; set; }
public IDictionary<string, Attribute> Attributes { get; set; }
}
I'd like to map these dictionaries with Fluent NHibernate. I've gotten most things to work, but first I'm having difficulties with the self-referencing Attribute.Attributes property. This is due to NHibernate making the Key a primary key of the Attribute table as well as the Id it inherits from Entity. This is how my mapping works:
ManyToManyPart<Attribute> manyToMany = mapping
.HasManyToMany<Attribute>(x => x.Attributes)
.ChildKeyColumn("AttributeId")
.ParentKeyColumn(String.Concat(entityName, "Id"))
.AsMap(x => x.Key, a => a.Column("`Key`"))
.Cascade.AllDeleteOrphan();
if (entityType == typeof(Attribute))
{
manyToMany
.Table("AttributeAttribute")
.ParentKeyColumn("ParentAttributeId");
}
If I replace the if statement with the following:
if (entityType == typeof(Attribute))
{
manyToMany
.Table("Attribute")
.ParentKeyColumn("ParentAttributeId");
}
I get the following exception:
NHibernate.FKUnmatchingColumnsException : Foreign key (FK_Attribute_Attribute [ParentAttributeId])) must have same number of columns as the referenced primary key (Attribute [ParentAttributeId, Key])
This is due to NHibernate automatically making Key the primary key alongside Id in my Attribute column. I'd like Key to not be primary key, since it shows up in all of my many to many tables;
create table FooAttribute (
FooId INT not null,
AttributeId INT not null,
[Key] NVARCHAR(255) not null
)
I'd like the foreign keys to only reference Id and not (Id, Key), since having Key as a primary key requires it to be unique, which it won't be across all of my ManyToManys.
where do you map Attribute itself (does it contain a Composite Key)?
AttributeValue may be a better name to show that it contains a value.
.AsMap(x => x.Key) is enough to say that Key should be the dictionary key
create table FooAttribute (
FooId INT not null,
AttributeValueId INT not null
)
or consider using
mapping.HasMany(x => x.Attributes)
.KeyColumn(entity + Id)
.AsMap(x => x.Key)
.Cascade.AllDeleteOrphan();
which will create
create table Attribute (
Id INT not null,
FooId INT,
BarId INT,
ParentAttributeId INT,
Key TEXT,
Value TEXT,
)

Fluent NHibernate generates extra columns

We are using Fluent NHibernate for data object model in the company i work.
A couple of days ago, we encountered an issue that Fluent NHibernate generates an extra column which does exist neither in model nor in mapping. Here is the situation:
My Model: FirstClass.cs
public class FirstClass
{
public virtual int Id { get; private set; }
public virtual SecondClass MyReference { get; set; }
public virtual DateTime DecisionDate { get; set; }
}
My Mapping:
public class FirstClassMap : ClassMap<FirstClass>
{
public FirstClassMap()
{
Id(x => x.Id);
Map(x => x.DecisionDate);
References(x => x.MyReference);
}
}
After building the schema with the following code,
Instance._sessionFactory = Fluently.Configure()
.Database(MySQLConfiguration.Standard
.ConnectionString(connectionString)
.ShowSql())
.ExposeConfiguration(c =>
{
c.Properties.Add("current_session_context_class", ConfigurationHelper.getSetting("SessionContext"));
})
.ExposeConfiguration(BuildSchema)
.Mappings( m => m.FluentMappings.AddFromAssemblyOf<Community>())
.BuildSessionFactory();
An extra column named "SecondClass_id" is produced with index and foreign key to SecondClass table with Id column. Here is the table produced:
CREATE TABLE `FirstClass` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`DecisionDate` datetime DEFAULT NULL,
`MyReference_id` int(11) DEFAULT NULL,
`SecondClass_id` int(11) DEFAULT NULL,
PRIMARY KEY (`Id`),
KEY `MyReference_id` (`MyReference_id`),
KEY `SecondClass_id` (`SecondClass_id`),
CONSTRAINT `FK4AFFB59B2540756F` FOREIGN KEY (`MyReference_id`) REFERENCES `SecondClass` (`Id`),
CONSTRAINT `FK4AFFB59B51EFB484` FOREIGN KEY (`SecondClass_id`) REFERENCES `SecondClass` (`Id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I found that, if I rename "MyReference" to "SecondClass" (same name as the class type), there is no extra column created. But i want to use my property with the name i specified, not with the class name. Why that extra column is created? How do i fix that? I don't want extra foreign key columns hanging around.
This often happens when you're using FNH and you have a two-way relationship between entities.
public class FirstClass
{
public virtual SecondClass MyReference { get; set; }
}
public class SecondClass
{
public virtual List<FirstClass> ListOfFirstClass { get; set; }
}
public class FirstClassMap : ClassMap<FirstClass>
{
public FirstClassMap()
{
References(x => x.MyReference);
}
}
public class SecondClassMap : ClassMap<SecondClass>
{
public SecondClassMap()
{
HasMany(x => x.ListOfFirstClass);
}
}
To fix this you have to override the column name used in either ClassMap, like so:
public class SecondClassMap : ClassMap<SecondClass>
{
public SecondClasssMap()
{
HasMany(x => x.ListOfFirstClass).KeyColumn("MyReference_id");
}
}
or:
public class FirstClassMap : ClassMap<FirstClass>
{
public FirstClassMap()
{
References(x => x.MyReference).Column("SecondClass_id");
}
}
The reason for this is that FNH treats each mapping as a separate relationship, hence different columns, keys, and indexes get created.

Mapping a list of Enums

I have a table called UserPermissions with a FK to the users table by userId and then a string column for the string value of an enum.
The error I am seeing is NHibernate.MappingException: An association from the table UserPermissions refers to an unmapped class: GotRoleplay.Core.Domain.Model.Permission
My Permission Enum:
public enum Permission
{
[StringValue("Add User")]
AddUser,
[StringValue("Edit User")]
EditUser,
[StringValue("Delete User")]
DeleteUser,
[StringValue("Add Content")]
AddContent,
[StringValue("Edit Content")]
EditContent,
[StringValue("Delete Content")]
DeleteContent,
}
The property in my User class:
public virtual IList<Permission> Permissions { get; set; }
My database table:
CREATE TABLE dbo.UserPermissions
(
UserPermissionId int IDENTITY(1,1) NOT NULL,
UserId int NOT NULL,
PermissionName varchar (50) NOT NULL,
CONSTRAINT PK_UserPermissions PRIMARY KEY CLUSTERED (UserPermissionId),
CONSTRAINT FK_UserPermissions_Users FOREIGN KEY (UserId) REFERENCES Users(UserId),
CONSTRAINT U_User_Permission UNIQUE(UserId, PermissionName)
)
My attempt at mapping the permissions property of my user object:
HasManyToMany(x => x.Permissions)
.WithParentKeyColumn("UserId")
.WithChildKeyColumn("PermissionName")
.WithTableName("UserPermissions")
.LazyLoad();
What am I doing wrong that it can't map the permission to a list of enum values?
here is the way which worked for me
HasMany(x => x.Licences)
.WithTableName("DriverLicence")
.AsElement("Level").AsBag();
look here for more information answer
You need to specify the type so that NHibernate can convert the value in the table to a member of the Permission enum.
HasManyToMany(x => x.Permissions)
.WithParentKeyColumn("UserId")
.WithChildKeyColumn("PermissionName")
.WithTableName("UserPermissions")
.LazyLoad()
.CustomTypeIs(typeof(Permission));
Edited to add:
I'm sorry, I should have noticed that you had this as ManyToMany. That's not possible: You can't have a Users collection (other side of m:m) hanging off an enum. You need to define this as 1:m or create a Permission table and class and map that as m:m.
I thought I would post the code that I chose for my solution. It's only a workaround. I wish Fluent would support Enum lists, but until it does, here's a possible solution:
The Enum - This is my enum, your standard enum.
public enum PermissionCode
{
//site permissions 1-99
ViewUser = 1,
AddUser = 2,
EditUser = 3,
DeleteUser = 4
}
Next, I have my Permission class.
public class Permission
{
public virtual int PermissionId { get; set; }
public virtual string PermissionName { get; set; }
public virtual PermissionCode PermissionCode
{
get
{
return (PermissionCode)PermissionId;
}
}
}
As you can see, I have an ID and a name, and then a property that converts the Id into my PermissionCode enum.
The mapping looks like this:
public class PermissionMap : ClassMap<Permission>
{
public PermissionMap()
{
WithTable("Permissions");
Id(x => x.PermissionId).GeneratedBy.Identity();
Map(x => x.PermissionName);
}
}
Since the PermissionCode property is derived, we don't do anything in the mapping.
My Table structure behind the mapping looks like this:
CREATE TABLE dbo.Permissions
(
PermissionId int NOT NULL,
PermissionName varchar (50) NOT NULL,
CONSTRAINT PK_Permissions PRIMARY KEY CLUSTERED (PermissionId)
)
If you wanted to use the name instead of the integer value, with some slight modifications you can. It depends on your personal preferences.