Problem with Fluent nhibernate - fluent-nhibernate

i have written a code like this
public class person
{
public person()
{
public virtual int Id { get; set; }
public virtual string Code { get; set; }
}
}
public class Child : Person
{
public person()
{
public virtual string Name{ get; set; }
public virtual string Lastname{ get; set; }
}
}
public class Book
{
public virtual int Id { get; set; }
public virtual string Name {get;set;}
}
and my Mapper classes is like these
public class PersonMapping : ClassMap<Person>
{
public PersonMapping()
{
Table("tblPersons");
Id(x => x.Id).GeneratedBy.Native();
Map(p => p.Code);
JoinedSubClass<Child>("Id", MapChild);
}
public static void MapChild(JoinedSubClassPart<Child> child)
{
child.Table("tblChilds");
child.Map(p => p.Name);
child.Map(p => p.Lastname);
child.HasMany(x => x.Relatives);
}
}
public class RelativeMapping : ClassMap<Relative>
{
public RelativeMapping()
{
Table("tblRelatives");
Id(x => x.Id);
Map(p => p.Name);
References(x => x.Child).Column("ChildId");
}
}
this is Configuration
Assembly assm = Assembly.Load("BLL");
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c
.FromAppSetting("ConnStr"))
.Cache(c => c
.UseQueryCache()
.ProviderClass<HashtableCacheProvider>())
.ShowSql())
.Mappings(m => m.FluentMappings.AddFromAssembly(assm))
.BuildSessionFactory();
and this is the code of delete
public void Delete<T>(T obj)
{
ISessionFactory fact = FluentConfiguration.CreateSessionFactory();
ISession session = fact.OpenSession();
session.Delete(obj);
session.Flush();
}
my problem : when i wanna delete a Child it messages
" ILLEGAL ATTEMPT TO ASSOCIATE WITH TWO OPEN SESSION "
please help me

The error tells you the problem, and it is not with your entities or your mapping.
You have two or more open sessions and you're attempting to associate some entity with more than one of them.
update
In response to the updated code, I see that you have a method that accepts an entity as a parameter, creates a new session factory, creates a new session, and then tries to delete the entity.
There are some problems here:
You should only create the session factory once. Ever. This is an expensive operation.
You are passing the entity to the Delete() method. Where is this entity coming from? You've clearly already loaded it elsewhere in your application, using a different ISession. This is the crux of the problem. Unless you Evict() the entity from the first ISession (not recommended), trying to manipulate it with a different ISession will throw.
You're calling Flush() which should almost never be used.
You're using an implicit transaction.
You should really be deleting the entity with the same ISession with which it was loaded, and you should be performing work within a transaction, like this:
using(var transaction = session.BeginTransaction())
{
session.Delete(obj);
transaction.Commit();
}

Related

NHibernate - Unable to save child entities

This is supposed to be a simple 1-N relationship, however I am not able to save the children as NHibernate uses null as a value in the insert statement of the child.
public class Event
{
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<EventParameter> Parameters{ get; set; }
public Event() { Parameters = new List<EventParameter>(); }
}
[Serializable]
public class EventParameter : Parameter
{
public virtual Event Event { get; set; }
public virtual string ComparisonMethod { get; set; }
public override bool Equals(object obj) {}
public override int GetHashCode() {}
}
The mappings are like this
public EventMapping()
{
Table(...);
Id(x => x.Id)
.Column(...)
.GeneratedBy
.Custom<global::NHibernate.Id.SequenceGenerator>(builder => builder.AddParam("sequence", "..."));
Map(x => x.Name).Column("Name").Length(100);
HasMany<EventParameter>(x => x.Parameters)
.KeyColumns.Add(...)
.Inverse()
.Cascade.All();
}
public EventParameterMapping()
{
Table(....);
CompositeId()
.KeyProperty(x => x.Event.Id, "...")
.KeyProperty(x => x.ParameterId, "...");
References(x => x.Event);
}
The Insert statement for the parent is correct, however for the child it is not.
INSERT INTO ...
(...columns...)
VALUES (..., null, ...)
Before this happens I get the following warning: Unable to determine if entity is transient or detached; querying the database. Use explicit Save() or Update() in session to prevent this.
I do use Save() in the transaction. Any ideas why this happens?
Solution here is suprisingly simply. We have to use inverse="true" mapping:
HasMany<EventParameter>(x => x.Children)
.KeyColumns.Add("...")
.Inverse()
.Cascade.All();
But, this is kind of optimization, which would require both sides of relation to be always properly set in our C# code:
var parent = ...;
var child = new Child();
// both sides MUST be set
parent.Children.Add(child);
child.Parent = parent;
Please, observe this for more details
Inverse = “true” example and explanation

Fluent Nhibernate Enum Mapping

I have some problem with enum mapping in fluent NHibernate. I know this question has been asked many times but I couldn't find any solution that worked for me. I'm newbie in NHibernate and it looks like I may have missed something simple and stupid. Here is my code.
public class DBPublication
{
public virtual int pub_id { get; set; }
public virtual PublicationStatuses status { get; set; }
...
}
public enum PublicationStatuses
{
on_moderation,
active,
...
}
public class DBPublicationMap : ClassMap<DBPublication>
{
public DBPublicationMap()
{
Table("content.publications");
Id(x => x.pub_id).GeneratedBy.Sequence("content.pub_sq");
Map(x => x.status);
...
}
}
postgres enum type
CREATE TYPE content.enum_publication_status AS ENUM('on_moderation', 'active', ...);
but when I try to save, postgres throws this
column "status" is of type content.enum_publication_status but expression is of type text
any suggestion?
Here is a working sample of configuring nhibernate to store enum field.
public class Entity
{
public virtual int id { get; set; }
public virtual SomeEnum EnumField { get; set; }
}
public enum SomeEnum
{
Value1,
Value2
}
class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Id(x => x.id).GeneratedBy.Native();
Map(x => x.EnumField);
}
}
class Program
{
static void Main(string[] args)
{
var factory = Fluently.Configure().Mappings(x => x.FluentMappings.AddFromAssemblyOf<Entity>())
.ExposeConfiguration(config => new SchemaExport(config).Create(false, true))
.Database(MsSqlConfiguration.MsSql2008.ConnectionString("Data Source=.;Initial Catalog=nhtest;Integrated Security=True"))
.BuildSessionFactory();
using (var session = factory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var entity = new Entity();
entity.EnumField = SomeEnum.Value2;
session.Save(entity);
transaction.Commit();
}
}
}
}
In such case it is stored as strings in data base. If you want it be saved as integers, you need to change mapping for Enum field property to the following:
Map(x => x.EnumField).CustomType<int>();
Why are you creating enum types in progres? isn't this a maintenance-nightmare?
What does your configuration look like? Have you already tried using the conventions discribed here?
Looks like a simple casting error, so please consider using CustomType() / CustomSQLType() inside your mapping.
Simply add this class to your project:
public class PgEnumMapper<T> : NHibernate.Type.EnumStringType<T>
{
public override NHibernate.SqlTypes.SqlType SqlType
{
get { return new NHibernate.SqlTypes.SqlType(System.Data.DbType.Object); }
}
}
Then, you can use:
Map(x => x.status).CustomType<PgEnumMapper<PublicationStatuses>>();

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();
}
}

"No persister for" using Fluently mapping of Subclass in Fluent NHibernate

I'm doing a very basic thing with Fluent NHibernate. I found a lot of people with similar problems here in SO but none seemed to fix my problem.
I have 1 Class like:
public abstract class ParentClass
{
public virtual long Id { get; private set; }
public virtual DateTime CreateDate { get; set; }
public virtual int Type { get; set; }
}
And 1 Concrete classes like:
public class ChildClass : ParentClass
{
public virtual string PropertyX { get; set; }
public virtual int PropertyY{ get; set; }
}
So I made the mapping as follows:
public class ParentMap : ClassMap<ParentClass>
{
public ParentMap()
{
Id(x => x.Id);
Map(x => x.CreateDate);
DiscriminateSubClassesOnColumn("Type");
}
}
And
public class ChildMap : SubclassMap<ChildClass>
{
public ChildMap()
{
Extends<ParentClass>();
DiscriminatorValue(1);
Map(x => x.PropertyX);
Map(x => x.PropertyY);
}
}
My legacy database has 2 tables, 1 that holds all the data from the ParentClass and another one that holds the data from the Child with a foreign key in the ID.
The idea is to have different tables for each different implementation of the ParentClass but having the ParentClass table as a single repository for "Ids" and "Create Dates".
I'm creating my SessionFactory as follows:
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlCeConfiguration.Standard.ConnectionString(cstr => cstr.FromConnectionStringWithKey("TheConnectionString")))
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentClass>()
.ExportTo(#"c:\temp\Mappings"))
.BuildSessionFactory();
}
I'm doing just a basic test of storing things to the database as:
public void Store(ParentClass parent)
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(parent);
transaction.Commit();
}
}
}
But despite the method waits for a ParentClass variable, I'm passing a ChildClass instance for it (the method is actually a inheritance of an interface, that's why it expects a ParentClass).
And every time I it raises an error on "SaveOrUpdate" method saying "No persister for: ChildClass".
What am I doing wrong?
ps.: Another strange thing is that even with the "ExportTo" method on the SessionFactory creation, no mapping is being write on the folder.
Change
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentClass>()
To
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentMap>()

NHibernate query is not returning any results

I'm trying to get Fluent and NHibernate configured properly with ASP.NET MVC. As far as I know it's configured properly but when I access the page that uses this setup I am not receiving any data results.
The model I'm using is called Brand and the database table is Brands.
Here is a snippet from my BrandController:
public ActionResult Index()
{
IRepository<Brand> repo = new BrandRepository();
var brands = repo.GetAll().ToList<Brand>();
return View(brands);
}
Here is a snippet from my BrandRepository:
ICollection<Brand> IRepository<Brand>.GetAll()
{
using (ISession session = NHibernateHelper.OpenSession())
{
var brands = session
.CreateCriteria(typeof(Brand))
.List<Brand>();
return brands;
}
}
Here is my NHibernateHelper:
public class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2008
.ConnectionString(c => c
.FromConnectionStringWithKey("ShoesFullAccess")
)
)
.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
Here is my Brand model:
public class Brand
{
public int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Style> Styles { get; private set; }
public Brand()
{
Styles = new List<Style>();
}
public virtual void AddStyle(Style style)
{
Styles.Add(style);
}
}
And finally, here is my BrandMap:
public class BrandMap : ClassMap<Brand>
{
public BrandMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Styles)
.Inverse()
.Cascade.All();
}
}
If anyone could point me in the right direction for how I can narrow down the problem I would very grateful!
You are not adding the mappings to the configuration.
Add this before .BuildSessionFactory():
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<BrandMap>())
I needed to make a few modifications to my code. The first and main one was adding the mappings to the configuration as Diego pointed out.
You are not adding the mappings to the configuration.
Add this before .BuildSessionFactory():
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<BrandMap>())
The next thing I had to fix was my Brand model. I needed to make the Id field virtual.
And finally, I needed to change my BrandMap just a little bit -- I had to specify exactly which table it was pointing to by adding this code Table("Brands");