Multiple hasmany same keycolumn - illegal access to loading collection - nhibernate

it works. pull the data, but gives this error: illegal access to loading collection
public class Image : File
{
public virtual string ImagePath { get; set; }
}
public class Video : File
{
public virtual string VideoPath { get; set; }
public virtual string VideoType { get; set; }
}
public class Service : ContentBase
{
public virtual IList<Image> Images { get; set; }
public virtual IList<Video> Videos { get; set; }
}
public class ServiceMap:SubclassMap<Domain.Service>
{
public ServiceMap()
{
DiscriminatorValue("Service");
HasMany(x => x.Images).KeyColumn("ContentBase");
HasMany(x => x.Videos).KeyColumn("ContentBase");
}
}
public class ImageMap:SubclassMap<Image>
{
public ImageMap()
{
DiscriminatorValue("Image");
Map(x => x.ImagePath);
}
}
public class VideoMap:SubclassMap<Video>
{
public VideoMap()
{
DiscriminatorValue("Video");
Map(x => x.VideoPath);
}
}
it works. but it gives this error when I query. I think the same "keycolumn" gives this error to be. mapping'i How should I do?

Are you aware that joinalias wont eagerload the collections? that's what Fetch is for. Try this one instead
var service = UnitOfWork.CurrentSession.QueryOver<Service>()
.Fetch(x => x.Images).Eager
.Fetch(x => x.Videos).Eager
.Where(x => x.Id == serviceId)
.SingleOrDefault();
Update:
illegal access to loading collection could be thrown when
session is closed when accessing not initialized collection
mapping and collection type mismatch
Database doesn't match
have you inspected the sql generated to see if NH tries to access nonexistant columns or columns on the wrong table?

Related

NHibernate bi-directional association

I am trying to model a parent/child association where a Parent class (Person) owns many instances of a child class (OwnedThing) - I want the OwnedThing instances to be saved automatically when the Person class is saved, and I want the association to be bi-directional.
public class Person
{
public class MAP_Person : ClassMap<Person>
{
public MAP_Person()
{
this.Table("People");
this.Id(x => x.ID).GeneratedBy.GuidComb().Access.BackingField();
this.Map(x => x.FirstName);
this.HasMany(x => x.OwnedThings).Cascade.AllDeleteOrphan().KeyColumn("OwnerID").Inverse();
}
}
public virtual Guid ID { get; private set; }
public virtual string FirstName { get; set; }
public virtual IList<OwnedThing> OwnedThings { get; set; }
public Person()
{
OwnedThings = new List<OwnedThing>();
}
}
public class OwnedThing
{
public class MAP_OwnedThing : ClassMap<OwnedThing>
{
public MAP_OwnedThing()
{
this.Table("OwnedThings");
this.Id(x => x.ID).GeneratedBy.GuidComb().Access.BackingField();
this.Map(x => x.Name);
this.References(x => x.Owner).Column("OwnerID").Access.BackingField();
}
}
public virtual Guid ID { get; private set; }
public virtual Person Owner { get; private set; }
public virtual string Name { get; set; }
}
If I set Person.OwnedThings to Inverse then the OwnedThing instances are not saved when I save the Person. If I do not add Inverse then the save is successful but person.OwnedThings[0].Owner is always null after I retrieve it from the DB.
UPDATE
When saving the data NHibernate will set the single association end in the database because it is set via the many-end of the association, so when I retrieve the OwnedThing from the DB it does have the link back to the Person set. My null reference was from Envers which doesn't seem to do the same thing.
Am I understanding you correctly that your problem only occur on "history" entities read by nhibernate envers?
If so, it might be caused by this bug
https://nhibernate.jira.com/browse/NHE-64
The workaround for now is to use Merge instead of (SaveOr)Update.
OwnedThings[0].Owner is most likely null because you are not setting it when you do the add. When using bidirectional relationships you have to do something like the below:
Person person = new Person();
OwnedThing pwnedThing = new OwnedThing();
pwnedThing.Owner = person;
person.OwnedThings.Add(pwnedThing);
If you do not explicity set the pwnedThing.Owner and you query that same object in the same ISession that you created it on it will be null. Typically I have add or remove methods that do this "extra" work for me. Take the below example:
public class Order : Entity
{
private IList<OrderLine> orderLines;
public virtual IEnumerable<OrderLine> OrderLines { get { return orderLines.Select(x => x); } }
public virtual void AddLine(OrderLine orderLine)
{
orderLine.Order = this;
this.orderLines.Add(orderLine);
}
public virtual void RemoveLine(OrderLine orderLine)
{
this.orderLines.Remove(orderLine);
}
}
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
DynamicUpdate();
Table("ORDER_HEADER");
Id(x => x.Id, "ORDER_ID");
HasMany(x => x.OrderLines)
.Access.CamelCaseField()
.KeyColumn("ORDER_ID")
.Inverse()
.Cascade.AllDeleteOrphan();
}
}

Why doesn't this class hierarchy fluent mapping work?

What's wrong with my mapping shown below? Is this a problem with GeneratedBy.Foreign()? How should I use it cause my PK in UserTable(UID) is also the FK which refers to PersonTable PK(PID). I get the Duplicate class/entity mapping consoleMappingTest.SystemUser error. what do you suggest(be sure to look at database structure- no way to change it). thanks.
Inheritance structure:
public class Person
{
public virtual int ID { get; set; }
}
public class User:Person
{
public override int ID
{
get
{
return base.ID;
}
set
{
base.ID = value;
}
}
public virtual string Name { get; set; }
public virtual int Salary { get; set; }
}
public class SystemUser:User
{
public virtual int Password { get; set; }
}
Database structure:
for saving some info about person(some fields not shown here):
PersonTable(PID)
for saving User and all it's subclasses like system user:
UserTable(UID,Name,Salary,Type)
and here is my mapping:
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("PersonTable");
Id(x => x.ID, "PID").GeneratedBy.Assigned();//or HiLo-not important
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("UserTable");
DiscriminateSubClassesOnColumn("Type").Default("U");
Id(x => x.ID, "UID").GeneratedBy.Foreign("Person");//how should use this?
Map(x => x.Salary);
Join("PTable", j =>
{
j.KeyColumn("UID");
j.Map(x => x.Name);
});
}
}
public class SystemUserMap : SubclassMap<SystemUser>
{
public SystemUserMap()
{
DiscriminatorValue("SU");
Map(x => x.Password);
}
}
Foreign("") is meant to point to a Reference (Property with another mapped entity) from which the Id should be retrieved. You don't have a Reference to class Person named Person so you can't use it like this.
you already asked the same question with an answer. I know i didn't do it right first shot but would be nice if you told me what doesnt work with the latest edit or you dont like the solution befor asking the same question again

Fluent NHibernate one to many not saving children

I am using Fluent NHibernate. This is a classic case of a one to many relationship. I have one Supply parent with many SupplyAmount children.
The Supply parent object is saving with correct info, but the amounts are not getting inserted into the db when I save the parent. What am I doing for the cascade not to work?
The entities are as follows:
public class Supply : BaseEntity
{
public Guid SupplyId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Comments { get; set; }
public virtual IList<SupplyAmount> Amounts { get; set; }
public Supply()
{
Amounts = new List<SupplyAmount>();
}
public virtual void AddAmount(SupplyAmount amount)
{
amount.Supply = this;
Amounts.Add(amount);
}
}
public class SupplyAmount : BaseEntity
{
public virtual Guid SupplymountId { get; set; }
public virtual Supply Supply { get; set; }
public virtual int Amount { get; set; }
}
And the mapping as follows:
public class SupplyMap : ClassMap<Supply>
{
public SupplyMap()
{
Id(x => x.SupplyId);
Map(x => x.LastName);
Map(x => x.FirstName);
Map(x => x.Comments);
HasMany<SupplyAmount>(x => x.Amounts)
.Inverse().Cascade.SaveUpdate()
.KeyColumn("SupplyAmountId")
.AsBag();
}
}
public class SupplyAmountMap : ClassMap<SupplyAmount>
{
public SupplyAmountMap()
{
Id(x => x.SupplyAmountId);
References(x => x.Supply, "SupplyId").Cascade.SaveUpdate();
Map(x => x.Amount);
}
}
And this is how I call it:
public SaveIt()
{
Supply sOrder = Supply();
sOrder.FirstName = "TestFirst";
sOrder.LastName = "TestLast";
sOrder.Comments = "TestComments";
for (int i = 0; i < 5; i++)
{
SupplyAmount amount = new SupplyAmount();
amount.Amount = 50;
amount.Supply = sOrder;
sOrder.AddAmount(amount);
}
// This call saves the Supply to the Supply table but none of the Amounts
// to the SupplyAmount table.
AddSupplyOrder(sOrder);
}
I know this is an old post but why not...
// This call saves the Supply to the Supply table but none of the Amounts
This comment in SaveIt() indicates you call the save on the Supply and not the amounts.
In this case you have your logic the wrong way around.
So to fix this:
SupplyMap -> The Inverse shouldn't be there for Amounts.
HasMany<SupplyAmount>(x => x.Amounts).Cascade.SaveUpdate();
SupplyAmountMap ->
remove References(x => x.Supply, "SupplyId").Cascade.SaveUpdate();
Replace it with
References<Supply>(x=>x.Supply);
You should now be right to call the save on your supply object only and it will cascade down to the amounts.
Session.Save(supply);
In your test after you have arrange the supply and supplyamount make sure you call a
Session.Flush()
after your save to force it in.
This isn't as important in code as you will usually run in transactions before recalling the supply object.
Cheers,
Choco
Also as a side note it usually not a good idea to be to verbose with fluentmappings. let the default stuff do it thing which is why I would recommend against the column naming hints.

Fluent Nhibernate 3 Mapping Composite Field (Custom Type)

HI all, my scenario
public class Permission
{
public virtual Function Function { get; set; }
public virtual Profile Profile { get; set; }
}
public class MapPermission : ClassMap<Permission>
{
public MapPermission()
{
Table("Permissions".ToUpper());
CompositeId().KeyProperty(x => x.Function, "FunctionID").KeyProperty(x => x.Profile, "ProfileID");
}
}
Where Function AND Profile are two easy mapped entities. When i Run i have this error:
Could not determine type for: Data.Model.Entities.Function, Data.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns: NHibernate.Mapping.Column(FunctionID)"}
Is there a way to avoid this? ultimately i need to create a class with CompositeID made by two custom mapped classes. If i uses compositeID with int fields it works like a charm
Thanks in Advance
Function (Like Profile) Mapping
public class Function
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
public class MapFunction : ClassMap<Function>
{
public MapFunction()
{
Table("FUNCTIONS");
Id(x => x.ID);
Map(x => x.Name);
}
}
Use KeyReference instead of KeyProperty
public class MapPermission : ClassMap<Permission>
{
public MapPermission()
{
Table("Permissions".ToUpper());
CompositeId()
.KeyReference(x => x.Function, "FunctionID")
.KeyReference(x => x.Profile, "ProfileID");
}
}

How to restrict selection with QueryOver in (fluent) nhibernate?

I want to filter objects from the db by a property that comes from another object but i get an exception:
A first chance exception of type 'System.Collections.Generic.KeyNotFoundException' occurred in mscorlib.dll
A first chance exception of type 'NHibernate.QueryException' occurred in NHibernate.dll
A first chance exception of type 'NHibernate.QueryException' occurred in NHibernate.dll
The program '[5116] Examples.FirstProject.vshost.exe: Managed (v2.0.50727)' has exited with code -532459699 (0xe0434f4d).
This works:
var curves = session.QueryOver<Curve>().WhereRestrictionOn(p => p.Name).IsLike("%CurveName%").List();
foreach (Curve curve in curves)
{
Console.WriteLine(" ID:\t{0}\n Name:\t{1}\n Group:\t{2}\n", curve.Id, curve.Name, curve.Group.Name);
}
This not, it outputs the exception information:
var curves = session.QueryOver<Curve>().WhereRestrictionOn(p => p.Group.Name).IsLike("%GroupName%").List();
foreach (Curve curve in curves)
{
Console.WriteLine(" ID:\t{0}\n Name:\t{1}\n Group:\t{2}\n", curve.Id, curve.Name, curve.Group.Name);
}
These are my mappings:
public class CurveMap : ClassMap<Curve>
{
public CurveMap()
{
Table("CURVES");
Id(x => x.Id).Column("CURVE_ID");
Map(x => x.Name).Column("NAME");
References(x => x.Group).Column("GROUP_ID");
}
}
public class CurveGroupMap : ClassMap<CurveGroup>
{
public CurveGroupMap()
{
Table("GROUPS");
Id(x => x.Id).Column("GROUP_ID");
Map(x => x.Name).Column("NAME");
HasMany(x => x.Curves).KeyColumn("GROUP_ID").Cascade.All().Inverse();
}
}
And these are my objects
public class Curve
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual CurveGroup Group { get; set; }
}
public class CurveGroup
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Curve> Curves { get; set; }
}
Any idea, how to fix this. I am new to (fluent) nhibernate.
If you join CurveGroup and use Aliases it will work:
CurveGroup cgAlias = null;
var curves = session.QueryOver<Curve>()
.JoinAlias(e => e.Group, () => cgAlias)
.WhereRestrictionOn(() => cgAlias.Name).IsLike("%GroupName%").List();