In our project we have modules (extensions) for base project. Every module have its own entities, context and migrations. Now I have ModuleA and ModuleB.
In ModuleA I declare EntityA:
public class EntityA
{
public string Id { get; set; }
}
In ModuleB I declare EntityB with navigation property of type EntityA:
public class EntityB
{
public string Id { get; set; }
public string EntityAId { get; set; }
public EntityA EntityA { get; set; }
}
I want to cascade delete entities B, related to entity A, when A deletes:
modelBuilder.Entity<EntityB>().HasRequired(x => x.EntityA).WithMany().HasForeignKey(x => x.EntityAId).WillCascadeOnDelete(true);
The problem is what when I try to create new migration for ModuleB, EF creates tables and columns for EntityA too (which already exists in ModuleA). Is there any way to estabilish relationship between EntityA and EntityB, when they use different contexts?
Related
I have a property on my items class called vend_id which of course EF thinks is a foreign key to the vendor table. It actually should be a foreign key in the database but for reasons unknown to me the designers of the db chose not to make it a foreign key.
I am using EF to create a copy of the db schema on the local machine. When EF creates the database I want to tell it not to create a foreign key on the vend_id column. How do I do that? Ideally I do not want to rename the property because there are several such instances in my db and it just makes it confusing.
Thank you,
Sam
You can't have a navigation property to a Vendor entity in your Item entity class if the Items table does not have a foreign key to table Vendor. If you did not specify a navigation property in entity class Item, EF would not infer that vend_id is a foreign key.
Update:
Unable to reproduce with the following:
[Table("EntityA")]
public partial class EntityA
{
public int Id { get; set; }
public Nullable<int> EntityBId { get; set; }
public string Description { get; set; }
[ForeignKey( "EntityBId" )]
public virtual EntityB EntityB { get; set; }
// this is not created as a FK
// nor does EntityCId cause a FK
public int EntityC_Id { get; set; }
}
[Table("EntityC")]
public class EntityC
{
public EntityC()
{
EntitiesD = new HashSet<EntityD>();
}
public int EntityCId { get; set; }
public string Name { get; set; }
public virtual ICollection<EntityD> EntitiesD { get; set; }
}
Using a table-per-type inheritance model and Entity Framework Code First, I am trying to eager load a list of derived class. Please note that I can't change the model.
I have the following model (overly simplified)
public class Training
{
public string Name { get; set; }
public IList<Person> Persons { get; set; }
}
public abstract class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
[Table("Students")]
public class Student : Person
{
public string StudentNumber { get; set; }
public IList<Training> Trainings { get; set; }
}
[Table("Instructors")]
public class Instructor : Person
{
public DateTime StartingDate { get; set; }
public IList<Training> Trainings { get; set; }
}
I want to query Training by name and eager load all the persons including the derived class (Student and Instructor). Back in April 2011, Tom Dykstra seemed to claim it wasn't possible.
The current version of the Entity Framework doesn't support eager loading for one-to-zero-or-one relationships when the navigation property is on the derived class of a TPH inheritance structure.
Has this changed? I am using EF5.
I don't see why ...
var list = context.Trainings.Include(t => t.Persons)
.Where(t => t.Name == someName)
.ToList();
... shouldn't work. EF should populate the Persons list with concrete Student and Instructor entities.
You neither have a "one-to-zero-or-one relationship" nor is your navigation property (Training.Persons) "on the derived class". So, I think the mentioned limitation does not apply to your model and query.
I tried to map some classes using Entity Framework in TPC style and got the following error:
Error: The type 'A' cannot be mapped as defined because it maps
inherited properties from types that use entity splitting or another
form of inheritance. Either choose a different inheritance mapping
strategy so as to not map inherited properties, or change all types in
the hierarchy to map inherited properties and to not use splitting.
This error occurs when I use the following classes:
public abstract class BaseEntityTest
public abstract class BaseEntityTest2 : BaseEntityTest
public abstract class BaseEntityTest3 : BaseEntityTest2
public class A: BaseEntityTest3 // this class is the only one with a table in the db
In the OnModelCreating method I added the following code to get the TPC mapping
modelBuilder.Entity<A>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("A");
});
When I exclude BaseEntityTest2 from the structure (so that A inherits only from BaseEntityTest instead of BaseEntityTest2) the error goes away. Does that mean that it is not possible to create this mapping or do I just miss something?
EDIT:
Properties of classes:
public abstract class BaseEntityTest
{
[Key]
public Guid Id { get; set; }
public String Info { get; set; }
[Required]
public DateTime CreationDate { get; set; }
[Required]
public String CreationUser { get; set; }
[Required]
public DateTime ModificationDate { get; set; }
[Required]
public String ModificationUser { get; set; }
[ConcurrencyCheck]
[Required]
public int LockVersion { get; internal set; }
}
public abstract class BaseEntityTest2 : BaseEntityTest
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public abstract class BaseEntityTest3: BaseEntityTest2
{
[Required]
public DateTime FromDate { get; set; }
public DateTime ThruDate { get; set; }
}
public class A: BaseEntityTest3{
public String Test { get; set; }
}
The error occurs for EF 4.3.1 and earlier versions, but not for EF 4.4 and EF 5.0. (EF 4.4 is actually EF 5.0, but with .NET 4.0 as target platform.)
BUT: The error occurs only if you are using your abstract classes as entities in your model, that means
you either have DbSets for them in your context class, like
public DbSet<BaseEntityTestX> BaseEntityTestXs { get; set; }
or you have some Fluent mapping for BaseEntityTestX, some modelBuilder.Entity<BaseEntityTestX>()... stuff
or you are using one of the BaseEntityTestX as a navigation property in another (concrete) entity type
Do you need any of this?
Having a DbSet<BaseEntityTestX> in your context would only make sense if you really want to query for one of the abstract entities, like:
List<BaseEntityTest> list = context.BaseEntityTests
.Where(b => b.Info == "abc").ToList();
The result is of course a list of concrete entities that inherit from BaseEntityTest, but it can be a mix of different types, like some As and some Bs. Do you need such queries? Or do you only want to query for some of the concrete objects:
List<A> list = context.As
.Where(b => b.Info == "abc").ToList();
In the latter case you don't need a DbSet for the abstract base classes and you don't need any inheritance mapping. You can just remove the DbSet<BaseEntityTestX> from your context class and remove the TPC mapping and your error will go away.
The last point - having a navigation property to one of the abstract entities in another entity - doesn't make sense with TPC mapping. It is just not mappable to a relational database because with TPC mapping there is no table for the abstract entity, hence there is no target the foreign key relationship could refer to from the table of the concrete class that has the navigation property.
The error will also disappear if you extend your TPC mapping to the base classes:
modelBuilder.Entity<BaseEntityTestX>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("BaseEntityTestX");
});
But it will create tables for those abstract entities that don't seem to make sense to me.
in EF6.0 its happed when
EntityTypeConfiguration'<'YourBaseClass'>'
did not detailed ALL your derived class with
this.Map<DerivedClass1>(m =>
{
m.MapInheritedProperties();
m.ToTable("..");
});
if just one dervied class in the assembley not configured like so
you get this exception
I've seen many EF POCO examples where each POCO class inherits a base Entity class or implements an IEntity interface.
I kind of understand why this is used, but I can't see that it will work in all situations, unless I'm missing something.
The Entity base class might look like this:
public class Entity
{
#region Primitive Properties
[Key]
public int Id { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
[Timestamp]
public byte[] rowversion { get; set; }
#endregion
}
... and the concrete POCO class would look like this:
public class BlogCategory : Entity
{
#region Properties
[Required(ErrorMessage = "Category Name is required.")]
public string CategoryName { get; set; }
public virtual ICollection<Blog> BlogList { get; set; }
#endregion
}
This is fine when all my classes contain a single Primary Key property, but what happens when I have a many-to-many relationship? Usually in a many-to-many relationship, the entity has dual properties that represent the Primary Key of this entity.
Such as:
public class ClaimQuestionAnswer : Entity <-- this will not work, will it?
{
[Key]
public int QuestionId { get; set; }
[Key]
public int AnswerId { get; set; }
public string Answer { get; set; }
public byte[] rowversion { get; set; }
}
Will this particular POCO not inherit the base class?
Any clarification is appreciated.
Thanks.
You might have seen only examples which just don't use any entity classes with composite key. Otherwise they had the same problem you are facing now.
The many-to-many relationship is not the best example because in a true many-to-many relationship the join table does not have a corresponding entity in your model. But you might have for any other reason a composite key in an entity, or you could have entities whose key simply need to have another type (string, long, Guid or whatever).
In this case you cannot use your base class because the key is not a common property anymore for all entities. You could move the key out of the base class and put it into the different derived classes - only DateCreated, DateModified and rowversion are common properties. Or you can create multiple base classes for the different key types you are using.
It all depends what common properties you want to support in all entities.
I have three entities (EntityA, EntityB, EntityC) in code and their respective tables (TableA, TableB, TableC) in the database. I also have an existing join table that has three ID columns(TableA_ID, TableB_ID, TableC_ID).
In code, the entities are related as follows:
MODELS:
public class EntityA
{
public Guid EntityA_ID { get; set }
.....
// Each EntityA can be associated with 0 or Many EntityB
public virtual ICollection<EntityB> EntityBCollection { get; set; }
}
public class EntityB
{
public Guid EntityB_ID { get; set; }
.....
// Each EntityB can be associated with 0 or Many EntityA
public virtual ICollection<EntityA> EntityACollection { get; set; }
// Each EntityB can be assocated with 0 or Many EntityC,
// but it becomes 0 or 1 when EntityB is associated with an EntityA
public virtual EntityC EntityC { get; set; }
}
public class EntityC
{
public Guid EntityC_ID { get; set; }
......
// Each EntityC an only be associated with a EntityB
// an EntityC does not exist on its own
public virtual EntityB EntityB { get; set; }
}
DATA CONTEXT:
modelBuilder.Entity<EntityB>()
.HasOptional(entityb => entityb.EntityC)
.WithRequired(entityc => entityc.EntityB)
.Map(map =>
{
map.ToTable("ThreeIDColumnJoinTable").MapKey(new string[]{"EntityA_ID", "EntityB_ID", "EntityC_ID"});
});
I keep on getting the following error:
Unable to determine the principal end of an association between the types 'EntityC' and 'EntityB'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Any ideas on how I can reconfigure the mapping in the DATA CONTEXT so it will not produce an error and it will also include the relationship of EntityA that is specified in the ThreeIDColumnJoinTable?
// Each EntityB can be assocated with 0 or Many EntityC, but it becomes 0 or 1 when EntityB is associated with an EntityA
In such case your EntityB has wrong navigation property. It should be:
public class EntityB
{
public Guid EntityB_ID { get; set; }
.....
// Each EntityB can be associated with 0 or Many EntityA
public virtual ICollection<EntityA> EntityACollection { get; set; }
public virtual ICollection<EntityC> EntityCCollection { get; set; }
}
You need collection of EntityC to support "Many" part. The second part of the rule cannot be enforced by database / model. It must be enforced by your application logic.
Rest of your model can be used as is. Remove that fluent mapping and you should get many-to-many relation between A and B and one-to-many relation between B and C. That is exactly what your rules states.
There is nothing like automatic many-to-many for three tables. If you need that (not your current case) you must map junction table as fourth entity and point navigation properties from other three entities to this new entity providing relational bridge.