EF4.3 many to many table as entity - entity-framework

I have there entities two are normal the other is mapping between each other any time I put navigation mapping on I get invalid object name.
If I try and map I get table already in schema.
EntityA EntityB EntityC
Id Id EntityA has FK} PrimaryKey
Name description EntityB has FK}
The mapping classes
I have taken all navigation on EntityB that caused my problems and entity after trying that
I tried this id in entity c but still got invalid table name. the name it was trying to create was dbo.BA where the actual table is dbo.tblAB
//entity.HasRequired(p => p.A).WithMany(p => p.C).HasForeignKey(p => p.EntityA);
//entity.HasRequired(p => p.B).WithMany(p => p.C).HasForeignKey(p => p.EntityB)
I cannot use the .map because I need to use the all three entities with in the DAL
Mapping classes
Entity C
entity.HasKey(t => new {t.EntityA, t.EntityB});
entity.Property(qa => qa.EntityA).IsRequired();
entity.Property(qa => qa.EntityB.).IsRequired();
entity.toTable("tblC)
this was exiting table and because we need to delete these records we need to do through the entity.
Entity B and Entity A have ICollection Properties to Entity C and mapping of
In Entity A mapping
entity.HasMany(g => g.C).WithRequired().HasForeignKey(p => p.EntityA).WillCascadeOnDelete(false);
In entity B mapping
entity.HasMany(g => g.C).WithRequired().HasForeignKey(p => p.EntityB).WillCascadeOnDelete(false);
In entity C mapping
entity.Property(t => t.EntityA).HasColumnName("EntityA");
entity.Property(t => t.EntityB).HasColumnName("EntityB");
entity.Map(c => c.ToTable("tblC")).HasKey(t => new { t.EntityA, t.EntityB });*
I was getting a multiplicity error but now I am getting Invalid column name 'EntityC_EntityA'. Invalid column name 'EntityC_EntityB'.
UPDATE
I have now fixed the schema with
In EntityC
[Key, Column(Order = 0), ForeignKey("EntityA")]
[Key, Column(Order = 1), ForeignKey("EntityB"))
Navigaton properties
[ForeignKey("Id")]
public virtual EntityA EntityA { get; set; }
[ForeignKey("Id")]
public virtual EntityB EntityB { get; set; }
In the Entity A and b mapping
entity.HasMany(g => g.Cs).WithRequired(p=>p.EntityA).HasForeignKey(p => p.EntityA).WillCascadeOnDelete(false);
entity.HasMany(g => g.Cs).WithRequired(p=>p.EntityB).HasForeignKey(p => p.EntityB).WillCascadeOnDelete(false);
The problem know that the query is trying to create the db.BA table.
The query I have tried is
ctx.EntityAs.Include(p => p.Cs.Select(pr => pr.EntityB))
.Include(p => p.EntityBs.Select(pr => pr.Cs.Select(ps => ps.EntityA)))

I would like to thank Gert Arnold for putting in the write direction. The answer for the schema is in the question
The Linq to solve the many is to include EntityC with a select on B or A
entityA.Include(p => p.Cs.Select(pr => pr.EntityA))
or
entityB.Include(p => p.Cs.Select(pr => pr.EntityB))

Related

Entity Framework - error when adding entity with related entity

I have two entities:
public class EntityA
{
public int? Id { get; set; }
public string Name { get; set; }
public EntityB { get; set; }
}
public class EntityB
{
public int? Id { get; set; }
public string Version { get; set; }
}
I have existing records for EntityB already in the database. I want to add a new EntityA with reference to one of the EntityB records.
var entityB = _dbContext.EntityB.FirstOrDefault(e => e.Id == 1);
var entityA = new EntityA { Name = "Test", EntityB = entityB };
_dbContext.Add(entityA);
_dbContext.SaveChanges();
When the above code runs I get the following error:
System.InvalidOperationException: The property 'Id' on entity type 'EntityB' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key first delete the dependent and invoke 'SaveChanges' then associate the dependent with the new principal.
This seems to me, that the save is trying to also add EntityB, not just a reference to it. I do have the relationship specified in the database as well as in Entity Framework, e.g. when querying for EntityA if I include EntityB in the select, I get the referenced entity as well (so the relationship works).
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id);
e.HasOne(p => p.EntityB)
.WithOne()
.HasForeignKey<EntityB>(p => p.Id);
}
modelBuilder.Entity<EntityB>(e =>
{
e.HasKey(p => p.Id);
}
How can I save a new EntityA, with only a reference to the selected EntityB, rather than saving both entities?
It looks like you are trying to Extend EntityB with an optional 1:1 reference to a Row n the new table EntityA. You want both records to have the same value for Id.
This 1:1 link is sometimes referred to as Table Splitting.
Logically in your application the record from EntityB and EntityA represent the same business domain object.
If you were simply trying to create a regular 1 : many relationship, then you should remove the HasOne().WithOne() as this creates a 1:1, you would also not try to make the FK back to the Id property.
The following advice only applies to configure 1:1 relationship
you might use Table Splitting for performance reasons (usually middle tier performance) or security reasons. But it also comes up when we need to extend a legacy schema with new metadata and there is code that we cannot control that would have broken if we just added the extra fields to the existing table.
Your setup for this is mostly correct, except that EntityA.Id cannot be nullable, as the primary key it must have a value.
public class EntityA
{
public int Id { get; set; }
public string Name { get; set; }
public EntityB { get; set; }
}
If you want records to exist in EntityA that DO NOT have a corresponding record in EntityB then you need to use another Id column as either the primary key for EntityA or the foreign key to EntityB
You then need to close the gap with the EntityA.Id field by disabling the auto generated behaviour so that it assumes the Id value from EntityB:
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id).ValueGeneratedNever();
e.HasOne(p => p.EntityB)
.WithOne()
.HasForeignKey<EntityB>(p => p.Id);
}
I would probably go one step further and add the Reciprocating or Inverse navigation property into EntityB this would allow us to use more fluent style assignment, instead of using _dbContext.Add() to add the record to the database:
public class EntityB
{
public int Id { get; set; }
public string Version { get; set; }
public virtual EntityA { get; set; }
}
With config:
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id).ValueGeneratedNever();
e.HasOne(p => p.EntityB)
.WithOne(p => p.EntityA)
.HasForeignKey<EntityB>(p => p.Id);
}
This allows you to add in a more fluent style:
var entityB = _dbContext.EntityB.FirstOrDefault(e => e.Id == 1);
entityB.EntityA = new EntityA { Name = "Test" };
_dbContext.SaveChanges();
This will trip up because you are using EntityA's PK as the FK to Entity B, which enforces a 1 to 1 direct relation. An example of this would be to have something like an Order and OrderDetails which contains additional details about a specific order. Both would use "OrderId" as their PK and OrderDetails uses it's PK to relate back to its Order.
If instead, EntityB is more like an OrderType reference, you wouldn't use a HasOne / WithOne relationship because that would require Order #1 to only be associated with OrderType #1. If you tried linking OrderType #2 to Order #1, EF would be trying to replace the PK on OrderType, which is illegal.
Typically the relationship between EntityA and EntityB would require an EntityBId column on the EntityA table to serve as the FK. This can be a property in the EntityA entity, or left as a Shadow Property (Recommended where EntityA will have an EntityB navigation property) Using the above example with Order and OrderType, an Order record would have an OrderId (PK) and an OrderTypeId (FK) to the type of order it is associated with.
The mapping for this would be: (Shadow Property)
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id);
e.HasOne(p => p.EntityB)
.WithMany()
.HasForeignKey("EntityBId");
}
An OrderType can be assigned to many Orders, but we don't have an Orders collection on OrderType. We use the .HasForeignKey("EntityBId") to set up the shadow property of "EntityBId" on our EntityA table. Alternatively, if we declare the EntityBId property on our EntityA:
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id);
e.HasOne(p => p.EntityB)
.WithMany()
.HasForeignKey(p => p.EntityBId);
}
On a side note, navigation properties should be declared virtual. Even if you don't want to rely on lazy loading (recommended) it helps ensure the EF proxies for change tracking will be fully supported, and lazy loading is generally a better condition to be in at runtime than throwing NullReferenceExceptions.

Entity Framework nested joins

I am new to entity framework and am having a hard time trying to figure out how to query with a join when my models look like this (drastically simplified)
class Customer
{
public int Id {get; set;}
public Vehicles Vehicles {get; set;}
}
class Vehicles
{
public List<Vehicle> Items {get; set;}
}
class Vehicle
{
public int Id {get; set;}
public int CustomerId {get; set;}
}
If I put the List<Vehicle> on the customer class directly. I am able to do fluent mapping like this
builder.Entity<Customer>()
.HasMany(x => x.Items)
.WithOne()
.HasForeignKey(x => x.CustomerId);
Which then I can do this and I get back a customer object with vehicles
db.Customers.Include(x => x.Items).FirstOrDefault(x => x.Id == 1);
What I am not understanding is how to do this with my original set of models. I would like to keep them the way they are if possible. I have tried doing various versions of this in my onModelCreating method with no luck.
builder.Entity<Customer>(t =>
{
t.OwnsOne(x => x.Vehicles, v =>
{
v.HasMany(x => x.Items).WithOne().HasForeignKey(x => x.CustomerId);
});
});
It's possible to map the original classes, but in quite counterintuitive way.
Since the Vehicles class is just a container, mapping it as owned entity as you have tried seems the most natural way. However currently EF Core does not allow owned entity to be at the principal side of the relationship, and in your case this is needed.
So instead you need to map the Vehicles class as regular "entity" sharing the same table with the Customer - the so called table splitting. You have to do explcitly all that EF Core does implicitly for owned entities - define a shadow property and map is a both PK and FK for the one-to-one relationship with the Customer. You'd need also the explicitly map the Vehicle.CustomerId as a FK because from EF point of view the Vehicle is related to Vehicles rather than to Custome, hence the conventional FK property / column name assumed will be VehiclesId. Note that with this model you'll never be able to define an inverse navigation property Customer of the Vehicle.
With that being said, here is the fluent configuration needed:
modelBuilder.Entity<Vehicles>(builder =>
{
// Table
builder.ToTable(modelBuilder.Entity<Customer>().Metadata.Relational().TableName);
// PK
builder.Property<int>("Id");
builder.HasKey("Id");
// One-to-one relationship with Customer
builder.HasOne<Customer>()
.WithOne(e => e.Vehicles)
.HasForeignKey<Vehicles>("Id");
// One-to-many relationship with Vehicle
builder.HasMany(e => e.Items)
.WithOne()
.HasForeignKey(e => e.CustomerId);
});
and usage:
db.Customers
.Include(x => x.Vehicles.Items) // <--
// ...
Use .Join
See this question for some examples:
Entity Framework Join 3 Tables

Delete child entity when removed from parent collection in one to many relationship Entity Framework

I have the following database setup:
The tables are mapped as follow:
public class OrderMapping : EntityTypeConfiguration<Order>
{
public OrderMapping()
{
this.ToTable("Orders", "prf");
this.HasKey(o => o.OrderId);
this.HasMany(o => o.OrderItems)
.WithRequired(oi => oi.Order)
.HasForeignKey(oi => oi.OrderId);
this.HasRequired(o => o.Institution)
.WithMany()
.HasForeignKey(o => o.InstitutionId);
this.Property(o => o.IsConfirmed)
.IsRequired();
this.Ignore(o => o.Id);
}
}
public class OrderItemMapping : EntityTypeConfiguration<OrderItem>
{
public OrderItemMapping()
{
this.ToTable("OrderItems", "prf");
this.HasKey(oi => oi.OrderItemId);
this.HasRequired(oi => oi.Order)
.WithMany(oi => oi.OrderItems)
.HasForeignKey(oi => oi.OrderId);
this.HasRequired(oi => oi.Proficiency)
.WithMany()
.HasForeignKey(oi => oi.ProficiencyId);
this.HasOptional(oi => oi.Enrolment)
.WithMany()
.HasForeignKey(oi => oi.EnrolmentId);
this.HasMany(oi => oi.OrderItemSets)
.WithRequired(ois => ois.OrderItem)
.HasForeignKey(ois => ois.OrderItemId);
this.Property(oi => oi.DateCreated);
this.Ignore(oi => oi.Id);
}
}
public class OrderItemSetMapping : EntityTypeConfiguration<OrderItemSet>
{
public OrderItemSetMapping()
{
this.ToTable("OrderItemSets", "prf");
this.HasKey(ois => ois.OrderItemSetId);
this.HasRequired(ois => ois.OrderItem)
.WithMany(ois => ois.OrderItemSets)
.HasForeignKey(ois => ois.OrderItemId);
this.Property(ois => ois.NumberOfSets);
this.Property(ois => ois.Month);
}
}
When I try to remove an OrderItemSet from the OrderItem's collection Entity Framework is trying to set the foreignkey in OrderItemSet as null instead of deleting the row even though the foreignkey is not nullable and therefore throwing an exception stating the foreignkey cannot be set to null.
this.OrderItemSets.Remove(orderItemSet);
I don't know what is wrong with my mapping to have Entity Framework think it should set the foreignkey to null instead of deleting the row.
What you need is an identifying relationship between OderItem and OrderItemSet. From the section Considerations for Identifying and Non-identifying Relationships in the link provided above:
Removing the relationship deletes the dependent object. Calling the Remove method on the EntityCollection marks both the relationship and the dependent object for deletion.
You should consider the same type of relation ship for Order and OrderItem.
The basic idea is that, for the model of OrderItemSet, you make the foreign key to OrderItem part of the OrderItemSet's primary key, thus creating a composite key. Inside the mapping for OrderItemSet try to map the primary key like so:
public OrderItemSetMapping()
{
...
this.HasKey(ois => new { ois.OrderItemSetId, ois.OrderItemId });
...
}
In case this doesn't work with the fluent API, then try to create the mapping with attributes:
public class OrderItemSet
{
[Key, ForeignKey("OrderItem"), Column(Order = 1)]
public <YourKeyType> OrderItemId { get; set; }
[Key, Column(Order = 2)]
public <YourKeyType> OrderItemSetId { get; set; }
...
}
Why don't you just remove the child directly:
context.OrderItemSets.Remove(orderItemSet);
context.SaveChanges();
When you remove a child from a parent, you may want to add it to another parent, so it would be inappropriate for Entity Framework to delete the child automatically. You should do it yourself.
I have resolved the issue as per Abbondanza's suggestion. First off create the key of the entity to contain the foreign key (this will force entity framework to delete the child item as it cannot exist without the foreign key):
public OrderItemSetMapping()
{
...
this.HasKey(ois => new { ois.OrderItemSetId, ois.OrderItemId });
...
}
Entity Framework will now delete the entity if removed from the collection of the parent, however since OrderItemSetId is an Identity column this creates another issue where Entity Framework now wants to insert a value in that column when adding a new item to the parent collection (which will throw an exception). By specifying a DatabaseGenerationOption on this column the problem will be resolved:
public OrderItemSetMapping()
{
...
this.Property(r => r.OrderItemSetId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
...
}

Modeling a composite key of foreign keys (modelled as entity references) in EF6

I am coming from nHibernate and am trying to create an entity that has a 2 column composite key where both columns are also foreign keys.
For example I have a UserRole table that is (UserId Guid, RoleId Guid). I want to model this as
public class UserRole
{
public User User { get; set; }
public Role Role { get; set; }
}
EF doesn't seem to like this idea though. It seems to want me to also add Guid UserId {get;set;} and Guid RoleId { get; set; }. I managed to resolve this for the handling the FK part by defining the model in the dbcontext like so:
modelBuilder.Entity<UserRole>()
.HasRequired(x => x.Role)
.WithRequiredPrincipal()
.Map(x => x.MapKey("RoleId"));
modelBuilder.Entity<UserRole>()
.HasRequired(x => x.User)
.WithRequiredPrincipal()
.Map(x => x.MapKey("UserId"));
Which I hope I can turn into a convention. However when I tried to do this too:
modelBuilder.Entity<UserRole>().HasKey(x => new { x.User, x.Role });
it craps out at runtime with:
The property 'User' cannot be used as a key property on the entity 'Paroxysm.Domain.UserRole' because the property type is not a valid key type. Only scalar types, string and byte[] are supported key types.
FYI this is done in nHibernate byCode mapping like this (slightly different example):
public class ProjectUserProfileMap : ClassMap<ProjectUserProfile>
{
public ProjectUserProfileMap()
{
CompositeId()
.KeyReference(x => x.User, "UserId")
.KeyReference(x => x.Project, "ProjectId");
ReadOnly();
References(x => x.User, "UserId");
References(x => x.Project, "ProjectId");
Map(x => x.IsActive);
Map(x => x.ActivatedUtcDate).Not.Nullable().CustomType<NHibernate.Type.UtcDateTimeType>();
Map(x => x.InvitedUtcDate).Not.Nullable().CustomType<NHibernate.Type.UtcDateTimeType>();
Table("ProjectUserProfile");
}
}
So easy! Incidentally that little CustomType UTC behaviour doesn't seem to be supported by EF either.
Problem is not actually related to the fact that I have a composite key but having a single column PK which is also an FK would be a weird case (1:1 rel).
So I guess I want to know definitely if this can or cannot be done in EF6. The error message certainly indicates its not doable. Can someone confirm?
You could achieve this but only if you add to UserRole 2 foreign key properties: RoleId and UserId. This is because HasKey do not offer any mapping functionality - it can be defined only on properties existing in entity classes. It seems EF enforces that all Primary Key columns are always defined as concrete properties in classes wheres foreign key columns may not have corresponding properies defined. So to achieve what you want you need to define UserRole like this:
public class UserRole
{
public User User { get; set; }
public int UserId { get; set; }
public Role Role { get; set; }
public int RoleId { get; set; }
}
modelBuilder.Entity<UserRole>()
.HasRequired(x => x.Role)
.WithRequiredPrincipal()
.HasForeignKey(x => x.RoleId);
modelBuilder.Entity<UserRole>()
.HasRequired(x => x.User)
.WithRequiredPrincipal()
.HasForeignKey(x => x.UserId);
modelBuilder.Entity<UserRole>().HasKey(x => new { x.UserId, x.RoleId });
The exact situation as you posted you might alternatively achieve by many-to-many relationship:
modelBuilder.Entity<User>()
.HasMany(x => x.Roles)
.WithMany()
.Map(m =>
{
m.ToTable("UserRole");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
With this you would achieve UserRole table with primary key defined on UserId and RoleId.

EF 4.1 code first: how to refer to child records from parent in a one-to-many relationship

I have a simple 1:many aggregate relationship, lets say:
public class Parent
{
public string Name {get; set;}
public Child SelectedChild {get; set;}
public Child PublishedChild {get; set;}
public virtual ICollection<Child> AllChildren {get; set;}
}
public class Child
{
public string Name {get; set;}
[Required]
public Parent Father {get; set;}
}
When creating the schema from this model I get the error:
Introducing FOREIGN KEY constraint 'Parent_SelectedChild' on table 'Parent' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints
So I add the following to OnModelCreating:
modelBuilder.Entity<Child>()
.HasRequired(v => v.Parent)
.WithOptional(c => c.SelectedChild)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Child>()
.HasRequired(v => v.Parent)
.WithOptional(c => c.PublishedChild)
.WillCascadeOnDelete(false);
This gets round the original error but I now get:
Unable to determine the principal end of the 'xxx.Parent_SelectedChild' relationship.
Multiple added entities may have the same primary key.
Can anyone help?
All I essentially want to do is refer to particular child records on a 1:many aggregate relationship from the parent. I assume EF will create INT child id columns on the parent called e.g. SelectedChild_Id & PublishedChild_Id (or similar).
Thanks in advance
-macon
Edit: In response to #Slauma:
I can get a schema generated using:
modelBuilder.Entity<Parent>()
.HasOptional(p => p.SelectedChild)
.WithOptionalPrincipal()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Parent>()
.HasOptional(p => p.PublishedChild)
.WithOptionalPrincipal()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Parent>()
.HasMany(p => p.AllChildren)
.WithRequired(c => c.Father)
.WillCascadeOnDelete(false);
But this generates multiple FK on the Child record e.g. Parent_Id, Parent_Id1. I just want a reference from the Parent to one of the child rows e.g. Parent_SelectedChildId. Do I have to do this manually with an int column on parent?
I think you have three 1-to-many relationships:
modelBuilder.Entity<Parent>()
.HasOptional(p => p.SelectedChild)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Parent>()
.HasOptional(p => p.PublishedChild)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Parent>()
.HasMany(p => p.AllChildren)
.WithRequired(c => c.Father)
.WillCascadeOnDelete(false);
Edit
I've tested my mapping above with exactly the Parent and Child class you provided in your question - with the only exception that I have added a primary key property to both classes: public int Id { get; set; }. Otherwise EF would complain about a missing key property. This mapping doesn't throw an exception and creates the following tables in the database:
Parents table:
- Id int not nullable (PK)
- Name nvarchar(MAX) nullable
- SelectedChild_Id int nullable (FK)
- PublishedChild_Id int nullable (FK)
Children table:
- Id int not nullable (PK)
- Name nvarchar(MAX) nullable
- Father_Id int not nullable (FK)
So, there are the three foreign key columns as expected.
Since you get an exception according to your comment, I guess that there is actually some important difference in the code you have tested.
BTW: Mapping the two navigation properties of the Parent class as One-to-One relationships is much more difficult, if not impossible. In EF you need a shared primary key between the two tables to map a One-to-One relationship, so it would not be possible to assign two different entities to the two navigation properties because they cannot both have the same key as the parent.