This is not a how-to question, but rather a why&how it works. Here's the thing, when one has in his assembly two namespaces with two contexts implementing different parts of TPT tree but sharing the base tables, adding new entity fails when calling the SaveChanges() on the context with UpdateException: Error retrieving values from ObjectStateEntry. See inner exception for details. being thrown. The inner exception states that
(41,10) : error 3032: Problem in mapping fragments starting at lines
41, 47:EntityTypes app.a.ChildB,
app.a.ChildA are being mapped to the same
rows in table xBase. Mapping conditions can be used to
distinguish the rows that these types are mapped to.
The first question what these line numbers are, where are they refer to?
Now, given the code:
namespace app
{
public abstract class xBase
{
//...
}
}
namespace app.a
{
public class ChildA : xBase
{
//...
}
public class AContext : DbContext
{
public DbSet<xBase> Base { get; set; }
public DbSet<ChildA> Children { get; set; }
public AContext()
: base("name=AppAContext")
{
this.Configuration.LazyLoadingEnabled = false;
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("PlayGround");
modelBuilder.Configurations.Add(new xBaseConfiguration());
modelBuilder.Configurations.Add(new ChildAConfiguration());
}
public class xBaseConfiguration: EntityTypeConfiguration<xBase>
{
public xBaseConfiguration()
{
//...
this.Map(m => {
m.ToTable("xBase");
});
}
}
public class ChildAConfiguration : EntityTypeConfiguration<ChildA>
{
public ChildAConfiguration ()
{
//...
this.Map(m => {
m.ToTable("AChildren");
});
}
}
}
}
namespace app.b
{
public class ChildB : xBase
{
//...
}
public class BContext : DbContext
{
public DbSet<xBase> Base { get; set; }
public DbSet<ChildB> Children { get; set; }
public BContext()
: base("name=AppBContext")
{
this.Configuration.LazyLoadingEnabled = false;
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("PlayGround");
modelBuilder.Configurations.Add(new xBaseConfiguration());
modelBuilder.Configurations.Add(new ChildBConfiguration());
}
public class xBaseConfiguration: EntityTypeConfiguration<xBase>
{
public xBaseConfiguration()
{
//...
this.Map(m => {
m.ToTable("xBase");
});
}
}
public class ChildBConfiguration : EntityTypeConfiguration<ChildB>
{
public ChildBConfiguration()
{
//...
this.Map(m => {
m.ToTable("BChildren");
});
}
}
}
}
N.B.:Both contexts use different names but point to the very same db instance.
Then when one tries to add, f.e. a new ChildA the aforementioned exception is thrown, like in this example:
using (var db = new AContext())
{
var child = new AChild();
//...
db.Children.Add(child);
db.SaveChanges(); //<--At this point the exception is thrown
}
In case one excludes from the assembly either of the parts of the tree, the code that operates with the second part works as desired.
Now, it can be related to this behavior or not, but for some reason, as we can see in the exception's message, the framework thinks that both parts of the hierarchy reside in the same namespace, which is not the case, originally they were, so I started with separating them, but nothing has changed, then I separated the connection strings used by two contexts, but still got same behavior. It worth mentioning that the exception is being thrown even if only one of the contexts is instantiated and as soon as the first child is inserted and SaveChanges() is called. So how and why one context is aware of the existence of other entities in another namespace?
Related
I want to make a generic entity mapper that maps all entities to dbContext and EF. Currently, after I do Add-Migration init, I receive an error:
Cannot create an instance of Models.DataMapping.EntityMapper`1[TEntity] because Type.ContainsGenericParameters is true.
I do not understand what this error exacly means and why if type ContainsGenericParameters is true it should cause a crash. Any ideas what is wrong and how could I fix it?
This is my code:
namespace Models.DataMapping
{
public interface IEntityMapper
{
void Map(ModelBuilder modelBuilder);
}
}
namespace Models.DataMapping
{
public abstract class EntityMapper<TEntity> :
IEntityMapper where TEntity : class
{
public void Map(ModelBuilder modelBuilder)
{
EntityTypeBuilder<TEntity> entityTypeBuilder = modelBuilder.Entity<TEntity>();
MapEntity(entityTypeBuilder);
}
protected virtual void MapEntity(EntityTypeBuilder<TEntity> entityTypeBuilder)
{
}
}
}
namespace Models.DataMapping
{
public static class EntityMappersProvider
{
public static IReadOnlyCollection<IEntityMapper> GetLocalEntityMappers() {
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => typeof(IEntityMapper).IsAssignableFrom(t) && t.IsClass)
.Select(t => (IEntityMapper)Activator.CreateInstance(t))
.ToArray();
}
}
}
Including provider in dbContext.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
IReadOnlyCollection<IEntityMapper> entityMappers = EntityMappersProvider.GetLocalEntityMappers();
foreach (IEntityMapper mapper in entityMappers) {
mapper.Map(modelBuilder);
}
}
Example of Entity relationships realization.
namespace Models.DataMapping.EntitiesMapping
{
public class CourseMapper : EntityMapper<Course>
{
protected override void MapEntity(EntityTypeBuilder<Course> entityTypeBuilder)
{
entityTypeBuilder.ToTable("Courses");
entityTypeBuilder.HasKey(a => a.Id);
//....
}
}
}
Also what I have noticed that if I remove abstract keyword in EntityMapper and add t.IsAbstract in EntityMappersProvided empty migration gets created..
For me it looks like you are trying to reinvent IEntityTypeConfiguration which allows to move entity configuration code to separate classes :
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder
.Property(b => b.Url)
.IsRequired();
}
}
And provides convenience method to find all such configuration in assembly, which is available since EF Core 2.2:
modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);
As for your error - it happens because your code tries to instantiate an open generic type (EntityMapper<TEntity>) with Activator.CreateInstance:
public class OpenGeneric<T>{}
Activator.CreateInstance(typeof(OpenGeneric<>)); // throws Cannot create an instance of Generic`1[T] because Type.ContainsGenericParameters is true.
So you need to filter it out, for example by adding && !t.IsAbstract && ! t.ContainsGenericParameters in your Where clause.
I have two groups of classes in my code and one group has logic and other group has data and inheritance is also being used in each group. I tried to mimic the situation which I am dealing with in below code snippet. The problem I have is how to handle the objects of derived data classes efficiently in related instances of logic classes. Right now I am trying to cast the instance of derived data class in a method of derived logic class which I do not think is logical. I need some guidance to address this issue.
void Main()
{
var item1 = new D1();
var holder1 = new DataHolder1() { localProp1 = "test" };
var holderout = item1.Method1(holder1);
holderout.Dump();
}
public class BaseDataHolder
{
public string prop { get; set; }
}
public class DataHolder1 : BaseDataHolder
{
public string localProp1 { get; set; }
}
public class DataHolder2 : BaseDataHolder
{
public string localProp2 { get; set; }
}
public class BaseClass
{
public virtual BaseDataHolder Method1(BaseDataHolder holder)
{
return null;
}
}
public class D1 : BaseClass
{
public override BaseDataHolder Method1(BaseDataHolder holder)
{
(holder as DataHolder1).localProp1.Dump();
(holder as DataHolder1).localProp1 = "change1";
return holder;
}
}
public class D2 : BaseClass
{
public override BaseDataHolder Method1(BaseDataHolder holder)
{
(holder as DataHolder2).localProp2.Dump();
(holder as DataHolder2).localProp2 = "change2";
return holder;
}
}
I don't see why it would be illogical since looks like you are trying to get DataHolder1 always in class D1. Rather, why can't your class compose with Data class instance and use that in method like
public class D1 : BaseClass
{
private readonly DataHolder1 holder;
public D1(DataHolder1 holder) { this.holder = holder; }
public override BaseDataHolder Method1()
{
holder.localProp1.Dump();
holder.localProp1 = "change1";
return holder;
}
}
Then you can just say
var item1 = new D1(new DataHolder1());
BaseDataHolder data = item1.Method1();
This violates the Liskov substitution principle. In summary, it's bad, because your signature promises to work well with any BaseDataHolder but in reality it will just crash if the wrong BaseDataHolder is passed in.
I cannot really give a solution because we don't know your requirements. From what you have posted, your three logic classes should drop the inheritance and just have three different method signatures, each telling what it needs instead of all of them lying about what they need and then crashing randomly.
I got this problem when I try to create the database with EntityFramework Core:
The property 'Rating.RatingScores' could not be mapped, because it is of type 'List' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Here is the class:
public class Rating
{
public int Id { get; set; }
public List<decimal> RatingScores { get; set; }
public decimal Score
{
set => Score = value;
get => Math.Round(RatingScores.Sum() / RatingScores.Count, 1);
}
}
If the Rating class has multiple RatingScores you have a one-to-many relationship and the RatingScores property needs its own table, you therefore need to create a new class.
Class RatingScore
{
public int Id { get; set; }
public decimal RtSc { get; set; }
}
Then the Rating property will look like this:
public List<RatingScore> MyRatingScores { get; set; }
However if each Rating has one RatingScore, your property should not be a collection.
public RatingScore MyRatingScore { get; Set; }
When you really need to put multiple values in single column can use below way
Let's say you want to create only one table for below class
public class SomeClass
{
public Guid ID { get; set; }
public IEnumerable<int> Values { get; set; }
}
First create a converter, which will control .net values to db values and vice versa
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
public class IntListToStringValueConverter : ValueConverter<IEnumerable<int>, string>
{
public IntListToStringValueConverter() : base(le => ListToString(le), (s => StringToList(s)))
{
}
public static string ListToString(IEnumerable<int> value)
{
if (value==null || value.Count()==0)
{
return null;
}
return value.Join(',');
}
public static IEnumerable<int> StringToList(string value)
{
if (value==null || value==string.Empty)
{
return null;
}
return value.Split(',').Select(i => Convert.ToInt32(i)); ;
}
}
And DbContext should have below method
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
.....
var IntValueConverter = new IntListToStringValueConverter();
modelBuilder
.Entity<SomeClass>()
.Property(e => e.Values)//Property
.HasConversion(IntValueConverter);
}
Done!! IT should work
Ok. Here is my error and I just found what the problem is.
The property 'LogEntry.Timestamp' could not be mapped because it is of type 'Instant', which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Actually its fairly clear what it is trying to say.
I have overridden OnModelCreating but I missed a trivial thing.
Here is my dbcontext class.
public class ExtendedElsaMigrationsDbContext : SqlServerContext
{
public ExtendedElsaMigrationsDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// I should not comment out this base method call.
// base.OnModelCreating(modelBuilder);
// modelBuilder.Entity<User>();
modelBuilder.ConfigureExtendedElsaDbContext();
}
}
Note that I had commented out the base method call. And that is what is causing the problem.
The overridden method should be as follows.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// //modelBuilder.Entity<User>();
modelBuilder.ConfigureExtendedElsaDbContext();
}
Summary: Don't forget to call the base method when you are overriding a method. Many times base method don't do much, so we ignore them. But some times they do. So always call the base methods.
For EntityFramework Core. This solution will help you to save proper data in DB as well as, whenever the result comes back from DB, it will convert to List.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<WebhookSubscription>()
.Property(p => p.Webhooks)
.HasConversion(v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<List<string>>(v));
}
I have a base class for all entities:
public class BaseClass
{
public int SomeProperty {get; set;}
}
public class SomeEntity : BaseClass
{
...
}
I want to ignore this property in some cases. Could I do in the OnModelCreating method something like this:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<int>()
.Where(p => p.Name == "SomeProperty")
.Ignore();
}
?
You could try:
modelBuilder.Entity<SomeEntity>().Ignore(p => p.SomeProperty);
It will cause SomeProperty not to be mapped to SomeEntity.
EDIT: If this property should never be mapped to database you can add NotMapped annotation in your BaseClass:
public class BaseClass
{
[NotMapped]
public int SomeProperty {get; set;}
}
This will be the same as ignoring this property in all extending classes.
Could you override it?
public class SomeEntity : BaseClass
{
[NotMapped]
public override int SomeProperty { get; set; }
...
}
A late entry here - but in case it's useful...
Having recently encountered similar requirements, I went with this:-
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Types<EntityBase>()
.Configure(config => config.Ignore(x => x.SomeBaseClassPropertyToIgnore));
}
}
This will apply the given configuration to all entity types that inherit from EntityBase. The same technique can be used to configure entity types based on an interface they implement (probably a better approach anyway).
Advantages are:-
No need to write and maintain the same config code for multiple concrete entities.
No need for [NotMapped] attribute, which is less flexible and adds potentially unwanted dependencies.
Note that the targeted types can be filtered further if necessary:-
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Types<EntityBase>().Where(t => t != typeof(SpecialExceptionEntity)).Configure(...);
}
Refs:-
https://msdn.microsoft.com/en-us/library/dn235653(v=vs.113).aspx
https://msdn.microsoft.com/en-us/library/dn323206(v=vs.113).aspx
In VS2012(.NET 4.5 and Entity Framework 5 )
When exposed the inheritance relationship,caused the compile-time errors:
You cannot use Ignore method on the property 'InnerString' on type
'MrTree.SubSubClass' because this type inherits from the type
'MrTree.BaseClass' where this property is mapped. To exclude this
property from your model, use NotMappedAttribute or Ignore method on
the base type.
The code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyDbcontext db = new MyDbcontext();
int i = db.SubSubClasses.Count();
}
}
public class BaseClass
{
[NotMapped]
public string InnerString { get; set; }
}
public class SubClass : BaseClass
{
}
public class SubSubClass : SubClass
{
}
public class MyDbcontext : DbContext
{
public DbSet<SubSubClass> SubSubClasses { get; set; }
public DbSet<SubClass> SubClasses { get; set; }
}
}
Can you tell me what's wrong?
I have found a work around for this issue I have experienced myself. I did post some supplementary information about what might be causing the issue but it was deleted by some egotistical nerd who felt it wasn't worthy.
So here is my ANSWER to this problem - might not be entirely right but it works for me so please don't delete this Mr Nerd just in case it helps out someone else.
Firstly, this issue only affects classes that do not inherit the abstract class using the NotMapped attribute directly but instead those that inherit from another class that itself inherits from the base class, as per the original statement.
The following works for me using something similar to the following class setup:
BASE CLASS:
public abstract class EntityBase
{
public virtual int Id { get; set; } // this will be mapped
public virtual int DoNotMap { get; set; } // this should be ignored
}
FIRST LEVEL INHERITANCE
public class Contact : EntityBase
{
public string Name { get; set; }
}
SECOND LEVEL INHERITANCE
public class Employee : Contact
{
public DateTime StartDate { get; set; }
}
Create a generic configuration class that inherits from EntityTypeConfiguration:
public class DataEntityBaseConfiguration<T>
: EntityTypeConfiguration<T> where T : EntityBase
{
public DataEntityBaseConfiguration()
{
Ignore(x => x.DoNotMap);
}
}
Create configuration classes for all first level inheritance that inherit directly from the EntityBase class, i.e.:
public class ContactConfiguration : DataEntityBaseConfiguration<Contact>
{
// place any further configuration rules in the constructor
}
WORKAROUND: any classes that inherit from the EntityBase indirectly, simply create a Configuration class that inherits from EntityTypeConfiguration:
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
// place any further configuration rules in the constructor
}
In your DataContext, in the OnModelCreating method, add you configurations:
modelBuilder.Configurations.Add(new ContactConfiguration());
modelBuilder.Configurations.Add(new EmployeeConfiguration());
Assuming a TPT approach in this instance, map the specialised version, Employee to its own table with an FK relation to Contact:
modelBuilder.Entity<Contact>().Map<Employee>(m => m.ToTable("Employees"));
This, for me at least, creates a contact table without the "DoNotMap" property and an Employee table without the "DoNotMap" table and with a PK/FK relation to Contact. Even though I do not inherit from the EntityBaseConfiguration for Employee Configuration, it still, somehow, picks up Ignore and leaves it out.
I would assume that if we went with a TPH approach, we would simply end up with a single Contact table plus Discriminator column. TPC would obviously recreate all the Contact properties in the Employee table but I'm not sure if it would also create the DoNotMap property - will test that when I have a moment spare.
In short, going back to the original question, I'm not sure why this happens with both the NotMapped attribute and the Ignore. I was getting the error when my EmployeeConfiguration inherited from the EntityBaseConfiguration. I'm not keen on the workaround as it firstly the error is false in its statement and secondly, it's a an error that would easy to fall into again, even if the solution is now quite simple.
Hope that helps anyone that has struggled with this inheritance issue.
Regards
The fact that the primary key has to be included in the POCO class definition A already means that Ais not a POCO object. You cannot ignore properties on POCO objects
Do you have a property called InnerString in your SubSubClass? That's what the error is saying, although you don't have it listed in your example code.
Your code above worked for me, but I had to add a PK, here's the entire Console app:
public class BaseClass
{
public int Id { get; set; }
[NotMapped]
public string InnerString { get; set; }
}
public class SubClass : BaseClass
{
}
public class SubSubClass : SubClass
{
}
public class MyDbcontext : DbContext
{
public DbSet<SubSubClass> SubSubClasses { get; set; }
public DbSet<SubClass> SubClasses { get; set; }
}
class Program
{
static void Main( string[] args )
{
var context = new MyDbcontext();
context.SubSubClasses.Add(new SubSubClass());
context.SaveChanges();
}
}
And the database it created: