I have following domain classes:
public class News: EntityBase
{
public virtual DateTime CreationDate { get; set; }
public virtual IList<DomainNameToNews> DomainNameToNews { get; set; }
public News()
{
DomainNameToNews=new List<DomainNameToNews>();
}
}
public class DomainNameToNews : EntityBase
{
public virtual DomainName DomainName { get; set; }
public virtual News News { get; set; }
}
Mapping:
public class NewsMap : ClassMap<News>
{
public NewsMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.CreationDate).Not.Nullable();
HasMany(x => x.DomainNameToNews).Cascade.AllDeleteOrphan();
}
}
public class DomainNameToNewsMap : ClassMap<DomainNameToNews>
{
public DomainNameToNewsMap()
{
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.News).UniqueKey("UQ_DOMAIN_NEWS").Cascade.Delete();
References(x => x.DomainName).UniqueKey("UQ_DOMAIN_NEWS");
}
}
Explanation:
News can be used in different domains so DomainNameToNews is relationship between them. One news can be used by multiple domains.
Problem:
What I want is to delete and add DomainToNews objects through News repository.
On update of News Object this object will have a list of DomainNameToNews so When I will update News the row from DomainNameToNews that will not be in this list I want to delete at all from database .Now the row that is not in this List will have Null News ,but I want to delete at all. How I must to map my object to achieve this?
If I didn't explain myself enough clear please ask more details. Thnaks!!
You need to specify 'inverse' at the one-to-many collection association.
public class NewsMap : ClassMap<News>
{
public NewsMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.CreationDate).Not.Nullable();
HasMany(x => x.DomainNameToNews).Inverse.Cascade.AllDeleteOrphan();
}
}
public class DomainNameToNewsMap : ClassMap<DomainNameToNews>
{
public DomainNameToNewsMap()
{
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.News).UniqueKey("UQ_DOMAIN_NEWS");
References(x => x.DomainName).UniqueKey("UQ_DOMAIN_NEWS");
}
}
Explanation on 'inverse' in this blog post
Related
Hey i have the following mysql tables:
and i have the following maping classes:
public class WalletMap : ClassMap<Wallet>
{
public WalletMap()
{
Id(x => x.wallet_id).GeneratedBy.Identity();
Map(x => x.amount);
HasManyToMany(x => x.users).Inverse().Cascade.All().Table("User_has_Wallet");
References(x => x.currency);
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x=>x.user_id).GeneratedBy.Identity();
Map(x => x.firstname);
Map(x => x.lastname);
Map(x => x.birthday);
Map(x => x.email);
Map(x => x.password);
Map(x => x.creditcardnumber);
Map(x => x.country);
Map(x => x.street);
Map(x => x.housenumber);
Map(x => x.zipcode);
Map(x => x.telephonenumber);
HasManyToMany(x => x.wallets).Cascade.All().Table("User_has_Wallet");
}
}
the error says "MySqlException: Field 'currency_id' doesn't have a default value",
does somebody has an idea how to fix this error?
When you have a relationship between 2 entities, there are 2 modes of that relationship.
Painting
Color
PaintingToColorLink
"Simple".
Your "middle" aka "link" table has ONLY 2 columns.
In the above situation..... the PaintingToColorLink table will have ONLY 2 columns.
PaintingKey
ColorKey
again ONLY 2 columns.
..
Or your M:N relationship is "complex"
Employee
long EmployeeKey
JobTitle
long JobTitleKey
EmployeeToJobTitleLink
long EmployeeToJobTitleLinkKey
long EmployeeKey
long JobTitleKey
int PriorityRank
DateTime JobStartedOnDate
So Mary may be a "software developer", but she (specifically only her) started on 01/01/2021.
And John may also be a "software developer", but he (specifically only him) started on 02/02/2022.
The JobStartedOnDate is a property of the RELATIONSHIP. It doesn't belong to the JobTitle only (not everyone starts being a software-developer on the date).....nor does it belong on the Employee (I do not start every job-title I have on the same date necessarily).
When you have a "complex" M:N, you must add this "relationship entity" to your Entity-Models and your mappings.
In my example, you need this class defined:
[Serializable]
public partial class EmployeeToJobTitleMatchLink
{
public long TheEmployeeForeignKey { get; set; }
public long TheJobTitleForeignKey { get; set; }
public long EmployeeToJobTitleLinkKey { get; set; }
/* These are "scalar properties of the ~~relationship~~ */
public int PriorityRank { get; set; }
public DateTime JobStartedOnDate { get; set; }
public Employee TheEmployee { get; set; }
public JobTitle TheJobTitle { get; set; }
}
...
In your case, you need to create this "middle" relationship object.... and an Orm-Map for it.
public class UserToWalletLink
public int UserId {get;set}
public int WalletId {get;set}
public User TheUser {get;set}
public Wallet TheWallet {get;set}
public int CurrencyId {get;set}
/* it CurrencyId is FK to another table, then you need
public Currency TheCurrency {get;set}
as well */
I am trying to implement the persistence layer of AspNet Identity using NHibernate, e.g. UserStore etc.
I have all the persistence classes, i.e. UserClaim mapped. but when I try and run the project, I get an error like:
Association references unmapped class:
Core.AspNet.Identity.NHibernate.IdentityUserClaim
I suspect this is because I declare public partial class Userclaim : IdentityUserClaim, but IdentityUserClaim isn't supposed to have a table, maybe some sort of null mapping, but I just don't know.
My current mapping looks like:
public partial class UserclaimMap : ClassMap<Userclaim>
{
public UserclaimMap()
{
Table("UserClaim");
Id(x => x.Id).Column("Id");
References(x => x.AgentUser).Column("UserId");
Map(x => x.DateCreated).Column("DateCreated").Not.Nullable();
Map(x => x.DateUpdated).Column("DateUpdated").Not.Nullable();
Map(x => x.ClaimType).Column("ClaimType").Not.Nullable();
Map(x => x.ClaimValue).Column("ClaimValue");
}
}
I suspect that your User class references this class. You have to tell him that it is actually your UserClaim class.
public UserMap()
{
HasMany<UserClaim>(x => x.Claims);
}
You could use,
AutoMap.AssemblyOf<Entity>()
.IgnoreBase<Entity>()
in your session factory to ignore base class,
reference :
Fluent NHibernate: How to tell it not to map a base class
Edit:
I have done a brief test and it seems like if you do not have a class map for the super class NHibernate actually ignores it, I added following classes and maps
public class IdentityUserClaim
{
public virtual string Name { get; set; }
}
public class Userclaim : IdentityUserClaim
{
public virtual int Id { get; set; }
public virtual DateTime DateCreated { get; set; }
public virtual DateTime DateUpdated { get; set; }
public virtual string ClaimType { get; set; }
public virtual string ClaimValue { get; set; }
}
public partial class UserclaimMap : ClassMap<Userclaim>
{
public UserclaimMap()
{
Table("UserClaim");
Id(x => x.Id).Column("Id");
Map(x => x.DateCreated).Column("DateCreated").Not.Nullable();
Map(x => x.DateUpdated).Column("DateUpdated").Not.Nullable();
Map(x => x.ClaimType).Column("ClaimType").Not.Nullable();
Map(x => x.ClaimValue).Column("ClaimValue");
}
}
and the session factory is,
private FluentConfiguration CreateConfiguration(IPersistenceConfigurer configurer)
{
return Fluently.Configure()
.Database(configurer)
.Cache(c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Userclaim>())
.ExposeConfiguration(x => x.SetInterceptor(new SqlStatementInterceptor()));
}
private IPersistenceConfigurer CreateTestDatabaseConfiguration()
{
return SQLiteConfiguration
.Standard
.InMemory()
.ShowSql();
}
So it looks like the problem is in some other mapping, could be due to a class map referring super class IdentityUserClaim instead of the UserClaim.
I'm trying to set up an in-memory sqlite database for unit testing per Ayende's recommendation. Everything is working great except one entity, UserPermission, always causes NHibernate to throw an GenericADOException "constraint failed\r\nFOREIGN KEY constraint failed"
This is a standard one-to-many relationship; each User can have zero, one or many UserPermissions.
Here are my models:
public class User
{
public virtual int Id { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual IList<UserPermission> UserPermissions { get; set; }
}
public class UserPermission
{
public virtual int Id { get; set; }
public virtual int PermissionId { get; set; }
public virtual User User { get; set; }
public virtual Organization Organization { get; set; }
}
Here are the mappings:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("USERS");
Id(x => x.Id).Column("USER_ID").GeneratedBy.Native(x => x.AddParam("sequence", "SEQ_USERS"));
HasMany(x => x.UserPermissions);
Map(x => x.LastName);
Map(x => x.FirstName);
}
}
public class UserPermissionMap : ClassMap<UserPermission>{
public UserPermissionMap()
{
Table("USER_PERMISSION");
Id(x => x.Id).Column("USER_PERMISSION_ID").GeneratedBy.Native(x => x.AddParam("sequence", "SEQ_USER_PERMISSION"));
Map(x => x.PermissionId);
References(x => x.User).Column("USER_ID");
References(x => x.Organization).Column("ORG_ID").Nullable();
}
}
I have googled around quite a bit this afternoon and came across a few seemingly relevant posts but none of their suggestions worked for me. Specifically here is what I tried:
In UserMap I tried adding Inverse() to the HasMany(x => x.UserPermissions)
Also in UserMap I tried adding Cascade.All() to the HasMany(x => x.UserPermissions)
In UserPermissionMap I tried adding Cascade.All() to References(x => x.User)
Various combinations of the above
In my tests the Organization is always null but I have tried setting it to a dummy Organization and that did not fix it
The crazy thing is that I am able to create and save UserPermission objects against an Oracle database; this error only happens when I attempt to save a UserPermission against sqlite.
You may try to define your HasMany mapping as :
HasMany(x => x.UserPermissions)
.KeyColumn("User_ID")
.Cascade
.AllDeleteOrphan()
.Inverse();
The .AllDeleteOrphan would delete a UserPermission as part of its removal from the UserPermissions list.
You may replace it with a simple .All if it is not the desired behavior.
Got it figured out. We have a Permission model that had a HasMany mapped to UserPermission, but the UserPermission does not have a References to the Permission model. At one point the UserPermission model probably had a Permission property that would return the Permission object, then we changed it to an int and forgot to update the Permission mapping.
Here is what I found in my PermissionMap:
public class PermissionMap : ClassMap<Permission>
{
public PermissionMap()
{
Table("PERMISSION");
Id(x => x.Id).Column("PERMISSION_ID");
Map(x => x.Name);
HasMany(x => x.UserPermissions).LazyLoad(); // Removed this line to fix issue
}
}
Lesson learned: when you remove a relationship between to models, don't forget to remove it from BOTH sides.
I am using NHibernate. This is the employee class
public class Employee
{
public virtual int Id { get; protected set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
This is the store class:
public class Store
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual IList<Employee> Staff { get; set; }
public Store()
{
Staff = new List<Employee>();
}
}
The following are the mapping classes. Employee Map:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap ()
{
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Store);
}
}
Store Map:
public class StoreMap:ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Staff);
// HasManyToMany(x => x.Products).Cascade.All();
//.Table("StoreProduct");
}
}
When I run this code:
using (session.BeginTransaction())
{
var stores = session.CreateCriteria(typeof(Store)).List<Store>();
//for (int i=0; i<stores.Count;)
//{
// Response.Write(st
//}
foreach (var item in stores)
{
Response.Write(item.Staff.ToList());
}
}
I receive the following error:
could not initialize a collection: [test.Models.Store.Staff#1][SQL:
SELECT staff0_.Store_id as Store4_1_, staff0_.Id as Id1_, staff0_.Id
as Id0_0_, staff0_.LastName as LastName0_0_, staff0_.FirstName as
FirstName0_0_, staff0_.Store_id as Store4_0_0_ FROM [Employee] staff0_
WHERE staff0_.Store_id=?]
You code seems to work fine for me. But I'm generating an empty scheme from scratch, don't know if you are maybe missing something or if the id references are set correctly.
If you do not specify the reference Id column for staff->Store it uses Sore_id as you can see in your query text. Does this column exist?
Anyways, if it is still not working for you, try to explicitly define a name for the .KeyColumn for your HasMany(x => x.Staff); mapping
:edit:
You need to change StoreMap to have HasMany(x => x.Staff).KeyColumn("store");
And you need to change EmployeeMap to have References(x => x.Store).Columns("store");
So that both sides link to the same reference column...
i'm new to nhibernate so maybe the response depends on my lack of knowledge.
I created these two tables:
(sorry for italian language, i hope you can understand withouth any problems).
Then, i have these object in my model:
[Serializable]
public class Profilo
{
public virtual int Id { get; set; }
public virtual string Matricola { get; set; }
public virtual string Ruolo { get; set; }
public virtual IList ListaSedi { get; set; }
public Profilo()
{
ListaSedi = new List();
}
}
[Serializable]
public class Sede
{
public virtual string CodiceSede { get; set; }
public virtual string DescrizioneSede { get; set; }
public virtual Profilo Profilo { get; set; }
}
This is the way i mapped entities using fluent nhibernate:
public class Map_Sede : FluentNHibernate.Mapping.ClassMap
{
public Map_Sede()
{
Table("TBA_Sede");
Id(x => x.CodiceSede).Column("codice_sede").GeneratedBy.Assigned();
Map(x => x.DescrizioneSede)
.Column("descrizione");
References(prof => prof.Profilo)
.Column("codice_sede");
}
}
public class Map_Profilo : FluentNHibernate.Mapping.ClassMap
{
public Map_Profilo()
{
Table("TBA_Profilo");
Id(x => x.Id).Column("id").GeneratedBy.Identity();
Map(x => x.Matricola)
.Column("matricola");
Map(x => x.Ruolo)
.Column("ruolo");
HasMany(x => x.ListaSedi)
.AsBag()
.KeyColumns.Add("codice_sede")
.Not.LazyLoad()
.Cascade.None();
}
}
Now, i'd like to insert a new Profilo instance on my. Everything seems to work but nhibernate does not insert values on TBA_Profilo.codice_sede column. I noticed that the insert statement is composed by two parameters (matricola, ruolo) - why does it forget the third parameter?
I read somewhere (on nhibernate mailing list) that's quite normal 'cause nhiberate insert values with null first and then update the same records with right values contained in the list property.
Is it right?
Am i doing any errors?
I hope to have clarified the situation.
thx guys
ps: I'm using Nhibernate 2.1 and fluent nhibernate 1.1
UPDATE: This is the code i use to save entity.
var sesionFactory = NHibernateHelper.createSessionFactory();
using (NHibernate.ISession session = sesionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
try
{
session.SaveOrUpdate(entity);
transaction.Commit();
session.Flush();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
}
}
}
UPDATE 2: Following sly answer i slightly modified my solution. These are new model entities:
[Serializable]
public class Profilo
{
public virtual int Id { get; set; }
public virtual string Matricola { get; set; }
public virtual string Ruolo { get; set; }
public virtual IList ListaSedi { get; set; }
public Profilo()
{
ListaSedi = new List();
}
}
[Serializable]
public class Sede
{
public virtual string CodiceSede { get; set; }
public virtual string DescrizioneSede { get; set; }
public virtual IList ProfiliAssociati { get; set; }
}
And new mappings:
public class Map_Sede : FluentNHibernate.Mapping.ClassMap
{
public Map_Sede()
{
Table("TBA_Sede");
Id(x => x.CodiceSede).Column("codice_sede").GeneratedBy.Assigned();
Map(x => x.DescrizioneSede)
.Column("descrizione");
HasMany(x => x.ProfiliAssociati)
.AsBag()
.KeyColumns.Add("codice_sede")
.Cascade.All();
}
}
public class Map_Profilo : FluentNHibernate.Mapping.ClassMap
{
public Map_Profilo()
{
Table("TBA_Profilo");
Id(x => x.Id).Column("id").GeneratedBy.Identity();
Map(x => x.Matricola)
.Column("matricola");
Map(x => x.Ruolo)
.Column("ruolo");
HasMany(x => x.ListaSedi)
.AsBag()
.Inverse()
.KeyColumns.Add("codice_sede")
.Cascade.SaveUpdate();
}
}
Now it seems quite to work: i mean that nhibernate can write TBA_Profilo.codice_sede column even if it doesn't cicle on Profilo.IList (it inserts the last element of this List).
Any ideas?
KeyColumns mapping is applied only child table in many-to-one connection.
If you want to have connection you will need to use Id column from TBA_portfolio table and reference it from TBA_Sede.
Something like this:
Tba_portfolio
Id|matricola|ruolo
Tba_sede
Id|PorfolioId|descrizione
Your mapping is wrong, try this:
public class Map_Profilo : FluentNHibernate.Mapping.ClassMap
{
public Map_Profilo()
{
Table("TBA_Profilo");
Id(x => x.Id).Column("id").GeneratedBy.Identity();
Map(x => x.Matricola)
.Column("matricola");
Map(x => x.Ruolo)
.Column("ruolo");
HasMany(x => x.ListaSedi)
.AsBag()
.KeyColumns.Add("codice_sede")
.Not.LazyLoad()
.Cascade.SaveUpdate();
}
}
public class Map_Sede : FluentNHibernate.Mapping.ClassMap
{
public Map_Sede()
{
Table("TBA_Sede");
Id(x => x.CodiceSede).Column("codice_sede").GeneratedBy.Assigned();
Map(x => x.DescrizioneSede)
.Column("descrizione");
References(prof => prof.Profilo)
.Column("codice_sede")
.Cascade.None()
.Inverse();
}
}
The key is, you need the parent profilio to save its child items. Have the child items do nothing to its parent. Also, your table diagram looks wrong, profolio should not have a key to sede, but sede should have a fk to profolio.