Derived POCOs referencing same table - strange naming issues - c#

I have a "base" entity with some properties that's being used by a bunch of other stuff (repository patterns, queues, etc) in some shared libraries. Mapped to a pluralized table.
I need to add a property to it for my specific implementation, and I want to reuse all the rest of the normal behaviors.
I derive a class:
public interface IItem {
[Key]
Guid Id { get; set; }
string Name { get; set; }
}
public class Item : IItem {
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
}
public interface IExtended {
bool IsExtended { get; set; }
}
[Table("Items")] // <-- my nemesis
public class ExtendedItem : Item, IExtended {
[Column("_Extended")]
public bool IsExtended { get; set; }
}
I set up the code-first context:
public class MyContext : DbContext {
public MyContext(string connectionString) : base(connectionString) {
// manually creating the tables, no migrations
Database.SetInitializer<EfQueueContext>(null);
}
public DbSet<Item> Items { get; set; }
}
Without DataAnnotation [Table] I get exception "Invalid column name 'Discriminator'" -- okay, weird but makes sense
With [NotMapped] I get exception "The entity type ExtendedItem is not part of the model for the current context" -- okay, makes sense
With annotation [Table("Item"] I get exception "table 'dbo.Item' doesn't exist" -- okay, duh forgot it pluralized original
With annotation [Table("Items")] I get exception "table 'dbo.Items1' doesn't exist" -- what??? where did the 1 suffix come from?
Even creating a brand-new DbContext that only refers to ExtendedItem and not Item still adds the '1' (update - I didn't actually create a clean instance; see comment on answer)

The problem is EF deduce you want to create a Table per Type (TPT) when you apply the Table atribute on the subclasses to specify the mapped table name. In your case you are trying to rename the table related with theExtendedItem entity, but you are using the same name of the root table, that's way EF is creating two tables one called Items1 (because you already use the Items name in the inheriting entity) related to the Item entity and another called Items related to the ExtentedItem entity
If you want to create a Table per Hierarchy (TPH) and you want to rename the root table, then you need to apply the [Table("Items")] data annotation on the root entity (Item) and all entities that inherit from it.

Related

Invalid column name 'Discriminator' when having a derived class

I have a ASP.NET MVC + EF web application, but I get an error when trying to query a list of Product using LINQ:
Invalid column name 'Discriminator'. Invalid column name 'NewProductPhoto'
This happened after I added a derived class from one of my entities.
My entity class:
[Table("Product")]
public partial class Product
{
public Product()
{
ProductPhotos = new HashSet<ProductPhoto>();
SalesOrderItems = new HashSet<SalesOrderItem>();
}
// more attributes here
}
My new derived class:
public class ProductEx : Product
{
public byte[] NewProductPhoto { get; set; }
}
I understand that EF tries to distinguish between Product and ProductEx but the point is I have no intention to add ProductEx to EF model.
I don't know why it considers ProductEx as part of database model.
How can I tell EF to not consider ProductEx as database model.
I found that the same way I can exclude a property from entity framework persistent, I can exclude my ProductEx class from entity framework modeling.
The attribute [NotMapped] does the trick.
[NotMapped]
public class ProductEx : Product
{
public byte[] NewProductPhoto { get; set; }
}
I still don't understand why EF even considers my derived class for persistence.

EF Core defining "complex" relation

I'm having a hard time defining the relations I want using EF Core(1 to many) .
E.G:
I am an entity of Task and Employee , each task is given by an employee and also is appointed to an employee . I've created the Task class as follow:
public class Task
{
public int TaskId { get; set; }
[ForeignKey("RequestedBy")]
[Required]
public int RequestedById { get; set; }
[Required]
[ForeignKey("TaskedTo")]
public int TaskedToId { get; set; }
public virtual Employee RequestedBy { get; set; }
public virtual Employee TaskedTo { get; set; }
}
I think I've done it correctly, but I have a problem with my Employee class. Usually (When there's only one join) I would simply create virtual collection of Task Property in Employee .. but what am I supposed to do now ? Is this enough to set the relation or should I add virtual properties for these two tasks?
And another thing, when I have an owned entity, with 1-Many relation , is it enough to add the property in the owner entity, and do nothing in the owned one? Or do I have to specify the [Owned] Annotation?
The issue is that you have two one-to-many relationships between the same two entities. For the Employee class you would need two collections, one for each relationship. Additionally, you'll need to use the InverseProperty attribute to tell EF which foreign key goes with which collection:
public class Employee
{
...
[InverseProperty(nameof(Task.RequestedBy))]
public ICollection<Task> RequestedTasks { get; set; }
[InverseProperty(nameof(Task.TaskedTo))]
public ICollection<Task> AssignedTasks { get; set; }
}
You don't need virtual. That's to enable lazy-loading. For the lazy-loading functionality, EF creates a dynamic proxy class that inherits from your entity and overrides the getter on the navigation property. The virtual keyword is required in C# to allow a class member to be overridden.
Also, the Owned attribute is for value objects. It's a way of having a related class whose properties are literally mapped onto the same table or if given its own table, inherently tied to the entity that "owns" it, such that you access that data through the entity, not separately. Neither of which applies here.
Finally, you should reconsider the name Task for this class. .NET already has a Task class, and it's used very frequently. If you name your class Task as well, you'll be having to specify namespaces virtually every time you use either one, which is a pain.

Entity Framework Core Database First - Multiple Foreign keys to one table

Background Information
I am currently working with EF Core using a database first implementation.
Current tables
Fizz
{
[Id] INT
[Category] varchar
[Value] varchar
}
Buzz
{
[Id] UniqueIdentifier
[TypeId1] INT
[TypeId2] INT
CONSTRAINT [FK_Buzz_Fizz_1] FOREIGN KEY ([TypeId1] REFERENCES [Fizz][Id])
CONSTRAINT [FK_Buzz_Fizz_2] FOREIGN KEY ([TypeId2] REFERENCES [Fizz][Id])
}
Fizz currently acts a lookup table. Doing this allows for a single data repository to be used to find different values by category.
Buzz is a table that has two different type values to be stored e.g. TypeId1 could be brand which would exist in Fizz as (id, Brands, Nestle) and TypeId2 could be a flavor which would exist in Fizz as (id, Flavors, Grape).
The Issue
I scaffold the db to create the Data Models.
When running the application the following occurrs:
InvalidOperationException: Unable to determine the relationship represented by navigation property 'Buzz.TypeId1' of type 'Fizz'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
One solution that has occurred to me is to break this lookup table (Fizz) into multiple tables that way the references could be resolved by not having duplicate types used for Foreign Keys.
This would require re-work of the logic for the current data repository to either access multiple tables or be split into multiple data repos.
Another solution would be to modify the DBContext that is generated and use DataAnnotations on the DataModel. I would like to avoid doing this as the Context and Models will be regenerated in the future and these changes will be overwritten.
Is there a way to have a datamodel generated from a table that has multiple Foreign Keys to a single table without having to modify the generated code?
For posterity:
With the database approach a scaffold of the db is done to create the context and data models.
The data models generated (using the example tables above) look something like this -
public partial class Buzz
{
public Buzz()
{ }
public Guid Id { get; set; }
public int TypeId1 { get; set; }
public int TypeId2 { get; set; }
public Fizz TypeId1Fizz { get; set; }
public Fizz TypeId2Fizz { get; set; }
}
public partial class Fizz
{
public Fizz()
{ }
public int Id { get; set; }
public string Category { get; set; }
public string Value { get; set; }
public ICollection<Buzz> TypeId1Fizz { get; set; }
public ICollection<Buzz> TypeId2Fizz { get; set; }
}
The issue is that the relationship in Buzz could not be resolved.
The solution
When using scaffold on the database all models are generated as partials to a specified folder. I created a partial for the Buzz class in another directory that lives inside of the directory created by the scaffold (be sure that the namespaces match VS likes to add the directory name to the namespace and the partials won't be matched).
public partial class Buzz
{
[NotMapped]
public Fizz TypeId1Fizz { get; set; }
[NotMapped]
public Fizz TypeId2Fizz { get; set; }
}
but Leustherin then you lose the ability to utilize .Include for Fizz! EntityFramework won't create an SQL join statement for you so you will have to make an extra trip to the DB to obtain your look up value!
To get around this, override the Get or GetAll function of your data repository and create your own join statement.
Why I chose this solution
Maintainability.
Anytime the DataModels are regenerated instead of getting a runtime error there is now a compile error reminding the dev to delete the extra properties from the generated data model.
There is no other modification of automatically generated files.
There are no major schema changes done to accommodate the change.
I will do my best to keep this updated.

EF6 - TPH foreign key mapping in derived classes using base class property

I am using Entity Framework 6.0.2 with an existing database in which tags are stored in a single table that looks like this:
Id: int, primary key
TagType: string, determine the type of tag, either "usertag" or "movietag"
ItemId: int, contains the Id of the item to which is referred (either a User Id or a Movie Id)
The following classes describe this situation:
public class User
{
public int Id { get; set; }
}
public class Movie
{
public int Id { get; set; }
}
public abstract class Tag
{
public int Id { get; set; }
public int ItemId { get; set; }
}
public class UserTag : Tag
{
public virtual User User { get; set; }
}
public class MovieTag : Tag
{
public virtual Movie Movie { get; set; }
}
As you can see my derived classes have navigation properties, which are backed by the value of the ItemId property in the base class. My mapping is as follows:
public class Context : DbContext
{
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>()
.Map<UserTag>(m => m.Requires("TagType").HasValue("usertag"))
.Map<MovieTag>(m => m.Requires("TagType").HasValue("movietag"));
modelBuilder.Entity<UserTag>()
.HasRequired(m => m.User).WithMany().HasForeignKey(m => m.ItemId);
modelBuilder.Entity<MovieTag>()
.HasRequired(m => m.Movie).WithMany().HasForeignKey(m => m.ItemId);
}
}
Now when I try to use this mapping using the following code, I get an exception:
using System.Data.Entity;
class Program
{
static void Main()
{
using (var db = new Context())
{
db.Database.Delete();
db.Database.Initialize(false);
}
}
}
The exception that is thrown is:
Unhandled Exception: System.InvalidOperationException: The foreign key component 'ItemId' is not a declared property on type 'UserTag'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property
Yes the ItemId property is not declared on the type UserTag, but it is inherited from the base Tag class. To me it seems that this mapping should be possible. Is this a bug or a restriction in Entity Framework 6?
It is a restriction. EF is quite tightly bound to the way how relational database works. What you are trying to do in terms of the database is to put two foreign key constraints on single ItemId column. The foreign constraint in database is not conditional so the record will always use both constraints no matter of the tag type. That is not what you want because such definition will always require both user and movie with specific Id to exist for every single tag.
Think about it in different way. If it works the way how you are trying to define it there would be no reason why to have User and Movie navigation properties in child entities - it would be enough to have single navigation property in parent. The fact that you have to define them in child entities because they are different for each of them also means you need to have two different foreign keys.
You need to have separate UserId and MovieId in their specific tags.

EF Code First - How to prevent duplicate column on created table on Table-Per-Hierarchy Mapping

I have entity like below
public abstract class MyBaseClass
{
public int Id { get; set; }
}
public class MyConcrete : MyBaseClass
{
public int TemplateName { get; set; }
public int Total { get; set; }
}
public class MyOtherConcrete : MyBaseClass
{
public int TemplateName { get; set; }
public int MyProperty { get; set; }
public string OtherProperty { get; set; }
}
using default initialization, EF will make table with columns like bellow:
Id
TemplateName
TemplateName1 // <-- this is the problem
Total
MyPorperty
MyOtherProperty
now my question how to configure EF so all the TemplateName property on derived class automatically mapped into TemplateName column without making another column. is it possible to configure it on OnModelCreating method?
EDIT
actually above was simplified version of my problem. i have 10 more entities some property might duplicated everywhere and i don't want to add any abstraction layers.
i have tried manually map the column on the OnModelCreating but having "Each property name in a type must be unique. Property name 'TemplateName' was already defined" exception any idea?
EDIT 2
so, i found here, that said it is impossible to do such thing like above in EF, it is weird for me..
Move TemplateName into MyBaseClass to avoid this problem.
If necessary, you can use intermediate base classes to hold properties shared by only a subset of your classes.
After searching through the net, so far that was not possible to do that. since i realize that my inheritance tree is wrong.
so in my case, i should change my code to match the EF requirement, it is sound weird.. because in many case ENTITY is a NO NO to change, we usually create an entity that used in multiple project. event we found our entity wrong we won't update it because updating it will require massive change on the other projects.
so far, i think there is no exact answer for my question. will update it soon after EF support it.

Categories

Resources