Why use Base class on POCO's with EF Repository Pattern? - entity-framework

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.

Related

How to change foreign key suffix in Entity Framework Core?

In EF Core with a code-first approach, by default column referencing another entity has an Id suffix - for example PersonId.
Is it possible - and if so, how? - to change it to _id, so to person_id?
Create the foreign key explicitly under the name you want - in your case Parent_Id. Keep a navigation property and foreign key property.
public int Parent_ID { get; set; }
public virtual Parent Parent { get; set; }
Map the foreign key relations using .HasForeignKey(). Something similar as below
builder.HasOne(d => d.Prop)
.WithMany(p => p.NavigationProp)
.HasForeignKey(d => d.ForeignKeyProp)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_ConstraintName");
If you prefer data annotation, you could also use
[Column("Parent_ID")]
public int ParentID { get; set; }
To add to WisdomSeeker's answer, you can use a [ForeignKey] annotation to point at a shadow property for the FK.
Given a class like a Course with a Person reference for a Teacher:
public class Course
{
[Key]
public int Id {get; set;}
// other fields.
[ForeignKey("person_id")]
public virtual Person Teacher { get; set; }
}
Alternatives as above would be:
[ForeignKey("Teacher")]
public int person_id { get; set; } // Not recommended naming convention in code.
public virtual Person Teacher { get; set; }
or
[Column("person_id"), ForeignKey("Teacher")]
public int TeacherId { get; set; }
public virtual Person Teacher { get; set; }
I generally avoid adding FK fields into classes as this leads to two sources of truth for what Teacher is assigned to a course. You have course.TeacherId and course.Teacher.Id, which could differ on update prior and after a SaveChanges. Shadow properties help avoid confusion and keep data updates consistent.
Using [Column] is common in Db-First implementations where you want to use a C# naming convention for properties to use in-code, but abide by existing/desired DB naming conventions in the database. I don't generally recommend using DB naming conventions in C# classes.

.NET Core Entity Framework linking subtable to property

This is an existing .NET Core 3.1 project I inherited.
I have a class referring to a database table
public class SupportContract
{
public int Id { get; set; }
public DateTime StartDate { get; set; }
public int SupportContractStatusId { get; set; }
public virtual SupportContractStatus SupportContractStatus { get; set; }
}
and a sub table with a foreign key
public class SupportContractStatus
{
public int Id { get; set; }
public string SupportContractStatusName { get; set; }
}
This works fine I can get
supportContract.SupportContractStatus.SupportContractStatusName
But if I rename SupportContractStatusId to ContractStatusId in C# and the database, I get an error "SupportContractStatusId missing".
I cannot find any link between the column SupportContractStatusId and table SupportContractStatus anywhere in code nor is there any mention of the foreign key.
There is no link in the DbContext either.
Is this naming convention assumed by Entity Framework? How does the framework know of the foreign key?
Yes, the naming convention that EF expects by default is based on the class name, not the property name. It will look for ClassNameId or ClassName_Id. You can link the FK either through annotation or configuration.
I.e.
public int ContractStatusId { get; set; }
[ForeignKey("ContractStatusId")]
public virtual SupportContractStatus ContractStatus { get; set; }
Configuration is done through IEntityTypeConfiguration implementations or by implementing the OnModelCreating method in the DbContext and configuring the relationship within the modelBuilder. For occasional deviations from convention, the attribute approach can generally cover everything.

Relate Multiple Tables to Single General Purpose Table in Entity Framework Code First

Many times I have a general purpose entity that other entities contain a collection of. I don't want to have a new collection entity for each parent entity type that needs it but would like to re-use a single general purpose entity. For performance reasons, I also don't want to explicitly define many-to-many relationships as in this answer. The simplest example would be a collection of strings.
public class MyString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyString> { get; set; }
}
public class MyOtherString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherString> { get; set; }
}
I'd really like to combine MyString and MyOtherString into a single entity:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
Except now I'm going to have an additional foreign key in GeneralPurposeString for every entity that contains a collection of GeneralPurposeString.
What I would like would be a way to have an additional parent category column on the GeneralPurposeString table (but not the entity) that would specify which entity the item belongs to. I use Guid for primary keys, so the tables could look something like this:
CREATE TABLE [GeneralPurposeString]
(
[Id] uniqueidentifier NOT NULL
CONSTRAINT PK_GeneralPurposeString PRIMARY KEY,
[ParentEntityCategory] uniqueidentifier NOT NULL,
[ParentEntityId] uniqueidentifier NOT NULL,
[Value] nvarchar(MAX)
)
And some how in Code First to specify that MyEntity has a certain category, and that it's collection of GeneralPurposeString uses that category, and MyOtherEntity uses another category (Guid) for it's collections of GeneralPurposeString.
The key would be that GeneralPurposeString could be a collection in any other entity and that loading the parent entity and including the collection would automatically load without having to explicitly specify the category.
The purposes for all of this are
Allow .NET code to have GeneralPurposeString code that wasn't replicated everywhere (actual utility or business logic code). This can probably also be accomplished through inheritance and explicit mapping but that would still leave multiple tables in the database (see #2).
Have only one table in the database for GeneralPurposeString. This is more of a tidiness issue. Performance would possibly be better with multiple tables, but indexing on ParentEntityCategory/ParentEntityId and covering Value should be good performance for lookups.
Not have to explicitly code this relationship and the lookups everywhere it's needed.
I'm thinking if I can get over #2 and be OK with a separate table behind the scenes and implementing a derived class, that will be the simplest route to go.
So just:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
// It's just a GeneralPurposeString with a fancy MyEntity membership pin
public class MyEntityString: GeneralPurposeString {}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyEntityString> Strings { get; set; }
}
// Cool GeneralPurposeStrings belong to MyOtherEntity
public class MyOtherEntityString: GeneralPurposeString {}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherEntityString> Strings { get; set; }
}
public class MyContext: DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public DbSet<MyOtherEntity> MyOtherEntities { get; set; }
}
I don't have to add the derived classes to the DbContext and the tables get named with the plural of the derived class by default, so it's actually pretty straight forward.
My previous train of thought with the Parent Category would require additional coding/annotation even if EF supported it. This uses purely convention and nothing extra needed in annotations or in OnModelCreating().
I'm not seeing any harm in extra tables at this point in time. I don't see a need (currently) to have all of the data in one table for reporting, but that really depends on the type of general purpose entity, so I may need to revisit this in the future, or I may just take the many-to-many route if I do need the data in one table.
And I can still have:
public static class GeneralPurposeStringExtensions
{
public static void SassThatHoopyFrood(this GeneralPurposeString s)
{
// do stuff
}
}

Multiple inheritance with Entity Framework TPC

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

Entity Framework code first FK field

I have two classes:
public class Fighter
{
public int FighterID { get; set; }
public int DivsionID { get; set; }
public string Name { get; set; }
//...
public virtual Division Division { get; set; }
}
public class Division
{
public int DivisionID { get; set; }
public string Name { get; set; }
public int? FromWeight { get; set; }
public int? ToWeight { get; set; }
public ICollection<Fighter> Fighters { get; set; }
}
Why do I have Division_DivisionID on my Fighters table ? I thought the DevisionID should be the FK.
I wrote an article on how this works, take a look at http://blog.staticvoid.co.nz/2012/07/entity-framework-navigation-property.html - See How does Entity Framework detect Navigation Properties
In short this is due to a convention which says FKs are named
<LocalPropertyName>_<ForeignIdPropertyName>
Also see Entity Framework Navigation Property generation rules
to make EF name the FK DivisionID, add the following to your modelbuilder
modelBuilder.Entity<Fighter>()
.HasRequired(f => f.Division)
.WithMany(d => d.Fighters)
.HasForeignKey(f => f.DivisionID);
You are mixing the EF FK Association concept with your database FK concept, they are not the same. The FK Association concept on EF was introduced so you could do things like lookups and data binding more easily (like DropDownList data binding for example).
The ER FK concept that is created in your table is a mapping for the composition you have on the Fighter class, in this case the Division property. The naming of that table column follows EF's rules.
For more on the EF FK Association read this article.