I've created two entities which are related to each other by joining using three columns, both are views on the database. For the Travel I have the Id, FromCityId and ToCityId, for TravelCost I have the TravelId, FromCityId and ToCityId. The fluent mapping is as below
TravelEntityConfiguration class:
HasMany(x => x.Amounts)
.WithRequired()
.HasForeignKey(x => new
{
x.TravelId,
x.FromCityId,
x.ToCityId
});
and for the travel cost its
TravelCostEntityConfiguration
HasRequired(x => x.Travel)
.WithMany()
.HasForeignKey(x => new
{
x.TravelId,
x.FromCityId,
x.ToCityId
});
When i query these entities and include the navigation property
context.Set<Table>().Include(x => x.TravelCost)....
For each result only the first of the child collection is loaded. I checked the query generated, even the query is correct it returns the full set with all the children and the join is correctly. What am i missing here. Need some help no clue for the moment. basically i suspect the mapping is not correct but didn't found out the problem yet.
Thank you Asad, you lighted up my problem. The problem was with the Id of the TravelCost view which were generated on the fly and ended up to be the same so EF thinks it is the same record, the mapping was totally correct.
Related
I have a parent object which has a list of children object. Plain POCOs, nothing special. EF syntax is one to many.
In my dbcontext, I have the following:
if (parent.childObjectList.Any())
{
foreach (var x in parent.childObjectList)
{
x.Id = 0;
x.ParentId = parent.Id;
}
parent.childObjectList.ForEach(n => _context.ChildObjects.Add(n));
await _context.SaveChangesAsync();
}
EF syntax in model builder:
entity.HasMany(p => p.ChildObject)
.WithOne(e => e.Parent)
.HasForeignKey(p => p.ParentId)
.OnDelete(DeleteBehavior.Restrict);
In the child EF syntax:
entity.HasOne(x => x.Parent)
.WithMany(x => x.ChildObject)
.HasForeignKey(x => x.ParentId)
.OnDelete(DeleteBehavior.NoAction);
When I run this, if there are two childobjects in the list, 4 entries in the db get created. If there are 3 in the list, 6 get created. Where am I wrong in my syntax?
For one, manually persisting child entities is completely unnecessary. When you create the Parent and new Child instances under it and add the Parent, then call SaveChanges, the children will automatically be persisted and the FKs will be populated automatically. Also, configuring the relationship only needs to be done on one side. I.e. from parent with a HasMany().WithOne(), or from the child with a HasOne().WithMany(), not both.
The typical cause of double entries is when you create entirely new references, setting a PK and FK and adding those entities to another's collection and saving. EF isn't tracking those new instances so it treats them as an insert where you'd expect and update. That doesn't look to be what is happening here, but possibly something similar where by resetting IDs EF is treating these as new instances causing the double-up since if the Parent was already saved, the child records would have been already as well.
I have a Person class which can have Relatives which is a self reference many-to-many relationship(Relative is also a Person). I wanted to maintain this Person->Relatives mapping in a separate table. So, to create this relationship in EF, I did this:
modelBuilder.Entity<Person>()
.HasMany(e => e.Relatives)
.WithMany()
.Map(c =>
{
c.MapLeftKey("PersonId");
c.MapRightKey("RelativeId");
c.ToTable("PersonRelative");
});
Now, this created a new table PersonRelative with two columns PersonId and RelativeId automatically as I planned. It also have composite primary key on PersonId and RelativeId.
Now, there're two things I wish to do here:
I want to add a new identity "Id" column in this new PersonRelative table for better performance. How can I do that?
Now, can I create a separate class for PersonRelative table, the way we have a class for every table. Because that would try creating the table again. So, I think I can either keep that fluent api mapping or the PersonRelative class. Is this correct?
I would really appreciate any help here.
you can achieve two points by the following code:
modelBuilder.Entity<PersonRelative>().HasKey(x => x.Id);
modelBuilder.Entity<PersonRelative>()
.HasOne<Person>(x => x.Person)
.WithMany(s => s.PersonRelative)
.HasForeignKey(x => x.PersonId);
modelBuilder.Entity<PersonRelative>()
.HasOne<Realtive>(x => x.Realtive)
.WithMany(s => s.PersonRelative)
.HasForeignKey(x => x.RelativeId);
it's how to make many-to-many relation in entity framework core, as it's not supported yet in core, you can use it in non-core entity framework too.
Please Note, if you want a new table mapping just for accessing data with navigation properties, then you can add property of type "ICollection" for Students in Relative Class, and the same for Student Class you can add ICollection for access Relatives from Student
I'm using Fluent to map a parent collection -> child collection scenario for a framework demo I'm doing, I'm a bit of an NHibernate newbie so go easy :)
I have the following mappings for my DTOs:
public class OrderMapping : ClassMap<OrderDTO>
{
public OrderMapping()
{
Id(x => x.OrderId);
Map(x => x.OrderDate);
Map(x => x.Address);
HasMany<OrderLineDTO>(x => x.OrderLines).KeyColumn("OrderId").Not.LazyLoad();
Table("`Order`");
}
}
and
public class OrderLineMapping : ClassMap<OrderLineDTO>
{
public OrderLineMapping()
{
Id(x => x.OrderLineId);
Map(x => x.OrderId).ReadOnly();
Map(x => x.Amount);
Map(x => x.Description);
References<OrderDTO>(x => x.Order).Column("OrderId");
Table("OrderLine");
}
}
The DTO objects are set up to receive these values, and this all works fine (I've turned off LazyLoad for the child for now as I'm working on the framework and LazyLoading isn't finished).
So I open my test app - the data is loaded into two grids, with the parent and the children showing correctly
If I make changes to the child or parent object and attempt to save, I can see that the SQL generated for the parent/child save is correct and the values passed to the SQL are spot on - the only issue is that after the updates, NHib decides it wants to delete the collection? It attempts this SQL:
UPDATE OrderLine SET OrderId = null WHERE OrderId = 2
Which fails because the table has a FK from OrderLine to Order on OrderLine.OrderId = Order.OrderId with no cascade. Plus I don't understand why the collection requires a delete..
NHib throws a 'could not delete collection' error - is this because it's trying to delete the 'Order' reference from the OrderLineDTO child?
Edit:
Actually looking at the DTO it looks like on the reverse mapping from my business objects that 'Order' is not included on each OrderLineDTO so I've probably worked this out already! Hate it when I do that :)
Edit 2:
Turns out that even when correctly re-creating the object on the server side I get the same issue
any ideas?
Figured it out - RTFM :P
In the manual:
Very Important Note: If the column of a association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true". See the discussion of bidirectional associations later in this chapter.
How do I override the default convention for the foreign key column in EF4 to specify a different column name?
For example, I have one entity with a property called Parent that references to other one of the same type.
EF4 tries to resolve the relation by looking for the foreign key named EntityId, but in my DB schema it is Entity_Id. How do I tell EF that the FK column name is not EntityId?
I've tried the following:
modelBuilder.Entity<SomeEntity>()
.HasOptional(m => m.Parent)
.WithMany()
.IsIndependent()
.Map(m => m.MapKey(k => k.Id, "Entity_Id")));
But I get an exception saying: Sequence contains more than one matching element.
Any help on this?
Thanks!
First: Upgrade to EF 4.1 RTW. CTP 5 is outdated and contains potentially many bugs which are fixed now.
If you have done the upgrade the following should work:
modelBuilder.Entity<SomeEntity>()
.HasOptional(m => m.Parent)
.WithMany()
.Map(c => c.MapKey("Entity_Id"));
I'm working on a RoleProvider in .NET, using Fluent NHibernate to map tables in an Oracle 9.2 database.
The problem is that the many-to-many table connecting users and roles uses a primary key generated from a sequence, as opposed to a composite key. I can't really change this, because I'm writing it to be implemented in a larger existing system.
Here is my UserMap:
public UserMap()
{
this.Table("USR");
HasMany(x => x.Memberships).Cascade.All()
.Table("MEMBERSHIP").Inverse().LazyLoad();
HasManyToMany(x => x.Roles)
.Table("USR_ROLE")
.Cascade.SaveUpdate()
.ParentKeyColumn("USR_ID")
.ChildKeyColumn("ROLE_ID")
.Not.LazyLoad();
}
And my RoleMap:
public RoleMap()
{
this.Table("ROLE");
Map(x => x.Description).Column("ROLE_NAME");
Map(x => x.Comment).Column("ROLE_COMMENT");
HasManyToMany(x => x.Users)
.Table("USR_ROLE")
.ParentKeyColumn("ROLE_ID")
.ChildKeyColumn("USR_ID")
.Inverse();
}
Yet, this is giving me the error:
Type 'FluentNHibernate.Cfg.FluentConfigurationException' in assembly 'FluentNHibernate, Version=1.0.0.593, Culture=neutral, PublicKeyToken=8aa435e3cb308880' is not marked as serializable.
Is there a simple fix to allow this HasMayToMany to use my PersistentObjectMap extension? I'm thinking I may have to add a convention for this many-to-many relationship, but I don't know where to start with that, since I've just started using NHibernate and Fluent NHibernate only recently.
I've been working on this problem for a while and I can't seem to find a solution.
Any help would be much appreciated. Thanks.
EDIT: I think I've found a possible solution here: http://marekblotny.blogspot.com/2009/02/fluent-nhbernate-and-collections.html
I'll try the above method of creating an entity and a class map for the linking table and post my findings.
EDIT 2: I created a linking entity as mentioned in the above blog post and downloaded the newest binaries (1.0.0.623).
This helped me discover that the issue was with setting lazy load and trying to add roles to the user object in a completely new session.
I modified the code to move OpenSession to the BeginRequest of an HttpModule as described here. After doing this, I changed my data access code from wrapping the open session in a using statement, which closes the session when it is finished, to getting the current session and wrapping only the transaction in a using statement.
This seems to have resolved the bulk of my issue, but I am now getting an error that says "Could not insert collection" into the USR_ROLE table. And I'm wondering if the above code should work with a UserRoleMap described as:
public UserRoleMap()
{
this.Table("USR_ROLE");
/* maps audit fields id, created date/user, updated date/user */
this.PersistentObjectMap("USR_ROLE");
/* Link these tables */
References(x => x.Role).Column("ROLE_ID");
References(x => x.User).Column("USR_ID");
}
Hibernate's documentation for many-to-many relationship suggests creating an object to maintain a one-to-many/many-to-one, as in an ERD. I'm sure this would be much easier with conventional naming standards, but I have to stick with certain abbreviations and odd (and not always properly-implemented) conventions.
To fix this, I created an Entity, Mapping, and Repository for UserRole. And, instead of HasManyToMany mapping in the User and Role Entities, I have a HasMany mapping. It's a little weird, because I now have:
IList<UserRole> UserRoles {get; protected set;}
and IList<Role> Roles { get{ return UserRoles.Select(u => u.Role).ToList(); } }
This works, however, I'm not 100% sure why this works and the HasManyToMany doesn't.