I'm using the new Map by Code pieces in NHibernate. My understanding was that an update was made in NHibernate 3 so that unidirectional one-to-many relationships would no longer insert null on the foreign key then update it to the correct value, as long as you set inverse=false on the collection and made the foreign key not nullable.
What I'm seeing is that NHibernate now INSERTs the correct foreign key, but it still issues an additional UPDATE that sets the foreign key to the value that was used in the insert?!?
Have I done something incorrectly in my mapping? (A user can have many passwords. The password object does not reference back to the user in my domain.)
mapper.Class<Password>(map =>
{
map.Table("Passwords");
map.Id(x => x.Id, x => { x.Generator(Generators.Native); x.Column("PasswordId"); });
map.Property(x => x.PasswordFormat, x => { x.NotNullable(true); });
map.Property(x => x.Salt, x => { x.Length(100); });
map.Property(x => x.PasswordValue, x => { x.NotNullable(true); x.Length(500); });
map.Property(x => x.CreateDate, x => { x.NotNullable(true); });
});
mapper.Class<User>(map =>
{
map.Table("Users");
map.Id(x => x.Id, x => { x.Generator(Generators.Native); x.Column("UserId"); });
map.Property(x => x.UserName, x => { x.NotNullable(true); x.Length(100); x.UniqueKey("UX_Users_Username"); });
map.Property(x => x.Email, x => { x.Length(100); x.Index("IX_Users_Email"); });
map.Property(x => x.IsAnonymous, x => { x.NotNullable(true); });
map.Property(x => x.IsApproved, x => { x.NotNullable(true); });
map.Property(x => x.LastActivityDate, x => { x.NotNullable(true); });
map.Property(x => x.CreateDate, x => { x.NotNullable(true); });
map.Set(x => x.Passwords, x => { x.Access(Accessor.Field); x.Inverse(false); x.Key(k => { k.Column("UserId"); k.NotNullable(true); k.ForeignKey("FK_Passwords_UserId"); }); x.Cascade(Cascade.All); x.Lazy(CollectionLazy.Lazy); }, x => x.OneToMany());
});
Note: This is using built-in NHibernate.Mapping.ByCode, not Fluent NHibernate.
Turns out I can accomplish this by setting k.Update(false) on the foreign key portion of the Passwords collection mapping.
The answer from hazzik on the following question queued me in.
https://stackoverflow.com/a/11576097/139694
It should be Inverse() not Inverse(false).
Inverse() means that other entity owns the relationship and it is responsible for providing data for NHibernate about the relationship while inserting/updating information i.e., if "user" is set to on inverse, "password" needs to take care of providing relationship info. to NHibernate.
For this you need to set up "User" reference property on "Password" entity. And while creating/updating the password, assign user property explicitly.
//create new password
Password objPassword = new Password();
objPassword.otherproperties =///assign
objPassword.User = <<---assign the user property
Currently, you have Inverse(false) which is default setting for NHibernate. In this case, insert statements will be executed with passwordid as null and then references are updated resulting in two operations.
Related
I want my "tag_post" table to be created in "article" schema but it's created in "public" schema.
List(x => x.Tags, l =>
{
l.Where("deleted = 0");
l.Key(k =>
{
k.Column("post_id");
k.NotNullable(true);
});
Schema(Constants.DatabaseSchemaNames.Article);
l.Table("tag_post");
}, x =>
{
x.ManyToMany(m => m.Column("tag_id"));
});
I have never used mapping by code, but i assume this is the solution:
List(x => x.Students, l =>
{
l.Where("deleted = 0");
l.Key(k =>
{
k.Column("post_id");
k.NotNullable(true);
});
l.Schema(Constants.DatabaseSchemaNames.Article);
l.Table("tag_post");
}, x =>
{
x.ManyToMany(m => m.Column("tag_id"));
});
i am using nhibenate by code mappings. for some reason it is doing eager fetching by default, whereas it should be lazy.
below is the mapping i have:
public EntityMap()
{
Lazy(true);
Id(x => x.Id, map =>
{
map.Generator(Generators.GuidComb);
map.UnsavedValue("00000000-0000-0000-0000-000000000000");
});
}
so i tried to specify the lazy(true) in the base class, so that all the relationships are done with lazy loading.
i am also using mapping by convention, which is configured as below:
// foreign key convention (many2one side)
mapper.BeforeMapManyToOne += (insp, prop, map) => map.Lazy(LazyRelation.Proxy);
mapper.BeforeMapManyToOne += (insp, prop, map) => map.Fetch(FetchKind.Select);
// bag conventions (one2many side)
mapper.BeforeMapBag += (insp, prop, map) => map.Lazy(CollectionLazy.Lazy);
mapper.BeforeMapBag += (insp, prop, map) => map.Fetch(CollectionFetchMode.Select);
// set conventions (one2many side)
mapper.BeforeMapSet += (insp, prop, map) => map.Lazy(CollectionLazy.Lazy);
mapper.BeforeMapSet += (insp, prop, map) => map.Fetch(CollectionFetchMode.Select);
so i have tried all the settings to make it fetch lazy, but its still fetching eager..
below is the query i am using to load the data:
var session = SessionManager.GetCurrentSession();
return session.QueryOver<Customer>().List();
the one/many to many mapping is specified as below:
Bag(x => x.Customer, colmap => { }, map => map.OneToMany(x => { }));
ManyToOne(x => x.Orders, map => { map.NotNullable(true); });
please help!!!
all the settings mentioned above were added to make it lazy load, initially none of the settings where specified....
I have two classes that I'm trying to map in Loquacious Nhibernate.
The mapping is like the following
public class FooMap : ClassMapping<Foo>
{
Table("FooTableName");
ComposedId(compIDMapper =>
{
compIDMapper.Property(x => x.SomeInt, m => m.Column("SomeInt"));
compIDMapper.ManyToOne(x => x.SomeReference, m => m.Column("SomeReference"));
});
}
public class BarMap : ClassMapping<Bar>
{
Table("BarTableName");
Id(x => x.ID, m => m.Column("barID"));
ManyToOne(x => x.Foo, m => m.Columns( columnMapper =>
{
columnMapper.Name("SomeIntID"); //Both of these columns are in the BarTableName like they should be
columnMapper.Name("SomeReferenceID");
}));
}
But when the mappings are being built I get the following error:
Foreign key (FK554EAF2427B2CA28:BarTableName[SomeIntID])) must have same number of columns as the refe,renced primary key (FooTableName[SomeInt, SomeReference])
I'm not sure what I'm doing wrong, it looks like it should work, but I've been banging my head on this for awhile now and haven't gotten anywhere. Any ideas on what I'm doing wrong?
Finally figured this out, posting this for anyone else who comes along.
My problem was misunderstanding the columns mapper. What it is supposed to be is the following:
ManyToOne(x => x.Foo, m => m.Columns(new Action<IColumnMapper>[]
{
colMapper => colMapper.Name("SomeIntID"),
colMapper => colMapper.Name("SomeReferenceID")
}));
This solved the issue. Should have noticed it when I looked at the function signature, but I completely missed it.
And also another shorter way
ManyToOne(x => x.Foo, m => m.Columns(c=> c.Name("SomeIntID"),c => c.Name("SomeReferenceID")));
How can I map IDictionary<Entity, Component>? I've done this way:
Map<GeneralResourceType, Quantity>(x => x.BookedResources,
c =>
{
c.Key(ck => ck.Column("ProposedAction"));
c.Table("BookedResources");
},
k => k.ManyToMany(key => key.Column("ResourceTypeId")),
r => r.Component(qc => QuantityMapping.Mapping()));
(where GeneralResourceType is a mapped Entity and Quantity is a ValueObject). But during the call of BuildSession() exception is thrown:
NHibernate.MappingException : An association from the table BookedResources refers to an unmapped class: {MyNamespace}.Quantity.
Seams like it tries to find ClassMapping for Quantity, while value part mapped as Component.
First variant:
Map component in separate class inherited from ComponentMapping generic class.
Map dictionary property as follows:
Map(x => x.BookedResources, c =>
{
//any options access, cascade etc
});
Second variant (inline):
Map(x => x.BookedResources, x =>
{
//any options access, cascade etc
},
x => x.Element(),
x => x.Component(c =>
{
c.Class<Quantity>();
c.Property(p => p.Amount);
c.Property(p => p.Unit);
// any other properties
}
));
I have a very basic need to get some data from the database and return a DTO. I found that joining multiple tables using nHibernate and "projecting" so to say, to a DTO to be quite a bit of code. After looking at several examples, most which didn't work leaving me a DTO with null values, I cam up with the following and was wondering if you nHibernate ninja's out there could tell me if there is a better way.
public IOpenIdUser GetOpenIdUser(string claimedIdentifier, IOpenIdUser openIdUserDto)
{
User user = null;
OpenIdUser openIdUser = null;
Profile profile = null;
UserType userType = null;
return
SessionWrapper.Session.QueryOver(() => user).JoinAlias(() => user.Profiles, () => profile).
JoinAlias(() => user.OpenIdUsers, () => openIdUser).JoinAlias(() => user.UserType, () => userType)
.Where(() => user.UserName == claimedIdentifier)
.SelectList(l => l
.Select(x => openIdUser.OpenIdUserId).WithAlias(() => openIdUser.OpenIdUserId)
.Select(x => user.UserId).WithAlias(() => openIdUserDto.UserId)
.Select(x => openIdUser.OpenIdClaimedIdentifier).WithAlias(
() => openIdUserDto.ClaimedIdentifier)
.Select(x => openIdUser.OpenIdFriendlyIdentifier).WithAlias(
() => openIdUserDto.FriendlyIdentifier)
.Select(x => openIdUser.OpenIdEndPoint).WithAlias(
() => openIdUserDto.OpenIdEndPoint)
.Select(x => user.UserName).WithAlias(() => openIdUserDto.UserName)
.Select(x => userType.Type).WithAlias(() => openIdUserDto.UserType)
.Select(x => profile.DisplayName).WithAlias(() => openIdUserDto.DisplayName)
.Select(x => profile.EmailAddress).WithAlias(() => openIdUserDto.EmailAddress)
.Select(x => openIdUser.DateCreated).WithAlias(() => openIdUserDto.DateCreated)
.Select(x => openIdUser.LastUpdated).WithAlias(() => openIdUserDto.LastUpdated)
.Select(x => openIdUser.UsageCount).WithAlias(() => openIdUserDto.UsageCount)
).TransformUsing(Transformers.AliasToBean<OpenIdUserDto>()).Future<OpenIdUserDto>().Single();
}
This method sits in my UserRepository and is called by my UserService. Please not that this actually works, I just think it is overkill for such a simple task. Also please note that I am new to this so if this code is crappy I apologize in advance.
If you use Queryover then this is the only way.
If you really think less lines of code are preferable, more intuitive, or sits better with you then you can do either:-
Create a DB view and create mapings files for the view with
mutable="false" in your class definition and use protected set; on your class properties
Use the LINQ provider instead e.g. .Query (see
this blog post for more info)