TPH Discriminator not on base class / table - entity-framework

I'm having an issue with discriminators in TPH inheritance with Entity Framework v6.1.1
I would expect that the discriminator should fall on the table that is representative of the base class. However, it appears EF is trying to map the discriminator column to the table mapped to the derived class
E.g.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Staff : Person
{
public decimal? Salary { get; set; }
}
public class MyContext : DbContext
{
public MyContext()
: base("MyConnectionString") {}
public virtual IDbSet<Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Map(x => x.ToTable("Person"))
.Map<Staff>(x => x.ToTable("Staff").Requires("PersonTypeId").HasValue(1));
}
}
I am also using an existing schema too - i.e:
CREATE TABLE Person
(
Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
Name VARCHAR(50) NULL,
PersonTypeId INT NOT NULL
)
CREATE TABLE Staff
(
Id INT NOT NULL REFERENCES Person(Id),
Salary DECIMAL NULL
)
GO
However, when I try to add a new Staff, I encounter the following exception:
'System.Data.SqlClient.SqlException : Cannot insert the value NULL into column 'PersonTypeId', table 'MyDb.dbo.Person'; column does not allow nulls. INSERT fails.
The statement has been terminated'
It appears that it is trying to insert the discriminator (incorrectly) to the derived table. Hope someone can help.

So it appears that I've mis-understood the TPH setup for Entity Framework.
In my case, I'm wanting to map derived types to separate tables, which is an example of Table per Type - http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt
Discriminators are redundant in this context.

Related

23502: null value in column "id" violates not-null constraint in postgreSQL

I create a table with primary key.
I tried to insert new data with entityframework6, but it would get 23502 error.
But I add the default value to the column before I insert it.
I don't understand why it would get this error.
Table DDL:
CREATE TABLE ERRORLOG(
id numeric NOT NULL,
message varchar(50) NULL,
CONSTRAINT pterrorlog_pk PRIMARY KEY (id)
);
Model:
public partial class ERRORLOG
{
[Key]
[Column(Order = 0)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
Funcation:
using (DbContext Db as new DbContext)
using (TransactionScope transactionScope = new TransactionScope())
{
ERRORLOG iLog = new ERRORLOG();
iLog.MESSAGE = Message;
Db.ERRORLOG.Add(iLog);
Db.SaveChanges(); //Get 23502 error
}
Here is the insert script, it looks like didn't insert the id, why is that?
INSERT INTO "pterrorlog"("message") VALUES (#p_0) RETURNING "id"
Edit:
After I add this script on the Model, it works fine now.
public partial class ERRORLOG
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
Looks like Entity Framework auto insert a value to the column.
After I add the script to prevent this issue, it works fine now.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
Model would like:
public partial class ERRORLOG
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
You can use PGAdmin to profile the SQL that EF is actually attempting to execute on SaveChanges. C# is case sensitive while Postgres defaults to lower case. If I recall NPGSQL will format all EF SQL Queries with double-quotes so if your Entities were declared with properties like ID, it would be generating statements like INSERT INTO "ERRORLOG" ( "ID", "MESSAGE" ) VALUES ( ... ) so a column named "id" wouldn't be getting set.
If you want your entities to use a different case than the DB, and leave Postgres using lower case then I'd recommend using [Column] attributes to rename the columns:
public partial class ERRORLOG
{
[Key, Column(Name = "id")]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
[Column(Name = "message")]
public string MESSAGE { get; set; }
}
The other detail is that Order on the Column attribute is only needed when dealing with composite keys, such as many-to-many joining tables where the PK is made up of two or more columns. It isn't needed for normal single-value PKs.
If that isn't the cause, checking the insert statement in PGAdmin should give you a clue what NPGSQL / EF is attempting to execute.

Code First: TPT inheritance - Specify a different name for the primary key column in each table

I've posted my problem on codeplex http://entityframework.codeplex.com/workitem/2087.
There are also some questions posted here but they are not successfully answered.
See
Mapping TPT in EF Code First 4.1 w/ Different Primary Keys
Entity Framework 4 - TPT Inheritance in Features CTP5 (code first): rename foreign key column on inherited table
How can I use TPT inheritance models when primary keys have different names?
Is it now possible to have different column names for the primary keys when using TPT?
May be with 6.1.0
In TPT you're essentially do not want to declare the key in the subclasses, you'd miss the point otherwise.
If you must have a different Id name, just make proxy properties in the subclasses mapping to the base Id one.
public class BaseEntity
{
public int Id { get; set; }
}
public abstract class SubEntity : BaseEntity
{
public BaseId
{
get => Id;
set => Id = value;
}
}
Consider marking the sub fields as NotMapped, which in case you shouldn't include them in your LINQ queries.
With EF 6.4 I was able to use the ColumnAttribute to rename the Primary Key column in the dependent class
[Table("Person")]
public class Person
{
[Key]
public virtual int PersonId { get; set; }
// Person atributes...
}
[Table("Employee")]
public class Employee : Person
{
[Column("EmployeeId")] // <- Name of the primary Key column in the Database
public override int PersonId { get; set }
// Employee Attributes
}
Look at this code snip. Its work correct for me:
public partial class Person
{
// Any other PK name can thrown an exception
public int ID { get; set; }
}
public partial class Employee : Person
{
// Hide base class ID
private new int ID { get; set }
// Define derived class ID (that wrapped inherited ID)
[NotMapped]
public int EmployeeID
{
get { return base.PersonID; }
set { base.PersonID = value; }
}
}
Now, we must rename the inherited ID (with fluent API) for database table:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.Property(e => e.ID)
.HasColumnName("EmployeeID");
}

Foreign Key Not Mapped Correctly from Inheritance

If I have the following code generating my database it assigns a foreign key from the TankComponent table to the Asset table instead of the Tank table. Can someone explain why? Do I need to turn off a specific convention or change in the Fluent API? Is it really only looking at the column name?
[Table("Asset")]
public abstract class Asset
{
[Key]
public int AssetId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
[Table("Tank")]
public class Tank : Asset
{
public Tank()
{
this.TankCompnents = new Collection<TankComponent>();
}
public int TankField1 { get; set; }
public ICollection<TankComponent> TankCompnents { get; set; }
[NotMapped]
public IEnumerable<Floor> Floors { get { return this.TankCompnents.OfType<Floor>(); } }
}
[Table("TankComponent")]
public abstract class TankComponent
{
[Key]
public int TankComponentId { get; set; }
[ForeignKey("Tank")]
public int AssetId { get; set; }
public Tank Tank { get; set; }
public string Name { get; set; }
}
//forgot this in initial post
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Tank>()
.Map(m =>
{
m.Properties(a => new { a.AssetId, a.Name, a.Description });
m.Requires("AssetType").HasValue(1);
m.ToTable("Asset");
})
.Map(m =>
{
m.Properties(t => new { t.AssetId, t.TankField1 });
m.ToTable("Tank");
});
}
This mapping line...
m.Requires("AssetType").HasValue(1);
...and your comments seem to indicate that you possibly have a misunderstanding how Table-Per-Type (TPT) inheritance works.
EF does not need a specific column in the table of the base class Asset to detect what the actual type of the entity with a given primary key value is - unless you would use Table-Per-Hierarchy (TPH) inheritance mapping (i.e. a mapping without having [Table] attributes on your entities). For TPH a specific column - the discriminator - in indeed necessary to distinguish between the types because all properties of all entities in the inheritance tree would be stored in a single table. If you don't specify a discriminator explicitly - like AssetType - EF would create a column called Discriminator by default.
Now, TPT is a different story. If you query an entity that has other derived entities - for example...
var asset = context.Assets.First();
...EF will not only create a SQL query like SELECT TOP(1) * FROM ASSETS on the base table alone but instead a - possibly very complex - query with many LEFT OUTER JOINs to many other tables that belong to all possible derived entities. This query would either find a row in the Tank table or not. If it does find one EF will materialize a Tank object. If not it will materialize an Asset. (Cannot be the case here because Asset is abstract but assume for a moment it would not be abstract.) If Asset has other derived types EF will join their tables as well and decide again about the concrete entity type depending on the existence of joined rows.
So, with TPT the type is detected not by a special column but only by the result of (left outer) table joins.
The line above seems to confuse EF somehow. But it really doesn't belong into a TPT mapping and I would remove your whole mapping with Fluent API.
I've tested that the result is correct when you remove the mapping - i.e. the FK relationship will be created between TankComponent and Tank table (not Asset table).

EF 4 Code First - Combine Views and Tables

I researched this question for days and cannot seem to find an option I feel good about; however, here is a link to a very similar question:
Add Calculated field to Model
Ultimately, I have the same question, but I am hoping for a better solution.
Consider the following DB Tables:
CREATE TABLE [Contact](
[ContactID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[ContactName] [varchar](80) NOT NULL,
[Email] [varchar](80) NOT NULL,
[Title] [varchar](120) NOT NULL,
[Address1] [varchar](80) NOT NULL,
[Address2] [varchar](80) NOT NULL,
[City] [varchar](80) NOT NULL,
[State_Province] [varchar](50) NOT NULL,
[ZIP_PostalCode] [varchar](30) NOT NULL,
[Country] [varchar](50) NOT NULL,
[OfficePhone] [varchar](30) NOT NULL,
[MobilePhone] [varchar](30) NOT NULL)
CREATE TABLE [Blog](
[BlogID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[BlogName] [varchar](80) NOT NULL,
[CreatedByID] [int] NOT NULL, -- FK to ContactTable
[ModifiedByID] [int] NOT NULL -- FK to ContactTable
)
CREATE TABLE [Post](
[PostID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[BlogID] [int] NOT NULL, -- FK to BlogTable
[Entry] [varchar](8000) NOT NULL,
[CreatedByID] [int] NOT NULL, -- FK to ContactTable
[ModifiedByID] [int] NOT NULL -- FK to ContactTable
)
I now would like to use views for loading "common" lookup/calculated info. Every time we display a post on the site, we want to know the name of the person who created the post and who last modified it. These are two fields that are stored in separate tables from the post table. I could easily use the following syntax (assuming Lazy/eager loading was applied and CreatedBy was a property, of type Contact, based on CreatedByID): currentPost.CreatedBy.Name;
The problem with that approach is the number of Db calls and also the large record retrieved for contact, but we are only using Name 99% in this situation. I realize the DB schema above is tiny, but this is just a simplified example and the real contact table has about 50 fields.
To manage this type of situation in the past (prior to using EF), I have typically built out "detail" views for the tables I will use. The "detail" views contain common lookup/calculated fields so that it only takes 1 call to the DB to efficiently get all the info I need (NOTE: We also use indexing on our SQL views to make this extremely efficient for reading) Here is a list of views that I will commonly use (as they will contain "look up" fields from related tables):
ALTER VIEW [icoprod].[BlogDetail]
AS
SELECT B.[BlogID],
B.[BlogName],
B.[BlogDescription],
B.[CreatedByID],
B.[ModifiedByID],
CREATEDBY.[ContactName] AS CreatedByName,
MODIFIEDBY.[ContactName] AS ModifiedByName,
(SELECT COUNT(*) FROM Post P WHERE P.BlogID = B.BlogID) AS PostCount
FROM Blog AS B
JOIN Contact AS CREATEDBY ON B.CreatedByID = CREATEDBY.ContactID
JOIN Contact AS MODIFIEDBY ON B.ModifiedByID = MODIFIEDBY.ContactID
ALTER VIEW [icoprod].[PostDetail]
AS
SELECT P.[PostID],
P.[BlogID],
P.[Entry],
P.[CreatedByID],
P.[ModifiedByID],
CREATEDBY.[ContactName] AS CreatedByName,
MODIFIEDBY.[ContactName] AS ModifiedByName,
B.Name AS BlogName
FROM Post AS P
JOIN Contact AS CREATEDBY ON P.CreatedByID = CREATEDBY.ContactID
JOIN Contact AS MODIFIEDBY ON P.ModifiedByID = MODIFIEDBY.ContactID
JOIN Blog AS B ON B.BlogID = P.BlogID
Here is an overview of my "POCO" objects:
public class Blog
{
public int ID { get; set; }
public string Name { get; set; }
public int CreatedByID { get; set; }
public DateTime ModifiedByID { get; set; }
}
public class Post
{
public int ID { get; set; }
public string Name { get; set; }
public int CreatedByID { get; set; }
public DateTime ModifiedByID { get; set; }
}
public class Contact
{
public int ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Title { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string MobilePhone { get; set; }
}
public class BlogDetails : Blog
{
public string CreatedByName { get; set; }
public string ModifiedByName { get; set; }
public int PostsCount { get; set; }
}
public class PostDetails : Post
{
public string CreatedByName { get; set; }
public string ModifiedByName { get; set; }
public string BlogName { get; set; }
}
The reason I like this approach is that it allows me to retrieve information from the database based on tables or views AND if I load a view, the view contains all the "table" information which would allow me to load from a view but save to a table. IMO, this gives me the best of both worlds.
I have used this approach in the past, but typically, I just loaded information from the DB using datarows or info from stored procs or even used subsonic activerecord pattern and mapped fields after loading from the DB. I am really hoping I can do something in EF that lets me load these objects without creating another layer of abstraction.
Here is what I have tried to use for configuration (using Fluent API and code-first EF):
public class PostConfiguration : EntityTypeConfiguration<Post>
{
public PostConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("PostID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Post");
});
}
}
public class BlogConfiguration : EntityTypeConfiguration<Blog>
{
public BlogConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("BlogID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Blog");
});
}
}
public class ContactConfiguration : EntityTypeConfiguration<Contact>
{
public ContactConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("ContactID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Contact");
});
}
}
public class PostDetailsConfiguration : EntityTypeConfiguration<PostDetails>
{
public PostDetailsConfiguration()
: base()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("icoprod.PostDetails");
});
}
}
public class BlogDetailsConfiguration : EntityTypeConfiguration<BlogDetails>
{
public BlogDetailsConfiguration()
: base()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("icoprod.BlogDetails");
});
}
}
At this point, I have tried to use a view containing all of the information from the table with "extended" information and when I try this I get the dreaded 3032 error (error sample here). Then I tried to have the view ONLY contain the Primary key of the table and the "extended" properties (e.g. [Entry] is not in PostDetails view). When I try this, I get the following error:
All objects in the EntitySet 'DBContext.Post' must have unique primary keys. However, an instance of type 'PostDetails' and an instance of type 'Post' both have the same primary key value, 'EntitySet=Post;ID=1'.
So I have played with leaving off MapInheritedProperties a bit, but with no luck. I continue to get a similar error.
Does anyone have a suggestion on how to "extend" a base/table object and load info from a view? Again, I believe there is a big performance gain by doing this. The article I referenced at the beginning of this question has 2 potential solutions, but 1 requires too many DB hits (just to get some common lookup info) and the other requires an additional layer of abstraction (and I would really like to go directly to my POCO's from the DB, without writing any mapping).
Lastly, thank you to everyone who answers these types of questions. I applaud everyone who has contributed to responses over the years. I think too many of us developers take this information for granted!!
Loading record from view and saving it to table will not work with code mapping - Blog entity will always be loaded from table and saved to table and BlogDetail entity will always be loaded from view and saved to view - so you must have updatable view or instead of trigger to support this scenario. If you use EDMX you can also map custom SQL / Stored procedure executed for insert, update and delete to force saving to table but this feature is not available in code mapping. Anyway it is not your biggest problem.
You can use your view and you can map it to class as you did but you must not map the inheritance. The reason is the way how inheritance works. Inheritance says that entity is either parent or child (which can act as parent). There can never be database record which can be be both parent (I mean only parent) or child. It is even not possible in .NET because to support this scenario you need two instances - on of parent type and one of child type. These two instances are not equivalent because pure parent cannot be cast to child (it is not a child). And here comes the biggest problem. Once you map inheritance the key must be unique in the whole inheritance hierarchy. So you can never have two instances (one for parent and one for child) with the same key.
As a workaround don't derive BlogDetail from mapped entity (Blog). Either use third not mapped class as parent for both or interface. Also don't use MapInheritedProperties to make your BlogDetail completely unrelated to Blog.
Another workaround is not mapping BlogDetail at all. In such case you can use your code as is and instead of using a view create simple reusable query with projection:
var blogDetails = from b in context.Blogs
where ...
select new BlogDetail
{
Name = b.Name,
CreatedByID = b.CreatedByID,
...
CreatedByName = b.CreatedBy.Name // You need navigation property
...
};
In both cases if you need to save Blog you must create new instance and fill it from BlogDetail. After that you attach it to context, set it to modified state and save changes.

EF Code First "Invalid column name 'Discriminator'" but no inheritance

I have a table in my database called SEntries (see below the CREATE TABLE statement). It has a primary key, a couple of foreign keys and nothing special about it. I have many tables in my database similar to that one, but for some reason, this table ended up with a "Discriminator" column on the EF Proxy Class.
This is how the class is declared in C#:
public class SEntry
{
public long SEntryId { get; set; }
public long OriginatorId { get; set; }
public DateTime DatePosted { get; set; }
public string Message { get; set; }
public byte DataEntrySource { get; set; }
public string SourceLink { get; set; }
public int SourceAppId { get; set; }
public int? LocationId { get; set; }
public long? ActivityId { get; set; }
public short OriginatorObjectTypeId { get; set; }
}
public class EMData : DbContext
{
public DbSet<SEntry> SEntries { get; set; }
...
}
When I try to add a new row to that table, I get the error:
System.Data.SqlClient.SqlException: Invalid column name 'Discriminator'.
This problem only occurs if you are inheriting your C# class from another class, but SEntry is not inheriting from anything (as you can see above).
In addition to that, once I get the tool-tip on the debugger when I mouse over the EMData instance for the SEntries property, it displays:
base {System.Data.Entity.Infrastructure.DbQuery<EM.SEntry>} = {SELECT
[Extent1].[Discriminator] AS [Discriminator],
[Extent1].[SEntryId] AS [SEntryId],
[Extent1].[OriginatorId] AS [OriginatorId],
[Extent1].[DatePosted] AS [DatePosted],
[Extent1].[Message] AS [Message],
[Extent1].[DataEntrySource] AS [DataE...
Any suggestions or ideas where to get to the bottom of this issue? I tried renaming the table, the primary key and a few other things, but nothing works.
SQL-Table:
CREATE TABLE [dbo].[SEntries](
[SEntryId] [bigint] IDENTITY(1125899906842624,1) NOT NULL,
[OriginatorId] [bigint] NOT NULL,
[DatePosted] [datetime] NOT NULL,
[Message] [nvarchar](500) NOT NULL,
[DataEntrySource] [tinyint] NOT NULL,
[SourceLink] [nvarchar](100) NULL,
[SourceAppId] [int] NOT NULL,
[LocationId] [int] NULL,
[ActivityId] [bigint] NULL,
[OriginatorObjectTypeId] [smallint] NOT NULL,
CONSTRAINT [PK_SEntries] PRIMARY KEY CLUSTERED
(
[SEntryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[SEntries] WITH CHECK ADD CONSTRAINT [FK_SEntries_ObjectTypes] FOREIGN KEY([OriginatorObjectTypeId])
REFERENCES [dbo].[ObjectTypes] ([ObjectTypeId])
GO
ALTER TABLE [dbo].[SEntries] CHECK CONSTRAINT [FK_SEntries_ObjectTypes]
GO
ALTER TABLE [dbo].[SEntries] WITH CHECK ADD CONSTRAINT [FK_SEntries_SourceApps] FOREIGN KEY([SourceAppId])
REFERENCES [dbo].[SourceApps] ([SourceAppId])
GO
ALTER TABLE [dbo].[SEntries] CHECK CONSTRAINT [FK_SEntries_SourceApps]
GO
Turns out that Entity Framework will assume that any class that inherits from a POCO class that is mapped to a table on the database requires a Discriminator column, even if the derived class will not be saved to the DB.
The solution is quite simple and you just need to add [NotMapped] as an attribute of the derived class.
Example:
class Person
{
public string Name { get; set; }
}
[NotMapped]
class PersonViewModel : Person
{
public bool UpdateProfile { get; set; }
}
Now, even if you map the Person class to the Person table on the database, a "Discriminator" column will not be created because the derived class has [NotMapped].
As an additional tip, you can use [NotMapped] to properties you don't want to map to a field on the DB.
Here is the Fluent API syntax.
http://blogs.msdn.com/b/adonet/archive/2010/12/06/ef-feature-ctp5-fluent-api-samples.aspx
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName {
get {
return this.FirstName + " " + this.LastName;
}
}
}
class PersonViewModel : Person
{
public bool UpdateProfile { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ignore a type that is not mapped to a database table
modelBuilder.Ignore<PersonViewModel>();
// ignore a property that is not mapped to a database column
modelBuilder.Entity<Person>()
.Ignore(p => p.FullName);
}
I just encountered this and my problem was caused by having two entities both with the System.ComponentModel.DataAnnotations.Schema.TableAttribute referring to the same table.
for example:
[Table("foo")]
public class foo
{
// some stuff here
}
[Table("foo")]
public class fooExtended
{
// more stuff here
}
changing the second one from foo to foo_extended fixed this for me and I'm now using Table Per Type (TPT)
I had a similar problem, not exactly the same conditions and then i saw this post. Hope it helps someone. Apparently i was using one of my EF entity models a base class for a type that was not specified as a db set in my dbcontext. To fix this issue i had to create a base class that had all the properties common to the two types and inherit from the new base class among the two types.
Example:
//Bad Flow
//class defined in dbcontext as a dbset
public class Customer{
public int Id {get; set;}
public string Name {get; set;}
}
//class not defined in dbcontext as a dbset
public class DuplicateCustomer:Customer{
public object DuplicateId {get; set;}
}
//Good/Correct flow*
//Common base class
public class CustomerBase{
public int Id {get; set;}
public string Name {get; set;}
}
//entity model referenced in dbcontext as a dbset
public class Customer: CustomerBase{
}
//entity model not referenced in dbcontext as a dbset
public class DuplicateCustomer:CustomerBase{
public object DuplicateId {get; set;}
}
Another scenario where this occurs is when you have a base class and one or more subclasses, where at least one of the subclasses introduce extra properties:
class Folder {
[key]
public string Id { get; set; }
public string Name { get; set; }
}
// Adds no props, but comes from a different view in the db to Folder:
class SomeKindOfFolder: Folder {
}
// Adds some props, but comes from a different view in the db to Folder:
class AnotherKindOfFolder: Folder {
public string FolderAttributes { get; set; }
}
If these are mapped in the DbContext like below, the "'Invalid column name 'Discriminator'" error occurs when any type based on Folder base type is accessed:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Folder>().ToTable("All_Folders");
modelBuilder.Entity<SomeKindOfFolder>().ToTable("Some_Kind_Of_Folders");
modelBuilder.Entity<AnotherKindOfFolder>().ToTable("Another_Kind_Of_Folders");
}
I found that to fix the issue, we extract the props of Folder to a base class (which is not mapped in OnModelCreating()) like so - OnModelCreating should be unchanged:
class FolderBase {
[key]
public string Id { get; set; }
public string Name { get; set; }
}
class Folder: FolderBase {
}
class SomeKindOfFolder: FolderBase {
}
class AnotherKindOfFolder: FolderBase {
public string FolderAttributes { get; set; }
}
This eliminates the issue, but I don't know why!
I get the error in another situation, and here are the problem and the solution:
I have 2 classes derived from a same base class named LevledItem:
public partial class Team : LeveledItem
{
//Everything is ok here!
}
public partial class Story : LeveledItem
{
//Everything is ok here!
}
But in their DbContext, I copied some code but forget to change one of the class name:
public class MFCTeamDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Other codes here
modelBuilder.Entity<LeveledItem>()
.Map<Team>(m => m.Requires("Type").HasValue(ItemType.Team));
}
public class ProductBacklogDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Other codes here
modelBuilder.Entity<LeveledItem>()
.Map<Team>(m => m.Requires("Type").HasValue(ItemType.Story));
}
Yes, the second Map< Team> should be Map< Story>.
And it cost me half a day to figure it out!
Old Q, but for posterity...it also also happens (.NET Core 2.1) if you have a self-referencing navigation property ("Parent" or "Children" of the same type) but the Id property name isn't what EF expects. That is, I had an "Id" property on my class called WorkflowBase, and it had an array of related child steps, which were also of type WorkflowBase, and it kept trying to associate them with a non-existent "WorkflowBaseId" (the name i suppose it prefers as a natural/conventional default). I had to explicitly configure it using HasMany(), WithOne(), and HasConstraintName() to tell it how to traverse. But I spent a few hours thinking the problem was in 'locally' mapping the object's primary key, which i attempted to fix a bunch of different ways but which was probably always working.
this error happen with me because I did the following
I changed Column name of table in database
(I did not used Update Model from database in Edmx) I Renamed manually Property name to match the change in database schema
I did some refactoring to change name of the property in the class to be the same as database schema and models in Edmx
Although all of this, I got this error
so what to do
I Deleted the model from Edmx
Right Click and Update Model from database
this will regenerate the model, and entity framework will not give you this error
hope this help you