Make EF treat inheritance hierarchy as completely separate entities - entity-framework

Entity Framework 6 by default when it meets inheritance creates a special entity hierarchy with either TPH, TPT or TPC. But I would like EF to treat my classes as completely separate entities.
I have the following classes hierarchy where each class is mapped to a View in DB:
[Table("v_Item")]
class Item
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
}
[Table("v_ItemWithDescription")]
class ItemWithDescription : Item
{
public string Description { get; set; }
}
This design makes it possible to get more detailed info when needed. And it is so DRY. It is also nice to cast IQueryable:
IQueryable<ItemWithDescription> query = ...;
((IQueryable<Item>) query).Where(i => i.Name == "Foo")
But EF insists on adding discriminator column and badly distorts the queries. And there seems to be no way to make EF just forget that these classes have inheritance hierarchy!
Adding discriminator column to views and switching to TPC does not help as there appear lots of UNIONs in a query. It seems that my only option is to modify EF source code if I want to stick to my inherited approach. Are there any simpler solutions?
Thanks in advance!

If you don't map the base entity, it ignores the inheritance. So here's a trick you can do:
Create a base class for your Item class and pull all members up. Let's call it ItemBase. Do not map this class, and do not add it to your DbContext.
Then Item will be a mapped class without any properties of its own (will inherit everything from the base class). Make the rest of the classes (like ItemWithDescription) extend the ItemBase too.
This way, you'll have the code re-use, but lose the inheritance relationship between Item and its children, which depending on your case, may or may not be acceptable.

Related

Entity Framework creating table hierarchy when I don't want to [duplicate]

This question already has an answer here:
Entity Framework Core 2.0: How to configure abstract base class once
(1 answer)
Closed last year.
I have two classes SomeClass and SomeDerivedClass:
public abstract class SomeClass
{
int Id { get; set; }
public string BaseField { get; set; }
}
public class SomeOtherClass : SomeClass
{
public string SomeField { get; set; }
}
However, no matter what I try, Entity Framework tries to model the hierarchy in the database. I don't want SomeClass to be considered at all in the schema, I just want SomeOtherClass to act as its own entirely separate entity.
I want a single table called SomeOtherClass with columns Id, BaseField, and SomeField. However, Entity Framework always tries to create a table called SomeClass with a discriminator column, or one table for each.
List of things I have tried:
Calling .ToTable("SomeOtherClass") for both entities in OnModelCreating. Still adds a discriminator column. Tried adding HasNoDiscriminator(), but then I get the error
All the entity types in a hierarchy that don't have a discriminator must be mapped to different tables`
Calling .ToView(null) on SomeClass and .ToTable("SomeOtherClass") on SomeOtherClass. In this case the table loses the base columns (BaseField)
Calling .HasBaseType<SomeClass> .HasBaseType<SomeOtherClass > on both, but then I get an error about invalid hierarchy.
Making the base class abstract seems to have no effect.
How can I inherit a class in Entity Framework without it trying to make a hierarchy in the schema?
Found the answer in https://stackoverflow.com/a/49997115/472966
I had another entity referring to SomeClass directly which made EF Core think it was an entity rather than just a class.

Entity Framework Core does not see all subtype fields

Hello i am trying to use derived classes using EF Core using a single concrete table containing the reunion of the derived types.
public abstract A
{
public int Id{get;set;}
}
public B:A
{
public string Name{get;set;}
}
public C:A
{
public bool IsMan{get;set;}
}
public class MyContext:DBContext
{
public DBSet<A> ATable {get;set;}
}
When the database gets generated the Table ATable does not contain the fields of the C subtype.The resulting table contains only A-s fields and B-s.
Generated table
Id
Name
P.S Do i need to use some kind of discriminator ? I just want one table that has the reunion of the two subtypes , and to be able to access it using OfType Linq extension.
What you are describing is called TPH (Table per Hierarchy) inheritance strategy and currently is the only supported pattern by EF Core.
However, unlike EF6 EF Core does not automatically detect the derived entities by reflecting the model assembly. This was causing unexpected side effects in EF6, hence has been removed and now you are required to explicitly include derived types in the model (if not included by other mechanisms like navigation properties or explicit DbSets). This is explained in Entity type hierarchy mapping section of EF Core documentation:
By convention, EF will not automatically scan for base or derived types; this means that if you want a CLR type in your hierarchy to be mapped, you must explicitly specify that type on your model. For example, specifying only the base type of a hierarchy will not cause EF Core to implicitly include all of its sub-types.
So, the minimum you need to get TPH with your sample model is either adding
public DbSet<B> Bs { get; set; }
public DbSet<C> Cs { get; set; }
or Entity fluent API:
modelBuilder.Entity<B>();
modelBuilder.Entity<C>();

Entity Framework - Same field and column name in base and derived classes

I am using EF 4.3 and am adding Audit fields to my classes and tables. I have a service layer which is getting the credentials of the client applications by using the OperationContext, so I am passing that information to my tables through EF mappings. An example of this would be:
class A
{
string CreatedByUser { get; set; }
}
class B : A
{
}
I am using the fluent interface to provide my POCO to table mappings -- when I map the CreatedByUser column in the base and derived class, the derived class mappings do not take effect and the information is not passed to the database.
I have gotten around this by creating fields in my base class for the derived classes to use that are just pass-throughs of the audit columns but this is messy.
Try making class A abstract, I think that will give you the effect you are looking for(add the columns from class A to all class/tables that inherit it)

EF: Declaring Many-to-Many relationship in attributes

Many to many relationships can be declared like this in fluent-API.
modelBuilder.Entity<Course>()
.HasMany(t => t.Instructors)
.WithMany()
I would rather if there were certain attributes that could do the same in my domain model.
Are there such attributes somewhere in the framework, or can I make some of my own that can affect EF behavior when it generates the database?
I've assumed your model, try below and see if it works.
public class Course
{
[InverseProperty("Courses")] //add this attribute
public ICollection<Instructor> Instructors { get; set; }
//other properties..
}
public class Instructor
{
[InverseProperty("Instructors")] //add this attribute
public ICollection<Course> Courses { get; set; }
//other properties..
}
that way you tell entity framework to look for what property to map with in the Course model for Instructor.
Moreover, you don't even need to define that way. but In case you have more than one property of type Course in Instructor or vice-versa, you will need to point out correctly which is mapped to what.
Still, using fluent API is much better and scalable and manageable
You don't need any attributes for it. If you declare ICollection<T> in each entity, EF will create many to many relationships by convention. See here.

How to Map an Entity to multiple tables and have also navigation properties?

I have and EMDX created with Database first, i have followed the sample described here, so now i can map one entity to multiple tables in my database, and this is working fine.
But i need also to have the navigation properties that the FKs provide on the tables on this one entity, how could i achieve that?
I have two tables, on named Carteira and another named Ativo, in my EMDX i want to have the properties of Ativo also to be on Carteira, because they have an 0:1 relationship, so i have done this already and it is working! :) but i also want the navigation properties that are in the Ativo class to be on Carteira
Please see a sample describing the properties :
class Ativo
{
public string Id { get; set; }
public IList<Valor> Valores { get; set; }
}
class Carteira
{
public string AtivoID { get; set; }
public Ativo Ativo {get; set;}
}
So i would like to have something like
Carteira.Valores (and this should be infereed from Ativo).
Please note that i dont want to make this code manually using a partial class and acessing the navitation, i have already done this and i dont like this approach, it looks like this:
public partial class Carteira
{
public List<Valor> Valores
{
get
{
if (this.Ativo == null)
return null;
return this.Ativo.Valores;
}
}
}
I would like to know how to achieve this using the EMDX only, the same way that can be done for simple properties, is this possible?
For 0:1 to 1 relation,EF requires Foreign key to be same as Primary key on dependent table. So Ativo class must have Primary key of same name as AtivoID and that should be foreign key as well.
I managed to get the navigations working in 2 differente ways:
1 - Creating an 'Association' at the model (EMDX).
The associations work as if they were actual FKs, and since i already have the mapping collumns on both sides, it can be done by performing those steps:
Create a new Association at Carteira (second level class) that reflects the same properties of the FK present at Ativo (first level). When this is done, the navigation properties are created on both classes
2- Using Table-Per-Type Inheritance
In this specific scenario, the class could actually inherit from the other, and then take use of all of the navigations, for this to work i had to implement a TBT inheritance, altering the tables and adding "Conditional Mapping" to both tables (the concrete and the inherited) at the model (EDMX)