Inserting a parent entity with existing child in Fluent NHibernate - c#

This is a general question, I'm sure it's quite common, however I haven't found anything on it (or I don't know what to search for I guess).
I'm having the following entities in my project:
public class User
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Unit Unit { get; set; }
}
public class Unit
{
public virtual int Id { get; set;}
public virtual string Name { get; set;}
}
This is how I've done the Fluent NHibernate mappings:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id).GeneratedBy.Identity().Column("UserId");
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Unit).Column("UnitId");
}
}
public class UnitMap : ClassMap<Unit>
{
public UnitMap()
{
Table("Unit");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("UnitId");
Map(x => x.Name).Column("Name").Not.Nullable();
HasMany(x => x.Users).KeyColumn("UnitId");
}
}
Now here is my question. How do I create a new User if I only have the user's unit Id not a full unit object, and the unit already exists in the database (created previously) ?
Something like this:
public class TestClass
{
// Adding a user to a unit example
public void SavingAUser(int unitId)
{
var user = new User
{
FirstName = "TestFirstName",
LastName = "TestLastName",
Unit = new Unit() // <-- I have only the Id of the unit I don't actually have a unit object here I don't want to query the DB to get the full object, I already have the Id
};
var userRepository = new UserRepository();
userRepository.Save(user);
}
}
How would I go about something like this. I hope I'm making sense, if not please let me know I'll throw in more clarifications. I'm also pretty certain that this is a very common scenario

You can return a proxy to the unit without fetching it.
var user = new User
{
FirstName = "TestFirstName",
LastName = "TestLastName",
Unit = Session.Load<Unit>(unitId)
}
You'll need to expose the session object.

Related

One-Many via NHibernate Loquacious mapping by code or xml

I want to implement a one-to-many relationship between a person and car, and have CRUD operations on both person and car. Brief CRUD and relationships:
Update
A person has many cars
CRUD operations on both person and car via person object.
Deleting a person will delete all s/his cars
Ability to perform CRUD operation on someone's cars, either via person object or car object.
Is it possible via ORM, in particular NHibernate?
Classes like below:
public class PersonSet
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<CarSet> Cars { get; set; }
}
public class CarSet
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual PersonSet Person { get; set; }
}
Mapping is shown below:
public class PersonSetMap : ClassMapping<PersonSet>
{
public PersonSetMap()
{
Id(x => x.Id, m=>m.Generator(Generators.Identity));
Property(x=>x.Name);
Set(x => x.Cars, c =>
{
//c.Key(k =>
// {
// k.Column("PersonId");
// });
c.Cascade(Cascade.All);
c.Lazy(CollectionLazy.NoLazy);
// c.Inverse(true);
}
, r =>
{
r.OneToMany();
}
);
}
}
public class CarSetMap : ClassMapping<CarSet>
{
public CarSetMap()
{
Id(x => x.Id, m => m.Generator(Generators.Identity));
Property(x => x.Name);
ManyToOne(x => x.Person, m =>
{
m.Column("PersonId");
m.Cascade(Cascade.None);
m.NotNullable(true);
});
}
}
The problem I have is that if I update one car and try to save it on a person object, it doesn't change.
Update
I want to find out if it is possible, and where my mapping above is wrong. Any idea on either xml version or Loquacious would also be appreciated.
There should be a PersonId foreign key on table Car.
I don't know if this would solve your problem, but in a ManyToOne mapping the Unique and NotNullable methods should be applied at the column level.
ManyToOne(x => x.Person, m =>
{
m.Column(c =>
{
c.Name("PersonId");
c.NotNullable(true);
});
m.Cascade(Cascade.None);
});

Fluent nhibernate one-to-many

I have a one-to-many relationship and cannot get cascade to work, once I set cascade I just get "object references an unsaved transient instance ...".
My mapping looks like this
public class SharedDetailsMapping : ClassMap<SharedDetails>
{
public SharedDetailsMapping()
{
Id(x => x.Id).GeneratedBy.Identity();
HasMany(x => x.Foos);
}
}
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.SharedDetails).Cascade.SaveUpdate();
}
}
The classes like this
public class Foo
{
public Foo()
{
SharedDetails = new SharedDetails();
SharedDetails.Foos.Add(this);
}
public Foo(SharedDetails sharedDetails)
{
SharedDetails = sharedDetails;
SharedDetails.Foos.Add(this);
}
public virtual Guid Id { get; set; }
public virtual SharedDetails SharedDetails { get; set; }
}
public class SharedDetails
{
public SharedDetails()
{
Foos = new List<Foo>();
}
public virtual Guid Id { get; set; }
public virtual IList<Foo> Foos { get; set; }
}
I then want to create Foos without having to save the SharedDetails first if its a new Foo, like so:
using (var transaction = _session.BeginTransaction())
{
var shared = new SharedDetails();
var fooOne = new Foo(shared);
_session.SaveOrUpdate(fooOne);
var fooTwo = new Foo(shared);
_session.SaveOrUpdate(fooTwo);
transaction.Commit();
}
Cannot figure out what I have done wrong, it works ofcurse if I save the SharedDetails first but that is why I have Cascade setup.
In your SharedDetailsMapping, modify your HasMany to add .Inverse():
public class SharedDetailsMapping : ClassMap<SharedDetails>
{
public SharedDetailsMapping()
{
Id(x => x.Id).GeneratedBy.Identity();
HasMany(x => x.Foos)
.Inverse();
}
}
This instructs NHibernate that Foo owns the relationship, which will help it save objects in the relationship in the right order (in this case, the SharedDetails has to get saved first so we have its ID when Foo is saved).
Further info on the purpose/when to use Inverse: NHibernate's inverse - what does it really mean?
The TL;DNR version:
If you have a bidirectional relationship in your classes (HasMany on one side, References on the other), the HasMany should have .Inverse().

NHibernate References Dont' save Foreign Key

I have 2 classes that reference each other. It's a weird situation that our CRM needs.
I have an Organization and EmAddress tables and classes. The Organization inherits from Subscriber, which also has a table. I think this could be my problem, or the fact that I can't set Inverse on these because there is no "HasMany"...
The order of insert/update is ..
INSERT Email
INSERT Org
UPDATE Email to set Email.Subscriber
Email.Subscriber needs to be "NOT NULL", so this doesn't work. How can I change the order, I can't use Inverse because there is no list. Just 2 references.
public class Organization : Subscriber
{
public override string Class { get { return "Organization"; } }
EmAddress PrimaryEmailAddress {get;set;}
}
public class OrganizationMap : SubclassMap<Organization>
{
public OrganizationMap()
{
Table("Organization");
KeyColumn("Organization");
References(x => x.PrimaryEmail,"PrimaryEmailAddress").Cascade.SaveUpdate();
}
}
public EmAddressMap()
{
Id(x => x.Id, "EmAddress");
Map(x => x.EmailAddress, "eMailAddress");
References<Subscriber>(x => x.Subscriber,"Subscriber").LazyLoad().Fetch.Select().Not.Nullable();
/*not.nullable() throw s error. NHibernate INSERTS email, INSERTS org, UPDATES email. */
}
public class EmAddress
{
public virtual Guid Id { get; set; }
public virtual string EmailAddress { get; set; }
public virtual Subscriber Subscriber { get; set; }
}
//Implementation
var session = NHIbernateHelper.GetSession();
using(var tx = session.BeginTransaction())
{
var org = new Organization();
org.PrimaryEmail = new EmAddress(){Subscriber = org};
session.Save(org);
tx.commit();
}
This post might help:
http://ayende.com/blog/3960/nhibernate-mapping-one-to-one
Have only one side use many-to-one (Fluent: "References") and the other side uses one-to-one (Fluent: "HasOne").

Lost inheritance in NHibernate

I made conversion from EF4 to nHibernate and now have little problem with inheritence.
My entities and mappings:
public class User
{
public virtual int Id { get; set; }
public virtual string UserName { get; set; }
}
public class Account
{
public virtual int Id { get; set; }
public virtual User User { get; set; }
}
public class Member : User
{
public virtual string SpecialPropForMember { get; set; }
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
Map(x => x.UserName);
}
}
public class AccountMap : ClassMap<Account>
{
public AccountMap()
{
Id(x => x.Id);
References(x => x.User);
}
}
public class MemberMap : SubclassMap<Member>
{
public MemberMap()
{
Map(x => x.SpecialPropForMember);
}
}
My passed test:
[Test]
public void TestMemberUserInheritence()
{
User newUser = new User()
{
UserName = RandomValues.String()
};
Member newMember = new Member()
{
SpecialPropForMember = "special"
};
Account newAccount = new Account()
{
User = newMember
};
Member member = account.User as Member;
Assert.IsNotNull(member);
}
and failed test:
[Test]
public void TestMemberUserInheritenceFromNHibernate()
{
User newUser = new User()
{
UserName = RandomValues.String()
};
UsersService().AddUser(newUser);
Member newMember = new Member()
{
SpecialPropForMember = "special"
};
MemberService().Add(newMember);
Account newAccount = new Account()
{
User = newMember
};
AccountService().Add(newAccount);
Account account; ;
using (var session = DataAccess.OpenSession())
{
account = session.Linq<Account>().First();
}
Member member = account.User as Member;
Assert.IsNotNull(member);
}
Could someone explain me why NH don't resolve properly inheritance ?
The same issue is for table per class and table per hierarchy.
It's because of the way that NHibernate provides lazy loading. Value of account.User in your failing test is actually a proxy object. This means, that when you're loading account, the user is not fetched from DB until you access any of it's properties (unless you state so explicitly in your query). This means, that when NHibernate creates the proxy object, it does not know what is actual type of this object, and it creates proxy object that derives directly from User class. I've gone into more details in my anwser to this question: Force lazy entity to load real instance.
There is a mapping option "lazy = no-proxy" in NHibernate for the property that should fix the problem. I am not sure if it is available in Fluent NHibernate.
Read this blog post for more details
http://ayende.com/blog/4378/nhibernate-new-feature-no-proxy-associations

What are some reasons NHibernate will not execute SQL?

I am using Fluent NHibernate to map entities and am having a problem getting a repository to give a resultset. In the console, the SQL does not show but other repositories do. I have a feeling that it is because of the Mappings but can't tell why. The table name includes an underscore which is one of the only differences between this repo and others. My question is what could cause the sql not to be executed?
Here is my setup.
Entity:
public class Org
{
public virtual int ID { get; set; }
public virtual string IndividualName { get; set; }
public virtual string GroupName { get; set; }
public virtual string AddressLine1 { get; set; }
public virtual string AddressLine2 { get; set; }
}
Mapping:
public class OrgMap : ClassMap<Org>
{
public OrgMap()
{
Table(#"Org_Updates"); // Also tried Table("Org_Updates");
Map(x => x.ID);
Map(x => x.IndividualName);
Map(x => x.GroupName);
Map(x => x.AddressLine1, "PhysicalLocationAddress");
Map(x => x.AddressLine2, "PLAddr2");
Repository:
public class OrgRepository : RepositoryBase<Org>, IOrgRepository
{
public IList<Org>GetTop50()
{
var query = All().AsList();
return query;
}
}
RepositoryBase:
public class OrgRepositoryBase<T> : RepositoryBase<T> where T : class
{
public OrgRepositoryBase()
{
var registry = ServiceLocator.Current.GetInstance<EventListenerRegistry>();
registry.RegisterListenerForType<T>(GetType(), EventType.Save);
registry.RegisterListenerForType<T>(GetType(), EventType.Delete);
}
protected override ISession GetSession()
{
return UnitOfWork.Current.GetSessionFromContext<ISession>(typeof (OrgModel));
}
protected override Type ModelType
{
get { return typeof (OrgModel); }
}
}
}
As I said before, the other repositories that use other entites/mapping work. I can use this repository, exchanging the entity/mapping that it implements and it will work. I'm pretty sure it's because of hte mapping but can't tell which part. I have checked the table name and the column names.
Thanks for the help
I am making an assumption here that your table does have a primary key. If so, should you not map that primary key
public class OrgMap : ClassMap<Org>
{
public OrgMap()
{
Table(#"Org_Updates");
Id(x => x.ID);
Map(x => x.IndividualName);
Map(x => x.GroupName);
Map(x => x.AddressLine1, "PhysicalLocationAddress");
Map(x => x.AddressLine2, "PLAddr2");

Categories