Inheritence in Entity Framework Code First - c#

I have already created a database using EF Code First. I have implemented a TPH in my model classes. I mean, there is a Person class which is the parent of the Customer class. When I attempted to create the DB at first, only one table named Persons has created that includes a column named Discriminator, just as expected.
Now I am wondering how that column is disappeared!!I've recreated the DB but still no Discriminator filed is there! Does it make any trouble if I make the column directly in the SQL Server? What is the best solution for that?
I really don't know why it disappeared!!!
Thanks in advance

make discriminator a public property get/set if want to see it on the DB
public discriminator {get; set;} // consider virtual if want change tracking proxy

Related

How to get all previous data by using select method in EF Core

I know that my question is a bit confused, But let me explain in detail.
Suppose that I have person class like this.
public class Person {
public int Id {get; set;}
public string Name {get; set;}
}
and I want create a new entity, but these two classes are similarly so I would like to just inherit and add some new properties
public class Employee : Person {
public string Position {get; set;}
}
everything works fine, but I have a problem when I want to select the data from person table and add it to Employee class like this
employee = _context.Person.Select(
a => new Employee {
Name = a.Name,
Position = "Programmer"
}).ToList();
So as you can see here, I want to add the position property, but also want the previous data from person table. The problem is, I have to type the previous data from person table manually. If the person table has a lot of properties I need to type all of that to get all data. Is there anyway to get previous data without typing all of them. So in javascript it have something like
new State = {
...State,
Position : "employee"
}
Is it possible to do something like this in c#?
Having employee as an entity, you can use
var employees = _context.Employee.Include(e=>e.Person).ToList();
then you'll do it like this employees[0].Person.Name and so on.
If I understand you, you essentially want to "upgrade" an existing Person entity to an Employee entity. Unfortunately, this is not as simple or straight-forward as you would like. EF Core models inheritance via a single table with a discriminator column. That discriminator column informs what class type should actually be instantiated, when the entity is pulled from the database. Since it was saved as a Person, it will have "Person" as the value there, and EF Core will instantiate a Person instance when you retrieve it from the database.
You can then potentially downcast it to Employee, but EF Core isn't going to know what to do with this. Technically, the downcast instance will be untracked. If you attempt to track it, you'll get an error on saving as EF Core will attempt to add a new Employee with the same PK as an existing Person. Boom.
Your best bet is to map over the data from the Person instance to a new Employee instance, including all associated relationships. Then, create the Employee, causing the relationships to be moved at the database-level, before finally removing the old Person.
This will of course result in a new PK being assigned (which is why it's important to migrated the related entities), and that could potentially be problematic if you've got URLs referencing that PK or if you're simply dealing with an auto-increment PK. You'll end up leaving gaps in the keys, and could potentially even run out of keys, if you do this enough.
The only other potential alternative is to change the value of the discriminator column. This would have to be done via straight SQL, as the discriminator column is a shadow property not exposed by EF Core (i.e. there's no way to modify it via C# code). However, if you literally change the value to something like "Employee", then when you fetch it, EF will create an Employee instance, just will all the Employee-specific properties null or default. However, at that point, you can make the necessary updates and save it back.

Creating entity in database with unmapped property

The entity model I'm working on is structured with inheritence as per:
public abstract class Line {}
public class WooLine : Line{
public bool wooProperty{ get; set; }
}
public class BooLine : Line
These are both stored in the database in the table Line. And in the database the column wooProperty is NOT NULL and default value (0).
These are maintained in a web app written with Knockout & Breeze. When working with BooLine trying to create a new entity, it throws an exception that I can't insert NULL into column wooProperty.
I set up a profile to trace the query, and it appears that since it's mapped to the Line table, during the Insert EntityFramework reads up all the properties and tries to actually insert NULL into the wooProperty, since it's not present in the Boo model. I'm moderately upset that EF is actively trying to insert NULL to a property I'm not working with...
Anyway. I can't move the wooProperty to the Line model - it belongs in the WooLine model. I'm hoping to solve it by either modifying the metadata in Breeze or forcing the wooProperty onto the saveChanges data. But I can't get breeze to recognize the property in the metadata. I've tried to run
metadataStore.registerEntityTypeCtor(
'BooLine', function () {
this.wooProperty = false;
});
Which almost works - but Breeze maps it as __unmapped value and as such isn't recognized after being recieved by EntityFramework.
I also started playing around with overriding the EFContextProvider and overriding BeforeSaveEntity. Entity is ReadOnly of type BooLine, and I can clearly see WooProperty in the UnmappedProperties, but I have no idea where to go from there... Any ideas?
TLDR in a way; Want to 'trick' entity framework into thinking an unmapped value is mapped when creating an entity.
To summarize my comments I would recommend one of the following:
make your model use TPT inheritance so there is no wooProperty column in the Line table, but in the inherited WooLine table
change your wooProperty column to be nullable and mark the wooProperty property in your entity class as [Required] and let EF take care of reading only "valid WooLines" - this should work if there is also a valid discriminator column for EF to use

How do I get an arbitrary entity stored in the context with Entity Framework?

I am using Entity Framework. I just need to get an arbitrary entity from the context so I can use reflection to look at its properties. I don't need any specific entity, any arbitrary one will do. My call should look like this:
Entity oldEntity = (Entity)_context.Set(newEntity.GetType()).
And some method should go after the last . but I don't see any that would work (find is my best bet but I can't use it since I don't know the ID's of any of the entities).
Edit:
I forgot to mention that the newEntity I am passing in is generated with a CodeDom class, so newEntity will be the same as the entities in the context, but it may have different properties, which is why I have to get an example "old" entity to compare its properties. To clarify this, imagine I have stored a Person entity with the properties:
First name
Last name
Then I want to add a new property, let's say Age. The way I'm doing this is by generating a new Person entity class with CodeDom with Age as an additional property. I have updated the entity class on disk, so I can create a new entity with the extra field, however my SQL table isn't updated.
You don't need an actual instance of a class to look at its properties.
You can just use typeof(...) like typeof(Entity).GetProperties()

How to create Discriminator column in Database First

I have a Database First approach to my application and Entity Framework. I have a base class that many other classes inherit. I am trying to save the class to my EF database, however, I keep getting an error that there is no Discriminator column.
System.Data.SqlClient.SqlException: Invalid column name 'Discriminator'.
Since I am using Database First, how do I manually create this column in my table? I cannot seem to find the datatype of it anywhere.
The entity framework would try to check for the column in all of the derived classes (isn't that what inheritance is all about?). You can try adding [NotMapped] attribute to your child classes.
[NotMapped]
public class ChildClass : ParentClass {
// other stuff here
}
This would minimize your problem. This attribute tells Entity framework which of the properties (if applied to fields) or classes do not need to be mapped. Read more about it: https://msdn.microsoft.com/en-us/data/jj591583.aspx?f=255&MSPPError=-2147217396#NotMapped

Invalid object name 'dbo.TableName' when retrieving data from generated table

I'm using entity framework code first to create my tables. Please note - create the tables, not the DB, since I'm working on a hosted environment and I don't have a user that is allowed to create db's.
Committing a DB update works fine, but retrieving data gives the exception:
Exception Details: System.Data.SqlClient.SqlException: Invalid object name 'dbo.EventHosts'.
I've read that it happens because I'm not using EF Code First to create the DB. That's fine, but how do I elegantly solve this?
All the generated tables do not have a prefix like dbo. A solution like this doesn't work, and isn't elegant at all:
[Table("EventHosts", Schema = "")]
Ok, for me issue was that I had a table called dbo.UserState and in C# EF was trying to access dbo.UserStates because of pluralization.
The solution was to put Table attribute above class and specify the exact table name:
[Table("UserState")]
public class UserState
{
[Key]
public int UserId { get; set; }
}
To answer your first question: use the schema created for you by your hosting provider.
To answer your second question: No there is currently no direct way to change the default schema globally because you cannot modify existing conventions or create new conventions. You can try to hack it.
For example you can override OnModelCreating and use reflection to get all DbSet<> properties declared in your context. Than you can just use simple loop on these properties and create ToTable mapping call with name of the property as table name and your custom schema. It will require some playing with reflection to make this work.
Alternatively you can try to do some reusable approach by implementing custom conventions. You can find many different articles about using your own conventions with EF. Some examples:
Custom Conventions in Entity Framework Code First v 4.1
Conventions in Entity Framework 4.1 Final
My high level untested idea is following same principle and create assembly level attribute which will be processed by the convention mechanism and applied on all your entities.
Try to set default schema name to 'dbo' in SQL SERVER.
http://msdn.microsoft.com/en-us/library/ms173423.aspx
On of the reason for this error is the table named "EventHosts" may not Exist or that table is renamed to some other name please check with that..
https://stackoverflow.com/a/12808316/3069271
I had same issue, it was pluralize problem between mapping and db.

Categories

Resources