nhibernate mapping: A collection with cascade="all-delete-orphan" was no longer referenced - nhibernate

I am having some probs with my fluent mappings. I have an entity with a child collection of entities i.e Event and EventItems for example.
If I set my cascade mapping of the collection to AllDeleteOrphan I get the following error when saving a new entity to the DB:
NHibernate.HibernateException : A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: Core.Event.EventItems
If I set the cascade to All it works fine? Below are my classes and mapping files:
public class EventMap : ClassMap<Event>
{
public EventMap()
{
Id(x => x.Id, "Id")
.UnsavedValue("00000000-0000-0000-0000-000000000000")
.GeneratedBy.GuidComb();
Map(x => x.Name);
HasMany(x => x.EventItems)
.Inverse()
.KeyColumn("EventId")
.AsBag()
.Cascade.AllDeleteOrphan();
}
}
public class EventItemMap : SubclassMap<EventItem>
{
public EventItemMap()
{
Id(x => x.Id, "Id")
.UnsavedValue("00000000-0000-0000-0000-000000000000")
.GeneratedBy.GuidComb();
References(x => x.Event, "EventId");
}
}
public class Event : EntityBase
{
private IList<EventItem> _EventItems;
protected Event()
{
InitMembers();
}
public Event(string name)
: this()
{
Name = name;
}
private void InitMembers()
{
_EventItems = new List<EventItem>();
}
public virtual EventItem CreateEventItem(string name)
{
EventItem eventItem = new EventItem(this, name);
_EventItems.Add(eventItem);
return eventItem;
}
public virtual string Name { get; private set; }
public virtual IList<EventItem> EventItems
{
get
{
return _EventItems.ToList<EventItem>().AsReadOnly();
}
protected set
{
_EventItems = value;
}
}
}
public class EventItem : EntityBase
{
protected EventItem()
{
}
public EventItem(Event #event, string name):base(name)
{
Event = #event;
}
public virtual Event Event { get; private set; }
}
Pretty stumped here. Any tips greatly appreciated.
Chev

You need to map _EventItems using an access strategy so that NHibernate access the private member instead of the property. You're getting this error because the collection reference is changed when the list is copied to a new List in _EventItems.ToList<EventItem>(). Try this:
public class EventMap : ClassMap<Event>
{
public EventMap()
{
Id(x => x.Id, "Id")
.UnsavedValue("00000000-0000-0000-0000-000000000000")
.GeneratedBy.GuidComb();
Map(x => x.Name);
HasMany(x => x.EventItems)
.Access.PascalCaseField(Prefix.Underscore)
.Inverse()
.KeyColumn("EventId")
.AsBag()
.Cascade.AllDeleteOrphan();
}
}
}

I don't think the accepted answer is an elegant approach. The possible problem here is that Chev is reading Events from the database and then assigning a new EventItem list to the EventItems property. NHibernate throws this exception when you just ignore the previous children list and assign a new children list.
What you need to do here is,
If you want to discard the old EventItems, do this instead:
events.EventItems.Clear();
events.EventItems.Add(new EventItem { blah blah });

Check this SO post: NHibernate: Delete a child record from the parent collection
The comments to the accepted answer has similar issue.
You may want to try removing AsReadOnly for your EventItems to check if that's the cause.

Related

Table-per-subclass fluent nhibernate not working

I have the following classes defined:
And these tables in my database:
My fluent NHibernate mappings are:
public class BusinessUnitMap : ClassMap<BusinessUnit>
{
public BusinessUnitMap()
{
Table("BusinessUnits");
Id(x => x.Id);
Map(x => x.Code);
Map(x => x.Name);
Map(x => x.ParentId);
Map(x => x.Type).Column("Type").CustomType<BusinessUnitType>();
}
}
public class CompanyMap : SubclassMap<Company>
{
public CompanyMap()
{
Table("CompanyData");
KeyColumn("BusinessUnitID");
Map(x => x.Something);
}
}
public class FranchiseeMap : SubclassMap<Franchisee>
{
public FranchiseeMap()
{
Table("FranchiseeData");
KeyColumn("BusinessUnitID");
Map(x => x.SomethingDifferent);
}
}
public class StoreMap : SubclassMap<Store>
{
public StoreMap()
{
Table("StoreData");
KeyColumn("BusinessUnitID");
Map(x => x.SomethingElse);
}
}
Question #1
As far as I can tell, my code and database are setup the same as every example I've been able to find. According to those articles, NHibernate is supposed to be smart enough to determine what subclass to instantiate when I query for a particular subclass. But, when I execute the following statement:
var result = Session.QueryOver<BusinessUnit>()
.Where(x => x.Code == "Acme")
.SingleOrDefault();
an exception is thrown because it can't create an instance of the abstract BusinessUnit class. The only way I can get this to work is to specify Company as the type argument for QueryOver.
I've confirmed that using a discriminator breaks since NHibernate is looking for all of the columns to exist in a single table. Without it, though, I struggle to see how NHibernate would know what type to instantiate.
What am I doing wrong? Is the problem in my mappings, the way I'm querying, ...?
Question #2
When I change the query to something like this:
public T WithCode<T>(String code)
where T : BusinessUnit
{
var result = Session.QueryOver<T>()
.Where(x => x.Code == code)
.SingleOrDefault();
return result;
}
I get an exception indicating that the UPDATE statement conflicts with a foreign key constraint. Update statement!!!! Clearly something is still not right. How can a QueryOver call result in an UPDATE statement? What am I missing?
it looks like your data is not consistent. It might be better to use discrimnator mapping with optional. If you dont really need a BusinessUnitType property in code then just delete everything around the property Type
public enum BusinessUnitType
{
Company,
Franchisee
}
public abstract class BusinessUnit
{
public virtual int Id { get; set; }
public virtual string Code { get; set; }
public virtual string Name { get; set; }
public virtual BusinessUnit Parent { get; set; }
public abstract BusinessUnitType Type { get; }
}
public class Company : BusinessUnit
{
public virtual string Something { get; set; }
public override BusinessUnitType Type { get { return BusinessUnitType.Company; } }
}
public class Franchisee : BusinessUnit
{
public virtual string SomethingDifferent { get; set; }
public override BusinessUnitType Type { get { return BusinessUnitType.Franchisee; } }
}
public class BusinessUnitMap : ClassMap<BusinessUnit>
{
public BusinessUnitMap()
{
Table("BusinessUnits");
Id(x => x.Id);
Map(x => x.Code);
Map(x => x.Name);
References(x => x.Parent);
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Type, "Type")
.Access.None()
.CustomType<BusinessUnitType>().ReadOnly();
}
}
public class CompanyMap : SubclassMap<StrangeTablePerSubclass.Company>
{
public CompanyMap()
{
DiscriminatorValue((int)new Company().Type);
Join("CompanyData", join =>
{
join.KeyColumn("BusinessUnitID");
join.Optional();
join.Map(x => x.Something);
});
}
}
public class FranchiseeMap : SubclassMap<Franchisee>
{
public FranchiseeMap()
{
DiscriminatorValue((int)new Franchisee().Type);
Join("FranchiseeData", join =>
{
join.KeyColumn("BusinessUnitID");
join.Optional();
join.Map(x => x.SomethingDifferent);
});
}
}

Fluent NHibernate without Automapping SQL Exception, Incorrect syntax near the keyword 'User'

Just starting in NHibernate and to my eye Everything seems correct but obviously is not. When I ren unit tests shown below I receive back that there is a syntax error near the keyword "User"
here is my user class:
namespace Users
{
public class User
{
public virtual string firstName { get; set; }
public virtual string lastName { get; set; }
public virtual int Id { get; set; }
}
}
and the User mapping(Also ran without square brackets around column names with identical results:
namespace Users
{
class UserMap: ClassMap<User>
{
UserMap()
{
Table("User");
Id(x => x.Id).GeneratedBy.Native().Column("[Id]").Not.Nullable();
Map(x => x.firstName).Not.Nullable().Column("[firstName]");
Map(x => x.lastName).Not.Nullable().Column("[lastName]");
}
}
}
The Config file named Framework.cs
namespace Users
{
public class Framework
{
private const string ConnectionStr = "Data Source=ERALCH-ESTEPHEN;Initial
Catalog=NHDev;Integrated Security=True;Pooling=False";
public static ISessionFactory CreateFactory()
{
return Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration
.MsSql2008
.ConnectionString(ConnectionStr))
.Mappings(x=>x.FluentMappings.AddFromAssemblyOf<User>())
.BuildSessionFactory();
}
}
}
The Data Access Layer-- simply retrieves a user by Id
namespace Users
{
public class Accesslayer
{
public static IList<User> GetUserById(int Id)
{
ISessionFactory provider = Framework.CreateFactory();
using (ISession session = provider.OpenSession())
{
return session.CreateSQLQuery(String
.Format("SELECT * FROM User WHERE Id={0}", Id)).List<User>();
}
}
}
}
and finally the unit test layer
namespace Users
{
[TestFixture]
class AccessLayerTest
{
[Test]
public void CanGetUserById()
{
Assert.AreEqual(1, Accesslayer.GetUserById(1).Count());
}
}
}
The Database is MSsql with one table "User" with columns matching the user properties. Any help would be appreciated thanks
You should be able to do this in your configuration:
var factory = Fluently.Configure()
// ...
.ExposeConfiguration(c => SchemaMetadataUpdater.QuoteTableAndColumns(c))
.BuildSessionFactory();
Which should escape your table and column names for you automagically.
Did you try putting backticks around your User table name ?
namespace Users
{
class UserMap: ClassMap<User>
{
UserMap()
{
Table("`User`");
Id(x => x.Id).GeneratedBy.Native().Column("`Id`").Not.Nullable();
Map(x => x.firstName).Not.Nullable().Column("`firstName`");
Map(x => x.lastName).Not.Nullable().Column("`lastName`");
}
}
}
See this answer for more details : NHibernate - Force escaping on Table Names
Also, you should use NHibernate querying facilities instead of SQL :
namespace Users
{
public class Accesslayer
{
public static IList<User> GetUserById(int Id)
{
ISessionFactory provider = Framework.CreateFactory();
using (ISession session = provider.OpenSession())
{
return session.Query<User>().Where(x => x.Id == Id ).List<User>();
}
}
}
}
Have a look at this tutorial : http://www.d80.co.uk/post/2011/02/20/Linq-to-NHibernate-Tutorial.aspx
You should need the [Brackets] while mapping. Heck, if they have the same name, both of these would work the same:
public class UserMap: ClassMap<User>
{
UserMap()
{
Table("User");
Id(x => x.Id).GeneratedBy.Native().Not.Nullable();
Map(x => x.firstName).Not.Nullable();
Map(x => x.lastName).Not.Nullable();
}
}
public class UserMap: ClassMap<User>
{
UserMap()
{
Table("User");
Id(x => x.Id, "Id").GeneratedBy.Native().Not.Nullable();
Map(x => x.firstName, "firstName").Not.Nullable();
Map(x => x.lastName, "lastName").Not.Nullable();
}
}
Avoid naming your tables or columns using Reserved Keywords. Hibernate forms a SQL-Statement which won't be accepted by MS SQL. Had the same problem with NHibernate + Fluent + Automapper and solved it by renaming the "user"-column to "username". Don't know how other DBs handle it.
Further comments about this here.

Fluent NHibernate: How to Map List<String>

I know there are lots of questions about how to map List of strings using fluent nHibernate. I tried all the options I got. But still no luck.
My situation is as follows.
class BaseClass
{
public string Name {get;set;}
}
class FirstChild : BaseClass
{
public string Parameter{get;set;}
public IList<string> OtherParameter {get;set;}
}
The mapping file I am using is as follows:
public class BaseClassMap: ClassMap<BaseClass>
{
public BaseClassMap()
{
Table("BaseClass");
Map(x => x.Name);
DiscriminateSubClassesOnColumn<string>("Class");
}
}
public class FirstChildMap : SubclassMap<FirstChild>
{
public FirstChildMap ()
{
Map(x => x.Parameter);
HasMany(x => x.OtherParameter)
.Element("OtherParameter ")
.Table("OtherParameterTable").Cascade.AllDeleteOrphan();
}
}
After that when I try to save an object of type FirstChild, it saves the value of Name and Parameter but nothing goes into OtherParameterTable.
Can someone tell me where I am going wrong?

fluent nhibernate component one-to-many

I have couple of classes and want to map them correctly to database:
public class A
{
public virtual Guid Id { get; private set; }
public virtual ComponentClass Component { get; set; }
}
public class ComponentClass
{
public virtual IList<B> Elements { get;set; }
}
public class B
{
public virtual Guid Id { get; private set; }
public virtual DateTime Time { get; set; }
}
I map them using fluent mappings like that:
public class AMap : ClassMap<A>
{
public A() {
Id(x => x.Id);
Component(x => x.Component,
c => c.HasMany(x => x.Elements).Inverse().Cascade.All());
}
}
public class BMap : ClassMap<B>
{
public B() {
Id(x => x.Id);
Map(x => x.Time);
}
}
When I save my entity, I have class A mapped to one table and class B to another as expected.
But I have nulls in Component_id column.
Can you tell me what am I missing here?
I believe Components are supposed to be in the same table , as clearly stated in Ayende's blog post, as they serve only to make the data better represented as an object model. Be sure to read through his blog, it's probably one of the best nHibernate resources out there.
Ok, I've resolved my problem - I can use Id of my "parent" class. So the component mapping will become:
public class AMap : ClassMap<A>
{
public A() {
Id(x => x.Id);
Component(x => x.Component,
c => c.HasMany(x => x.Elements).Cascade.All().Column("Id"));
}
}
So obvious as I look at it now ... but It took me an hour.
If you have a one-to-many association direct to a collection of components (ie. without the ComponentClass wrapper as per the question) then you can map it directly:
HasMany(x => x.Elements)
.AsSet()
.Table("ElementTable")
.KeyColumn("KeyColumn")
.Cascade.All()
.Component(x =>
{
x.Map(c => c.Id);
x.Map(c => c.Time);
})
.LazyLoad();

FluentNHibernate IDctionary with manytomany

I have a mapping structured in this way:
public class Person
{
public IDictionary<bool, Action> Actions { get; set; }
}
public class Action
{
public string Name { get; set; }
}
// Map for Person
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.Id) ...
Map(x => x.Name) ...
Table("Persons")
}
}
// Map for Action
public class ActionMap : ActionMap<Action>
{
public ActionMap()
{
Id(x => x.Id) ...
Map(x => x.Name) ...
Table("Actions")
}
}
What I need to do now is this.
I need a third table that will contains this fields:
PersonId
ActionId
True/false
Because I have the collection of actions inside the class person i was thinking about using a manytomany, but I can't find documentation on how to map an IDictionary.
Any idea? Wrong approach?
Dunno if you've already found the solution but there's been a recent update(only a couple weeks ago) where you could simply map it as HasManyToMany(x => x.NameOfDictionary) so you might want to update your FNH.
I haven't tried it yet though but here's the link: mailing list post