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
Related
I am working on .NET CORE 6 along with EF CORE 7. I need to seed data in joining table but unable to do so and get error.
I am seed FileTypeId but not sure why EF core migration throwing error...
error
The seed entity for entity type 'JobFileType' cannot be added because it has the navigation 'FileType' set. To seed relationships, add the entity seed to 'JobFileType' and specify the foreign key values {'FileTypeId'}. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the involved property values.
ClassA
public class JobProfile
{
public JobProfile()
{
this.JobFileTypes = new HashSet<JobFileType>();
}
public Guid JobProfileId { get; set; }
public string Name { get; set; }
public ICollection<JobFileType>? JobFileTypes { get; set; }
}
ClassB
public class FileType
{
public FileType()
{
this.JobFileTypes = new HashSet<JobFileType>();
}
public Guid FileTypeId { get; set; }
public string Extension { get; set; } = string.Empty;
public ICollection<JobFileType>? JobFileTypes { get; set; }
}
Joing Table
public class JobFileType
{
public Guid JobFileTypeId { get; set; }
public Guid JobProfileId { get; set; }
public JobProfile JobProfile { get; set; } = new JobProfile();
public Guid FileTypeId { get; set; }
public FileType FileType { get; set; } = new FileType();
}
Seed Extension
public static class JobFileTypeSeed
{
public static void Seed(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<JobFileType>()
.HasData(
new JobFileType {JobFileTypeId = Guid.Parse("aaa"), JobProfileId = Guid.Parse("ccc"), FileTypeId = Guid.Parse("yyy") },
new JobFileType { JobFileTypeId = Guid.Parse("bbb"), JobProfileId = Guid.Parse("ccc"), FileTypeId = Guid.Parse("zzz") }
);
}
}
config
internal class JobFileTypeConfiguration : IEntityTypeConfiguration<JobFileType>
{
public void Configure(EntityTypeBuilder<JobFileType> builder)
{
builder.ToTable("JobFileType", "dbo");
builder.HasKey(column => column.JobFileTypeId);
builder
.HasOne(jobFileType => jobFileType.JobProfile)
.WithMany(jobProfile => jobProfile.JobFileTypes)
.HasForeignKey(jobFileType => jobFileType.JobProfileId);
builder
.HasOne(jobFileType => jobFileType.FileType)
.WithMany(fileType => fileType.JobFileTypes)
.HasForeignKey(jobFileType => jobFileType.FileTypeId);
}
}
There is not much to say about the concrete issue (which btw is not specific to joining entity, but any entity model seeding):
I am seed FileTypeId but not sure why EF core migration throwing error...
as the cause of the issue is included at the beginning of the error message:
because it has the navigation 'FileType' set.
And your entity has
public FileType FileType { get; set; } = new FileType();
// ^ ^ ^
// the problem
and the same for
public JobProfile JobProfile { get; set; } = new JobProfile();
which will be the next error if you resolve the original.
Remove both navigation property initializers (= new ...) and the problem will be gone.
As a general rule, you should never initialize reference navigation properties because it causes many side effects and/or improper behaviors (not only for seeding, but also eager/lazy/explicit data loading). Initializing collection navigation properties is arbitrary, but ok. Only reference navigation property initialization must be avoided. For more info, see EF codefirst : Should I initialize navigation properties? - quite old EF topic, but still applies.
If you are trying to resolve NRT warnings (as I guess), initializing with new is definitely not a proper way. One reason I don't like NRT is because it is forcing people to use "workarounds" for preventing compiler warnings, which in fact break the primary functionality. Specifically in EF Core, enabling NRT also changes the optional/required attribute of some properties, hence database column types (most noticeable for string properties/columns and reference navigations). You could read more about this in the Working with Nullable Reference Types topic in the official EF Core documentation, but in general I would just disable NRT for EF entity model classes.
The proper order is to set the "master data" first and then try to set the join table, as you would expect.
The defaulting
{get;set;} = new Something();
Could be the offending declaration, since any instance upon creation will have the relation JobFileType already set
Given the following code example, how do you implement this in entity framework core where you do not want a table created for the base class but you do for the derived class and the primary key is defined in the base class?
public class JobBase
{
public JobBase() { }
public Guid JobId { get; set; } = Guid.NewGuid();
public string Title { get; set; }
}
public class Job : JobBase
{
public Job() { }
public String AdditionalInformation { get; set; }
}
And here is what I have in my DBContext class:
public DbSet<Job> Jobs { get; set; }
var job = mb.Entity<Job>();
job.HasKey(aa => aa.JobId);
job.Property(aa => aa.JobId).HasColumnName("JobId");
I currently get the following error when trying to add-migration:
A key cannot be configured on 'Job' because it is a derived type. The key must be configured on the root type 'JobBase'. If you did not intend for 'JobBase' to be included in the model, ensure that it is not referenced by a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation on a type that is included in the model.
The only part of the error message I am doing is JobBase is referenced from a navigation on a type that is included in the model but doing that is the whole reason i implemented this relationship in the first place so i can't just remove that navigation.
modelBuilder.Entity<Job>().HasKey(x => x.JobId).ToTable("Job");
EF Inheritance and Primary Keys
I have the following Entity class definition:
[Table("Users")]
public class WebUser
{
public virtual int Id { get; set; }
public virtual ICollection<Client> Clients { get; set; }
// more properties...
}
Notice that table name is different than the class name. I also have a ClientUsers table which is a many-to-many mapping for clients and users. Problem is, when I try to access the webUser.Clients property I get the following exception:
"Invalid object name 'dbo.ClientWebUsers'."
Looks like Entity Framework is trying to guess the name of the third table, but it apparently was not smart enough to take into account the table attribute that I have there. How can I tell EF that it is ClientUsers and not ClientWebUsers? Also what rule does it follow to know which table name comes first and which one comes second in the new table name? I think it's not alphabetical order.
I'm using EF 5.0. Thanks!
From the looks of things you're using Code First, so I'll answer accordingly. If this is incorrect, please let me know.
I believe the convention being used to determine the name of the many-to-many table is determined by the order in which they occur as DbSet properties in your SomeContext : DbContext class.
As for forcing EntityFramework to name your table whatever you like, you can use the Fluent API in the OnModelCreating method of your SomeContext : DbContext class as follows:
public class DatabaseContext : DbContext
{
public DatabaseContext()
: base("SomeDB")
{
}
public DbSet<WebUser> Users { get; set; }
public DbSet<Client> Clients { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<WebUser>().HasMany(c => c.Clients)
.WithMany(p => p.WebUsers).Map(
m =>
{
m.MapLeftKey("ClientId");
m.MapRightKey("UserId");
m.ToTable("ClientUsers");
});
}
}
This assumes your classes are something like the following:
[Table("Users")]
public class WebUser
{
public virtual int Id { get; set; }
public virtual ICollection<Client> Clients { get; set; }
// more properties...
}
public class Client
{
public int Id { get; set; }
public ICollection<WebUser> WebUsers { get; set; }
// more properties
}
Finally, here's an integration test (NUnit) demonstrating the functionality working. You may need to drop your database before running it as Code First should want to update/migrate/recreate it.
[TestFixture]
public class Test
{
[Test]
public void UseDB()
{
var db = new DatabaseContext();
db.Users.Add(new WebUser { Clients = new List<Client> { new Client() } });
db.SaveChanges();
var webUser = db.Users.First();
var client = webUser.Clients.FirstOrDefault();
Assert.NotNull(client);
}
}
Edit: Link to relevant documentation for the Fluent API
Rowan's answer (adding here for reference):
Here is the information on how to configure a many-to-many table (including specifying the table name). The code you are after is something like:
modelBuilder.Entity<WebUser>()
.HasMany(u => u.Clients)
.WithMany(c => c.WebUsers)
.Map(m => m.ToTable("ClientUsers");
~Rowan
According to msdn article, the following should create an optional:optional relationship, but instead it creates optional:many relationship. Is the article wrong?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Optional_1>()
.HasKey(o1 => o1.id1);
modelBuilder.Entity<Optional_2>()
.HasKey(o2 => o2.id2);
modelBuilder.Entity<Optional_1>()
.HasOptional(o1 => o1.Dependent)
.WithOptionalPrincipal(o2 => o2.Principal);
}
public class Optional_1
{
public int id1 { get; set; }
public Optional_2 Dependent { get; set; }
}
public class Optional_2
{
public int id2 { get; set; }
public Optional_1 Principal { get; set; }
}
thank you
The table might look like one to many, but Entity Framework will enforce it as optional:optional because of the navigation properties. Since the navigation property is only a single object and not a collection, there is no way to add multiple.
If you look at the generated tables, it creates a nullable foreign key to your principal table (Optional_1). This allows you to create an Optional_2 that is not associated with an Optional_1.
If you were to insert multiple rows into Optional_2 that have the same foreign key to Optional_1 outside of EF, there wouldn't be anything preventing it from going through. If you were to try and load these entities you would get an error. You can't add a unique index to the column because it needs to allow NULL since it is optional.
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");
}