Doesn't update many to many attribute using NHibernate - asp.net-mvc-4

Suppose I have only two classes: Group and User. User has groups and Group has members (instance of users)
public class User {
public virtual int id { set; get; }
public virtual string username { set; get; }
public virtual IList<Group> groups { set; get; }
public User()
{
groups = new List<Group>();
}
public virtual void joinGroup(Group group)
{
if (this.groups.Contains(group))
throw new AlreadyJoinedException();
group.members.Add(this);
this.groups.Add(group);
}
public class Group
{
public virtual int id { set; get; }
public virtual string name { set; get; }
public virtual User administrator { set; get; }
public virtual IList<User> members { set; get; }
public Group()
{
members = new List<User>();
}
As you can see the domain it's quite simple. I've already mapped both classes correctly using Fluent NHibernate,
public class UserMapping : ClassMap<User>
{
public UserMapping()
{
this.Id(user => user.id).GeneratedBy.Identity();
this.Map(user => user.username).Not.Nullable().Length(50).Not.LazyLoad();
this.HasManyToMany(user => user.groups).Table("MemberPerGroup").ParentKeyColumn("id_user").ChildKeyColumn("id_group").Not.LazyLoad();
}
}
public class GroupMapping : ClassMap<Group>
{
public GroupMapping()
{
this.Id(group => group.id).GeneratedBy.Identity();
this.Map(group => group.name).Not.Nullable().Length(50).Not.LazyLoad();
this.References(group => group.administrator).Not.Nullable().Not.LazyLoad();
this.HasManyToMany(group => group.members).Table("MemberPerGroup").ParentKeyColumn("id_group").ChildKeyColumn("id_user").Not.LazyLoad();
}
}
I'm progamming a web application using ASP MVC 4. My problem shows up when a user tries to join group. It doesn't break but it neither works fine (doesn't insert into the table the new row in MemberPerGroup). I'm doing something like it:
public void JoinGroup(User user,Group group){
this.userRepository.GetSessionFactory().TransactionalInterceptor(() =>
{
user.joinGroup(group);
});
}
Thanks in advance.
Ivan.

It seems your mapping has no cascading set?
this.HasManyToMany(group => group.members)
.Table("MemberPerGroup")
.ParentKeyColumn("id_group")
.ChildKeyColumn("id_user")
.Not.LazyLoad()
.Cascade.SaveUpdate();
I'm curious - why do you use GetSessionFactory()? our repositories take an ISession object in the constructor, (injected by autofac, but that's irrelevant) from which we start our queries:
// even better to use a transaction, but this is just a sample
_session.SaveOrUpdate(user);
_session.Flush();

Related

Entity Framework Code First Many-to-Many relationship and inheritance

Forgive me if this question has been answered somewhere, I have been having a hard time finding a solution for this problem.
I am trying to set up EF Code First on an MVC4 Project. I have a User and Customer that both inherit from Person. I then have a Template object that has a Many-to-Many relationship with Customer and a One-to-Many relationship with User. Here is how I have it set up:
MODELS
public class Person
{
[Key]
public int PersonID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string FullName
{
get
{
return String.Format("{0} {1}", FirstName, LastName);
}
}
public string Email { get; set; }
public virtual List<Template> Templates { get; set; }
}
public class User : Person
{
....
}
public class Customer : Person
{
....
}
public class Template
{
public int TemplateId { get; set; }
public string TemplateName { get; set; }
public virtual List<Customer> Customers { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public virtual User User { get; set; }
}
CONTEXT
public class ProjectContext : DbContext
{
public ProjectContext()
: base("name=ProjectDB")
{
}
public DbSet<Template> Templates { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Template>()
.HasMany(x => x.Customers)
.WithMany(x => x.Templates)
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer")
);
}
}
If I remove the Person DBSet out of the context this works fine but sets up TPT inheritance. I would like to use TPH inheritance, but when I enable migrations with the Person DBSet in the context it chokes:
NavigationProperty 'Templates' is not valid. Type 'MvcProject.Models.Customer' of FromRole 'Template_Customers_Target' in AssociationType 'MvcProject.Models.Template_Customers' must exactly match with the type 'MvcProject.Models.Person' on which this NavigationProperty is declared on.
Where am I going wrong here?
You cannot inherit navigation properties from a base entity. They always must be declared in the class the other end of the relationship is refering to.
Template.Customers is refering to Customer (not to Person), hence the inverse navigation property Templates must be declared in Customer (not in Person)
Template.User is refering to User (not to Person), hence the inverse navigation property Templates must be declared in User (not in Person)
So, basically you must move the Templates collection from Person into both derived classes:
public class Person
{
// no Templates collection here
}
public class User : Person
{
//...
public virtual List<Template> Templates { get; set; }
}
public class Customer : Person
{
//...
public virtual List<Template> Templates { get; set; }
}
Then you can define the two relationships with Fluent API like so:
modelBuilder.Entity<Template>()
.HasMany(t => t.Customers)
.WithMany(c => c.Templates) // = Customer.Templates
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer"));
modelBuilder.Entity<Template>()
.HasRequired(t => t.User)
.WithMany(u => u.Templates) // = User.Templates
.HasForeignKey(t => t.UserId);
Change your HasMany selector to People:
modelBuilder.Entity<Template>()
.HasMany(x => x.People) // here
.WithMany(x => x.Templates)
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer")
);

mapping one-to-many relation with Fluent NHibernate does not contain any child when browsing through one-end

I've created the following domain classes:
public class Car
{
public virtual int Id { get; set; }
public virtual string Registration { get; set; }
public virtual User ResponsibleContact { get; set; }
protected Car()
{}
public Fahrzeug(User responsibleContact, string registration)
{
ResponsibleContact = responsibleContact;
Registration = registration;
ResponsibleContact.Cars.Add(this);
}
}
public class User
{
public virtual int Id { get; set; }
public virtual byte[] EncryptedUsername { get; set; }
public virtual byte[] EncryptedPassword { get; set; }
public virtual IList<Car> Cars { get; private set; }
public virtual string Username
{
get
{
var decrypter = UnityContainerProvider.GetInstance().UnityContainer.Resolve<IRijndaelCrypting>();
return decrypter.DecryptString(EncryptedUsername);
}
}
protected User()
{ }
public User(byte[] encryptedUser, byte[] encryptedPassword)
{
Cars = new List<Car>();
EncryptedUsername = encryptedUser;
EncryptedPassword = encryptedPassword;
}
}
and the mapping classes:
public class CarMap : ClassMap<Car>
{
public CarMap()
{
Id(c => c.Id).GeneratedBy.Native();
Map(c => c.Registration);
References(c => c.ResponsibleContact).Not.Nullable();
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(st => st.Id).GeneratedBy.Native();
Map(st => st.EncryptedUsername).Column("Username");
Map(st => st.EncryptedPassword).Column("Password");
HasMany(st => st.Cars).Inverse().AsBag();
}
}
If I query some Member objects, I get the members, but the cars collection is empty!
If I query some Cars I got all the cars with the right Member. But within the member, the cars collection is also empty!
Is there anybody who has an Idea of what can happened?
you have to make sure the foreign key column of the collection and the reference is the same otherwise there is a mismatch.
References(c => c.ResponsibleContact, "ResponsibleContact_id").Not.Nullable();
and
HasMany(st => st.Cars).Inverse().KeyColumn("ResponsibleContact_id");

FluentNhibernate many-to-many and Inverse()

I have the following database tables defined:
Club: Id, Name
Member: Id, Name
ClubMember: ClubId, MemberId
I have the following entity Classes defined:
public class Club() {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Member> Members { get; set; }
}
public class Member() {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Club> Clubs { get; set; }
}
I have the following overrides defined:
public class MemberOverride : IAutoMappingOverride<Member>
{
public void Override(AutoMapping<Member> mapping_)
{
mapping_
.HasManyToMany(x_ => x_.Clubs)
.ParentKeyColumn("MemberId")
.ChildKeyColumn("ClubId")
.Cascade.All()
.Table("ClubMembers");
}
}
public class ClubOverride : IAutoMappingOverride<Club>
{
public void Override(AutoMapping<Club> mapping_)
{
mapping_
.HasManyToMany(x_ => x_.Members)
.ParentKeyColumn("ClubId")
.ChildKeyColumn("MemberId")
.Inverse()
.Table("ClubMembers");
}
}
I can see from my overrides that the Inverse on the ClubOverride means you cannot do the following
session.Save(club.Members.Add(member));
but this works:
session.Save(member.Clubs.Add(club);
But it doesn't make logical sense. I want to be able to save either the club with members or member with clubs.
Am I trying to do something impossible with FluentNhibernate?
TIA
Yes, you're right, that's not possible. But it's not a question of FluentNhibernate, NHibernate works like that.
Only one side is the owner of the relation and on charge of adding elements.
From official documentation:
Changes made only to the inverse end of the association are not persisted. This means that NHibernate has two representations in memory for every bidirectional association, one link from A to B and another link from B to A. This is easier to understand if you think about the .NET object model and how we create a many-to-many relationship in C#:
You can create add or remove methods on your entities that will help accomplish this:
public class Club() {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
private IList<Member> members;
public virtual IEnumerable<Member> Members { get { return members.Select(x => x); } }
public Club() {
members = new List<Member>();
}
public virtual void AddMember(Member member){
if (members.Contains(member))
return;
members.Add(user);
member.AddClub(this);
}
public virtual void RemoveMember(Member member){
if (!members.Contains(member))
return;
members.Remove(member);
member.RemoveClub(this);
}
}

Sharp Architecture issue with EntityWithTypedId

I wish to use Guid comb for my identity. I've added the EntityWithTypedId interface to my class which cause my table not to persist. ie using nh prof i can see the SaveOrUpdate method is not called. If i just use the Entity interface i can see it in the profiler.
public class Application : EntityWithTypedId<Guid>
{
public virtual string Name { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual string ModifiedByUserName { get; set; }
public virtual DateTime Created { get; set; }
public virtual DateTime Modified { get; set; }
}
public class ApplicationQuery : NHibernateQuery, IApplicationQuery
{
public void Update(Application application)
{
Session.SaveOrUpdate(application);
}
}
public class ApplicationMap : IAutoMappingOverride<Application>
{
public void Override(AutoMapping<Application> mapping)
{
mapping.Table("Application");
mapping.Id(x => x.Id, "Id").GeneratedBy.GuidComb();
}
}
Controller
[HttpPost]
[ActionName("Edit")]
public ActionResult EditPost(Application application)
{
var updateApplication = new Application();
updateApplication.Created = DateTime.Now;
updateApplication.Modified = DateTime.Now;
_applicationQuery.Update(updateApplication);
return RedirectToAction("Index");
}
I fixed this by updating my mapping to the following.
public void Override(AutoMapping<Application> mapping)
{
mapping.Table("Application");
mapping.Id(x => x.Id, "Id").GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
}
See here for more info.
http://s274.codeinspot.com/q/1486941

NHibernate2 query is wired when fetch the collection from the proxy. Is this correct behavior?

This is my class:
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<UserFriend> Friends { get; protected set; }
}
public class UserFriend
{
public virtual int Id { get; set; }
public virtual User User { get; set; }
public virtual User Friend { get; set; }
}
This is my mapping (Fluent NHibernate):
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id, "UserId").GeneratedBy.Identity();
HasMany<UserFriend>(x => x.Friends);
}
}
public class UserFriendMap : ClassMap<UserFriend>
{
public UserFriendMap()
{
Id(x => x.Id, "UserFriendId").GeneratedBy.Identity();
References<User>(x => x.User).TheColumnNameIs("UserId").CanNotBeNull();
References<User>(x => x.Friend).TheColumnNameIs("FriendId").CanNotBeNull();
}
}
The problem is when I execute this code:
User user = repository.Load(1);
User friend = repository.Load(2);
UserFriend userFriend = new UserFriend();
userFriend.User = user;
userFriend.Friend = friend;
friendRepository.Save(userFriend);
var friends = user.Friends;
At the last line, NHibernate generate this query for me:
SELECT
friends0_.UserId as UserId1_,
friends0_.UserFriendId as UserFrie1_1_,
friends0_.UserFriendId as UserFrie1_6_0_,
friends0_.FriendId as FriendId6_0_,
friends0_.UserId as UserId6_0_
FROM "UserFriend" friends0_ WHERE friends0_.UserId=#p0; #p0 = '1'
QUESTION: Why the query look very wired? It should select only 3 fields (which are UserFriendId, UserId, FriendId) Am I right? or there is something going on inside NHibernate?
You should have a look at the mapping generated by fluent-nhibernate, maybe that generated something weird.