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
}
));
Related
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....
e.g.
this.Bag(
r => "privatefieldtomap",
map =>
{
map.Access(Access.Field);
map.Table("table");
map.Key(k => k.Column("foreignkey"));
},
r => r.Element(m => m.Column("columntomap")));
public SomeType()
{
this.Id(p => "privateidfield", Access(Access.Field));
this.Table("SomeTable");
this.Property(p => p.SomeProperty);
}
both throw an exception "expression expected; constant found"
We could do this using xml mapping.
Answer here https://groups.google.com/forum/#!topic/nhusers/wiH1DPGOhgU turns out there is an overload that accepts a string as first parameter whereas I was using a lambda expression.
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.
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")));
I have query in HQL which works good:
var x =_session.CreateQuery("SELECT r FROM NHFolder f JOIN f.DocumentComputedRights r WHERE f.Id = " + rightsHolder.Id + " AND r.OrganisationalUnit.Id=" + person.Id);
var right = x.UniqueResult<NHDocumentComputedRight>();
Basically I receive NHDocumentComputedRight instance.
I've tried to implement the same query in QueryOver. I did this:
var right = _session.QueryOver<NHFolder>().JoinAlias(b => b.DocumentComputedRights, () => cp).Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.Select(u => cp).List<NHDocumentComputedRight>();
But I get null reference exception.
How can I implement this query in QueryOver?
Update (added mappings) - NHibernate 3.2:
public class FolderMapping: ClassMapping<NHFolder>
{
public FolderMapping()
{
Table("Folders");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
Set(x => x.DocumentComputedRights, v =>
{
v.Table("DocumentComputedRightsFolder");
v.Cascade(Cascade.All | Cascade.DeleteOrphans);
v.Fetch(CollectionFetchMode.Subselect);
v.Lazy(CollectionLazy.Lazy);
}, h => h.ManyToMany());
Version(x => x.Version, map => map.Generated(VersionGeneration.Never));
}
}
public class DocumentComputedRightMapping : ClassMapping<NHDocumentComputedRight>
{
public DocumentComputedRightMapping()
{
Table("DocumentComputedRights");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
ManyToOne(x => x.OrganisationalUnit, map =>
{
map.Column("OrganisationalUnit");
map.NotNullable(false);
map.Cascade(Cascade.None);
});
}
}
public class OrganisationUnitMapping : ClassMapping<NHOrganisationalUnit>
{
public OrganisationUnitMapping()
{
Table("OrganisationalUnits");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
}
}
Thanks
AFAIK criteria/queryOver can only return the entity it was created for (NHFolder in your example) or columns which are set to entity with aliastobean. you could do a correlated subquery instead.
var subquery = QueryOver.Of<NHFolder>()
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.Select(u => cp.Id);
var right = _session.QueryOver<NHDocumentComputedRight>()
.WithSubquery.Where(r => r.Id).Eq(subquery)
.SingleOrDefault<NHDocumentComputedRight>();
I think you have a problem with the select statement, have you tried something like this:
var right = _session.QueryOver<NHFolder>()
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Select(x => x.DocumentComputedRights)
.Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.List<NHDocumentComputedRight>();
This is what is working for me so it should work in you case as well.
I would guess that the main reason behind the problem is the lack of proper overload on the Select method. In reality you would like to write it like this:
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Select(() => cp)
but the Expression<Func<object>> is not there. Hopefully it's going to be included in the next version.