I'm working on a Razor pages web app which works directly with a db context...yes this is not ideal but is what I'm stuck with for the time being.
In the data model, each object inherits from a base entity class containing audit data, e.g.:
public class BaseEntity
{
[Key]
public int Id { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
...etc.
public class Table1 : BaseEntity
{
public string TestItemName { get; set; }
}
In the database, I want CreatedBy to be required (not null), but I don't want to use the [Required] attribute since this will trigger the UI to validate the CreatedBy column. I don't want to expose this column in the UI and instead have service code which updates all of the audit properties based on Add/Insert.
What I'm looking for is a way via Fluent API which will give me the column type in the db that I need, e.g. NVARCHAR(MAX) NOT NULL.
I can accomplish this in the OnModelCreating method in the dbcontext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Table1>()
.Property(o => o.CreatedBy)
.IsRequired();
However this would require me to create a similar entry for every table in the model.
Is there code I can use in OnModelCreating which could accomplish this for all entities? Something like this (this is just pseudo-code, but looking to give an idea):
var entityTypes = modelBuilder.Model.GetEntityTypes().Select(o => o.GetType()).ToList();
entityTypes.ForEach(e =>
{
e.Property("CreatedBy").IsRequired();
});
Implement your entity configurations in discrete classes that implement IEntityTypeConfiguration. Your implementations should inherit from a base implementation that configures BaseEntity and the Configure method should be virtual with overriding implementations calling the base class' method:
public abstract class BaseEntityConfiguration<TEntity>
: IEntityTypeConfiguration<TEntity>
where TEntity : BaseEntity
{
public virtual void Configure(EntityTypeBuilder<TEntity> builder)
{
builder.Property(be => be.CreatedBy)
.IsRequired();
// etc
}
}
public class SomeEntityConfiguration : BaseEntityConfiguration<SomeEntity>
{
public override void Configure(EntityTypeBuilder<SomeEntity> builder)
{
// call base class method to configure BaseEntity properties
base.Configure(builder);
// configure remaining SomeEntity-specific properties/etc
builder.TestItemName.IsRequired();
}
}
You'll need to inform the model builder to use your configuration classes. For example, if your config classes are in the same assembly as your DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(YourDbContext).Assembly);
}
We use EntityFramework 6.1 with CodeFirst in our web mvc application (StdWebApp). Now we want to make a new custom version of this application (CustomWebApp) .
The CustomWebApp will use most of the code of the standard one, in it's domain model it will extend the Person class.
In CustomDomain we make implement a new DbContext that must connect with the database of the custom app (CustomSqlDb).
In (C#) code there is no problem that there is a Person in Domain and in CustomDomain. However we have not been able to devise a mapping for Person in the Custom DbContext that will:
Create a single "Person" table.
Contains fields form "CustomDomain.Person" AND those from "Domain.Person".
We tried some variants like this:
modelBuilder.Entity<Person>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Person");
}
);
using this document as our inspiration msdn mapping types
But EF complains about the simple name beeing equal.
Obviously we could rename the "Person" in "CustomDomain" to "PersonCustom" but that could lead to a lot of silly names if we have to do this again in the future like "PersonCustomExtraSpecial" etc.
Thoughts anyone?
UPDATE
we tried the solution suggested by mr100, here is the complete code:
namespace Domain
{
public class Person
{
public int Id { get; set; }
public string Stuff { get; set; }
}
}
namespace CustomDomain
{
public class Person : Domain.Person
{
public string ExtraStuff { get; set; }
}
}
namespace CustomDomain
{
public class DbModel : DbContext
{
DbSet<CustomDomain.Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<CustomDomain.Person>().Map(m => m.ToTable("Person"));
}
}
}
This still result in the error
The type 'CustomDomain.Person' and the type 'Domain.Person' both have the same simple name of 'Person' and so cannot be used in the same model. All types in a given model must have unique simple names. Use 'NotMappedAttribute' or call Ignore in the Code First fluent API to explicitly exclude a property or type from the model.
So we added the following code:
namespace CustomDomain
{
public class DbModel : DbContext
{
DbSet<CustomDomain.Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<Domain.Person>();
modelBuilder.Entity<CustomDomain.Person>().Map(m => m.ToTable("Person"));
}
}
}
Still same result.
To achieve this your DbContext class in CustomWebApps should have property People defined like this:
public DbSet<CustomDomain.Person> People {get; set;}
and no property:
public DbSet<Domain.Person> People {get; set;}
even if it comes from StdWebApp DbContext class from which CustomWebApp DbContext class may derive (if that is the case for you). Additionally you may set properly table name:
modelBuilder.Entity<Person>().ToTable("Person");
I use EF 5 with the code first approach. Now I try to define a "code" table in which I want to have several different codes (like address code, medium code, etc.). In this table I just have the following properties: ID (Guid), Name (String), Description (String) and a discriminator (in this case something like the type of the code: address code, medium code, etc.).
So I defined the following base class:
public abstract class Code : EntityBase
{
public string Name { get; set; }
public string Beschreibung { get; set; }
}
Then I derived two classes from code
public class AddressCode : Code {}
public class MediumCode : Code {}
The class EntityBase is abstract and just defines the Id property, we use it for every POCO class...
The goal is that I can use AddressCode as a property on my address POCO class:
public class Adresse : EntityBase
{
#region Properties
public string Name1 { get; set; }
public virtual AddressCode AddressCode { get; set; }
#endregion
}
The question now is, how can I explain EF how to do that? Anyone can help?
Thanks
Marco
Thanks for your answer!
I tried to do it like you said. Unfortunately I get an error because of my EntityBase class:
public abstract class EntityBase
{
#region Properties
public virtual Guid Id { get; set; }
public virtual bool IsValid
{
get
{
{
return Validate();
}
}
}
[NotMappedAttribute]
public virtual IList<ValidationFailure> ValidationFailures { get; set; }
#endregion
#region Methods
private bool Validate()
{
var validatorFactory = new AttributedValidatorFactory();
IValidator validator = validatorFactory.GetValidator(GetType());
if (validator == null)
{
return true;
}
ValidationResult validationResult = validator.Validate(this);
ValidationFailures = validationResult.Errors;
return validationResult.IsValid;
}
#endregion
}
The error message is:
You cannot use Ignore method on the property 'ValidationFailures' on type 'Entities.AdresseCode' because this type inherits from the type 'Entities.EntityBase' where this property is mapped. To exclude this property from your model, use NotMappedAttribute or Ignore method on the base type.
As you can see I already defined the property ValidationFailures as NotMapped but still I get this error.. Do you have an idea?
Thanks
Marco
Just create a context (derived from DbContext)
public class AddressesDb : DbContext
{
public DbSet<Code> Codes { get; set; }
public DbSet<Adresse> Adressen { get; set; }
}
And (when used in code) EF will create a database with default table and column names. It will create a discriminator column of type text (nvarchar) which will contain the names of the classes that derive from Code.
If you want different names and/or types you should either use data annotations or fluent API to configure these.
Finally I got it work!
In the DBContext be aware to define DbSets for the code derived classes before all the other POCO's and then it works!
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
I've got the following requirement that works well in the OO space but I can't seem to get it to map back to the DB using ADO EF code first.
I have numrous products each will have different aspects (attributes but not in the sense of code attributes). For instance ring would have aspects such as mineral type = gold etc whilst a diamond would have an aspec of clarity = VVSI1.
As you can see the products very greatly in thier composition and I want a dynamic way of growing my system.
As such I've created a product class:
public class Product
{
public int id { get; set; }
public string Name { get; set; }
private List<ProductAspect> aspects = new List<ProductAspect>();
public List<ProductAspect> Aspects { get { return aspects; } set { aspects = value; } }
}
It has a list of ProductAspect which is the base class for all aspects moving forward:
public class ProductAspect
{
public int id { get; set; }
public string AspectName { get; set; }
}
I then inherit from the ProductAspect using a generic which alows me to be specific (strongly typed) about my Aspect Value:
public abstract class ProductAspect<T> : ProductAspect
{
public T AspectValue { get; set; }
}
I then create some Aspects that will allow me to decorate my product:
public class StringAspect : ProductAspect<string> { };
public class DecimalAspect : ProductAspect<decimal> { };
public class ImageAspect : ProductAspect<byte[]> { };
I then give the DbContext a try and have tried both TPH and TPC inheritance mappings.
Neither seem to work. The DB model that get's generated doesn't create a foriegn key to the StringAspect or DecimalAspect tables from the Aspect Table.
public class IxamDataContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductAspect> Aspects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
AspectMapping(modelBuilder);
}
private void AspectMapping(DbModelBuilder mb)
{
//TPH
//mb.Entity<ProductAspect>()
// .Map<StringAspect>(m => m.Requires("type").HasValue("sa"))
// .Map<DecimalAspect>(m => m.Requires("type").HasValue("da"));
//TPC
//mb.Entity<StringAspect>().ToTable("StringAspect");
//mb.Entity<DecimalAspect>().ToTable("DecimalAspect");
}
}
Resulting in the following exception for this Seeding code:
Product p = new Product();
p.Name = "Diamond";
p.Aspects.Add(new StringAspect() { AspectName = "History", AspectValue = "Old and long" });
p.Aspects.Add(new DecimalAspect() { AspectName = "Weight", AspectValue= 96.5M });
context.Products.Add(p);
context.SaveChanges();
Excpetion:
EntityType 'StringAspect' does not
exist in the EntitySet
'IxamDataContext.Aspects'. Parameter
name: entity
Any ideas from the EF code first pros out there?
Entity framework doesn't support intermediate non mapped types in inheritance hierarchy. It means that you can't have this inheritance: A (mapped) -> B (not mapped) -> C (mapped). EF also doesn't support mapping generic types. It means that you must remove your generic intermediate class from the hierarchy and move AspectValue to derived types with correct type.
Maybe it's to late, but I would offer you using ComplexType attribute it will allows you to extend your types as you wish.