I am using EF CF approach for a website with MySQL.
For some reason EF creates a column in my Post table called "Discriminator" and contains the VARCHAR "Post".
Why is this column created? Can I do something to avoid it being created? Are there any advantages of having this column?
The Discriminator column is used and required in Table-Per-Hierarchy inheritance scenarios. If you for example have a model like this ...
public abstract class BaseEntity
{
public int Id { get; set; }
//...
}
public class Post : BaseEntity
{
//...
}
public class OtherEntity : BaseEntity
{
//...
}
... and make the BaseEntity part of the model, for instance by adding a DbSet<BaseEntity> to your derived context, Entity Framework will map this class hierarchy by default into a single table, but introduce a special column - the Discriminator - to distinguish between the different types (Post or OtherEntity) stored in this table. This column gets populated with the name of the type (again Post or OtherEntity).
You can stop the column being created by adding the [NotMapped] data annotation to the models that are inheriting from your base class. This will tell EF not to add your class to future migrations, removing the discriminator column.
public class BaseClass
{
}
[NotMapped]
public class InheritingClass : BaseClass
{
}
For completeness, if you want to use the fluent API to stop the inheriting class from being mapped with entity (and therefore stopping the discriminator column being created) you can do the following:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<InheritingClass>();
}
Related
I've having difficulties getting EF code-first to generate a database from the following entities:
public class Person : Animal
{
public int Id { get; set; }
public Animal Pet { get; set; }
}
public class Animal
{
public string Name { get; set; }
}
So conceptually a person is an animal with a name, and they have a pet that is also an animal with a name. My context contains an array of people but not animals, which is why Animal doesn't contain a key:
public DbSet<Person> People { get; set; }
If I try to create a database using code-first I get the following error:
System.Data.Entity.ModelConfiguration.ModelValidationException: One or more validation errors were detected during model generation:
MyProject.Database.Animal: : EntityType 'Animal' has no key defined. Define the key for this EntityType.
Animals: EntityType: EntitySet 'Animals' is based on type 'Animal' that has no keys defined.
If I remove the Pet field I get a table with Id and Name fields, which is my expected behavior. Similarly, if I remove the Animal inheritance I get a table with Id and Pet_Name fields, which is again my expected behavior. What I'm trying to get is a table with Id, Name and Pet_Name fields.
Can't help but feel I'm missing something very basic here, because I've done this on other ORMs without issue. Can anyone tell me how to do this with EF 6.2?
For anyone else that reads this in future EF treats classes as either Entities or Complex Types. Entities get their own table while complex types get their own fields added as fields to the classes of parents that contain them as properties. If you declare a class instance as a property of another then EF immediately assumes it's an Entity; if it sees you trying to use it as a base class in an inheritance hierarchy then it assumes it's a complex type. The error shown above occurs when EF has already erroneously assumed that the type is an Entity but you then try to use it as a complex type. Seems to me that EF shouldn't be making the assumption in the first place if the class has no key property, but there it is. The solution is to simply flag it as a complex type from the start in your OnModelCreating function:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Animal>();
base.OnModelCreating(modelBuilder);
}
i've a model like that
public class Class1 {
public int identifier {get;set;}
}
public class Class2 {
public int identifier {get;set;}
public List<Class1> holders {get;set;}
public List<Class1> users{get;set;}
}
my problem is the generated foreign keys in Class1 name are "Class2_identifier" and "Class2_identifier1" mean while what i want is "Class2_holders_identifier" and "Class2_users_identifier"
the real model is really huge so what i'm looking for is away to override how the names are generated in the "add-migration" step
Not a complete implementation, just a hint: If you are using EntityFramework 6 you can define a custom model convention:
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{
public void Apply(AssociationType association, DbModel model)
{
if (association.IsForeignKey)
{
var constraint = association.Constraint;
// Implement your renaming code.
// The data you need is inside association.Constraint.
}
}
}
And add it to your DbContext.OnModelCreating:
modelBuilder.Conventions.Add(new ForeignKeyNamingConvention());
This answer contains some code that you can reuse (in this case the convention is used to remove underscores in the column names).
Edit: OP included their final solution here:
The problem as mentioned in ef core "it's the same problem in ef6 but with no message" console
There are multiple relationships between 'Class1' and 'Class2' without configured foreign key properties causing EF to create shadow properties on 'Organization' with names dependent on the discovery order.
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{
public void Apply(AssociationType association, DbModel model)
{
if (association.IsForeignKey)
{
var constraint = association.Constraint;
// as i just needed the fk column name to be more clear
// "{entityName}_{propertyName}" which is provided in
// {association.Name}
association.Constraint.ToProperties[0].Name = association.Name;
}
}
}
I have a base class
public class BaseClass
{
public int Id {get; set;}
}
and two derived classes
public class Foobar: BaseClass
{
[Required]
public int Whatever {get; set;}
}
public class Snafu: BaseClass
{
[Required]
public int Whatever {get; set;}
}
I'm using Table Per Hierarchy inheritance and trying to cut down on my duplicate columns, so with Fluent API I've mapped them like so:
modelBuilder.Entity<Foobar>().Property(fb => fb.Whatever).HasColumnName("Whatever");
modelBuilder.Entity<Snafu>().Property(sf => sf.Whatever).HasColumnName("Whatever");
However, this results in
(137,10) : error 3023: Problem in mapping fragments starting at line 137:Column BaseClass.Whatever in table BaseClass must be mapped: It has no default value and is not nullable.
In EF6 this type of mapping seems to work fine if I take off the [Required] attribute from both subclasses. Adding a [DefaultValue(0)] attribute to both derived classes does not fix the problem.
Any idea how to get these properties to share a column in the database while maintaining their required attribute?
This is actually a bug in EF6. In EF5 the scenario used not to work at all (we would throw an exception in the lines of "column names need to be unique"). While in EF6 we did some work to enable it, but apparently we missed the fact that the shared column has to be nullable in the database even if the property is required in the derived types. The latter is because unless the base class is abstract, you need to be able to store an instance of the base type and for any instance of the base type the column should be null.
I have filed the issue in our bug database:
https://entityframework.codeplex.com/workitem/1924
Feel free to vote for it.
As for a workaround, if having an intermediary type is not an option, you can mark the column as nullable explicitly appending a call to .IsOptional() on the entity configurations. This won't give you exactly what you want because for the purpose of EF data validation this call to IsOptional() on the fluent API will override the [Required] data annotation. However, other flavors of data validation, such as MVC's validation will still honor the attribute.
There are other possible workarounds that I haven't tried, maybe if it is acceptable to use TPT and have both derived types have Whatever live in a different table this would work. I believe any approach that relies on setting a default value won't help because the bug is not only about the table schema not being able to hold an instance of the base class, it is also about the EF mapping generated by Code First not being valid.
UPDATE: This will be fixed in Entity Framework version 6.1.0 which is currently available in beta.
Introducing another type, which contains the required property shared by the other two accomplishes what you're looking for. The entities then look this:
public class BaseClass
{
public int Id { get; set; }
}
public abstract class BaseIntermediaryClass : BaseClass
{
[Required]
public int Whatever { get; set; }
}
public class Foobar : BaseIntermediaryClass
{
}
public class Snafu : BaseIntermediaryClass
{
}
And the mappings like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseIntermediaryClass>().Property(fb => fb.Whatever).HasColumnName("Whatever");
base.OnModelCreating(modelBuilder);
}
Full code of working example can be found here: https://gist.github.com/trayburn/7923392
I am using entity framework code first, I have 2 entities declared like so:
[Table("BaseTable")]
public class BaseEntity
{
public string SomeProperty{get; set;}
}
[Table("DerivedTable")]
public class DerivedEntity
: BaseEntity
{
public string SomeOtherProperty {get; set;}
}
The problem is when I call the generic set method on the dbcontext it always returns the entities as DerivedEntity types. (EF is setting up the correct table mapping in the database just not returing the expected entity type from the set() method)
DbContext.Set<BaseEntity>();
How can I force Set<BaseEntity>(); to return the entities as type BaseEntity so I can only update that table?
You cannot. EF works on entity level not on table level. So if your entity is of type DerivedEntity it will never be loaded as BaseEntity only. When you modify attached entity EF will build update command only for modified columns so it should not modify your second table if you are only changing property from BaseEntity table.
I'm trying to set up a TPC inheritance using Code First to model incoming and outgoing messages and the records therein.
The base type, SentRecord, is concrete and its derived type, ReceivedRecord, is also concrete and inherits from SentRecord and adds a few extra fields in order to record return codes. Something like this, but with more properties:
public class SentRecord : RecordBase {
public int Id { get; set; }
public string FooField { get; set; }
}
public class ReceivedRecord : SentRecord {
public int ReturnCode { get; set; }
public SentRecord SentRecord { get; set; }
}
The current model is TPH and as a result the tables get a descriminator column to identify the type of object that was persisted. It works, but I'd prefer both objects to be stored in separate tables, without the need of the discriminator column. The table SentRecord would only have the columns Id and FooField and the table ReceivedRecord would have Id, FooField, ReturnCode and an FK to SentRecord.
I currently have the following in my DataContext class:
public class Context : DContext {
public DbSet<SentRecord> SentRecords { get; set; }
public DbSet<ReceivedRecord> ReceivedRecords { get; set; }
}
And I have the following configuration for the ReceivedRecord:
public class ReceivedRecord_Configuration : EntityTypeConfiguration<ReceivedRecord>{
public ReceivedRecord_Configuration() {
this.Map(m => {
m.MapInheritedProperties();
m.ToTable("ReceivedRecords");
});
}
}
And the following for SentRecord:
public class SentRecord_Configuration : EntityTypeConfiguration<SentRecord>{
public SentRecord_Configuration() {
this.Map(m => {
m.MapInheritedProperties(); //In order to map the properties declared in RecordBase
m.ToTable("SentRecords");
});
}
}
But once I run this, I get the following error when EF is trying to initialize my database:
Problem in mapping fragments starting at lines 455, 1284:
An entity from one EntitySet is mapped to a row that is also mapped to an entity from another EntitySet with possibly different key.
Ensure these two mapping fragments do not map two unrelated EntitySets to two overlapping groups of rows.
I'm not sure what to do in order to set this up in the TPC way I described above? Or should I stick with TPH which works?
Thanks in advance!
Okay, I got it up and running. Truth be told, the example I gave was a little bit less complex than the actual classes and inheritance hierarchy I was working with. That hierarchy contained a lot of abstract classes and concrete classes from which other classes inherited.
'Flattening' the hierarchy by getting cutting down on inheritance made it work smooth and without any errors whatsoever. Response messages aren't inherited from the sent messages anymore.
Short version: Don't make complex inheritance trees with mixed concrete and abstract base types when trying to use code-first database models. It'll only make it more complex to persist.