I am currently working with efcore on an Blazor Server-Side-App and run into to following Problem.
Situation is like following:
I do have a Parent, Child and Family Class which are "connected" to eachother.
class Parent
{
public int ParentId { get; set; }
ICollection<Child> children {get;set;}
}
class Child
{
public int ChildId { get; set; }
public Parent ParentId { get; set; }
}
class Family
{
public int FamilyId { get; set; }
public ICollection<Parent> Parents {get;set;}
public ICollection<Child> Children {get;set;}
}
Now I do have a Dialog where you can create a new Parent and you can also add a Child to the Parent. The Family was already created in a View before.
My Code inside the Dialog for Parent and Child looks like this:
#code {
private Parent parent;
private Family Family;
ICollection<Family> families;
protected override async Task OnInitializedAsync()
{
using var context = DbFactory.CreateDbContext();
families = await context.families.ToListAsync();
Family = families.FirstOrDefault();
}
void CreateParent()
{
parent = new Parent();
parent.Family = Family;
}
void AddChildToParent()
{
Child child = new Child();
child.Family = Family;
parent.children.Add(child);
}
void Save()
{
using var context = DbFactory.CreateDbContext();
context.Parents.Update(parent);
context.SaveChanges();
}
}
When i try to save this, then EFcore throws an InvalidOperationException: The Instance of entity type "Family" cannot be tracked because another instance is already beeing tracked.
What am i doing wrong?
Btw. Code ist just pseudo
Are the instances of parent and Family in the private fields are coming from ef core and tracked?
If so, then no need to call Update on Save method, but just context.SaveChanges();
void Save()
{
using var context = DbFactory.CreateDbContext();
//context.Parents.Update(parent); omit this line
context.SaveChanges();
}
Otherwise, you can do the following:
void Save()
{
using var context = DbFactory.CreateDbContext();
context.Families.AsNoTracking.Update(Family);
context.SaveChanges();
}
Related
I'm trying to use AutoMapper v6.1.1 to map a class using projection, but AutoMapper doesn't include deeply nested objects.
I've attached a complete Visual Studio 2015 solution with a unit test here:
https://www.dropbox.com/s/omue5ou5dvxsa57/UnitTestProject2.zip?dl=0
I'm basically trying to map a Child and Parent hierarchy into a Person hierarchy, but the grand-Parents aren't getting included in the projection result.
Models:
public class Child
{
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
public class Parent
{
public string Name { get; set; }
public virtual Parent GrandParent { get; set; }
}
public class Person
{
public string Name { get; set; }
public virtual Person Parent { get; set; }
}
Mapping profile:
public class PersonProfile : Profile
{
public PersonProfile()
{
this.CreateMap<Child, Person>()
.MaxDepth(5);
this.CreateMap<Parent, Person>()
.ForMember(destinationMember => destinationMember.Parent, memberOptions => memberOptions.MapFrom(sourceMember => sourceMember.GrandParent))
.MaxDepth(5);
}
}
Unit test:
[TestClass]
public class UnitTest1
{
IMapper mapper;
List<Child> children;
[TestInitialize]
public void TestInitialize()
{
MapperConfiguration configuration = new MapperConfiguration((config =>
{
config.AddProfile(new PersonProfile());
config.ForAllMaps((mapType, mapperExpression) =>
{
mapperExpression.MaxDepth(5);
});
}));
this.mapper = configuration.CreateMapper();
mapper.ConfigurationProvider.AssertConfigurationIsValid();
this.children = new List<Child>
{
new Child
{
Name = "Child1",
Parent = new Parent
{
Name = "Parent1",
GrandParent = new Parent
{
Name = "GrandParent1",
GrandParent = new Parent
{
Name = "GreatGrandParent1"
}
}
}
}
};
}
[TestMethod]
public void TestProjection()
{
IQueryable<Person> people = children.AsQueryable().ProjectTo<Person>(mapper.ConfigurationProvider);
AssertPeople(people);
}
[TestMethod]
public void TestMap()
{
List<Person> people = mapper.Map<List<Child>, List<Person>>(children);
AssertPeople(people.AsQueryable());
}
private void AssertPeople(IQueryable<Person> people)
{
Assert.IsNotNull(people);
Assert.AreEqual(1, people.Count());
Person child1 = people.ElementAt(0);
Assert.AreEqual("Child1", child1.Name);
Person parent1 = child1.Parent;
Assert.IsNotNull(parent1);
Assert.AreEqual("Parent1", parent1.Name);
Person grandParent1 = parent1.Parent;
Assert.IsNotNull(grandParent1); // fails when using ProjectTo
Assert.AreEqual("GrandParent1", grandParent1.Name);
}
}
Using the Map method works but ProjectTo doesn't.
The classes in the sample code are much simpler than those used in production.
I'm trying to use projection so that I can return an IQueryable<Person> from OData and take advantage of the SQL generated by LINQ to Entities with query options automatically applied.
Any help is appreciated.
Thank you!
I think this describes the issue:
https://github.com/AutoMapper/AutoMapper/issues/2171
But as a workaround is it not possible to create an extension method that basically calls the Map internally:
public static class Extenstions
{
public static IQueryable<TDestination> ProjectToExt<TDestination, TSource>(this IQueryable<TSource> #this,
IMapper mapper)
{
return mapper.Map<IEnumerable<TDestination>>(#this).AsQueryable();
}
}
Then the calling code is like:
IQueryable<Person> people = children.AsQueryable().ProjectToExt<Person, Child>(mapper);
Suppose that we have two classes as bellow:
class Parent
{
public int ParentId {get; set;}
}
class Child : Parent
{
public int ChildId {get; set;}
}
According to the above, which line is correct and why? and also which line is not correct and why?
Parent p = new Child();
Child c = new Parent();
Update: I forget to inherit Child from Parent and I correct it!
First one is correct, second is not.
By definition, a Child will inherit all properties and methods of a Parent; however, Parent will not have all properties and/or methods of a Child, so the second statement wouldn't make sense:
class Parent
{
public int ParentId { get; set; }
public void Eat { ... }
}
class Child : Parent
{
public int ChildId { get; set; }
public void Play { ... }
}
Parent child = new Child();
child.Eat(); // this makes sense since this is common functionality
Child parent = new Parent();
parent.Play() // this does not make sense since a Parent doesn't know hot to play
Using EF 6.0, I have the following code in my app
using (var db = new TrackingEntities())
{
foreach (var p in points)
{
var parent= db.Parents.Find(points[0].Imei);
var record = new Child()
{
.....
};
parent.Children.Add(record);
db.SaveChanges();
}
}
and it throws
Additional information: Store update, insert, or delete statement
affected an unexpected number of rows (0). Entities may have been
modified or deleted since entities were loaded. Refresh
ObjectStateManager entries.
Update:
Something weird is happening. When I test the code while in started it to debug, it works fine and add children to parent. But when I disconnected it from debug and run the code again, it throws the mentioned error.
Here is the models:
public partial class Child
{
public long Id { get; set; }
public string ParentPK { get; set; }
public virtual TB_Parent TB_Parent { get; set; }
}
public partial class TB_Parent
{
public long ParentID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public TB_Parent()
{
this.Children = new HashSet<Child>();
}
}
You need to add the Child to the appropriate DB Set prior to saving.
Try db.Childs.Add(record); or the equivalent depending on how you've named things.
Suppose we have these two classes:
public class Parent
{
public int ID { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
public int ID { get; set; }
public int ParentID { get; set; }
public virtual Parent Parent { get; set; }
}
Suppose I create one of each with the following methods:
//Create a parent with new children
public void CreateParent(MyDbContext context)
{
context.Parents.Add(new Parent
{
Children = new List<Child>()
{
new Child(),
new Child(),
new Child()
}
});
context.SaveChanges();
}
//Create a child with a new parent
public void CreateChild(MyDbContext context)
{
context.Children.Add(new Child
{
Parent = new Parent()
});
context.SaveChanges();
}
Will either of these methods create both Parent and Child objects with foreign keys appropriately assigned? My gut tells me that CreateParent would work, but CreateChild would not.
Thank you!
Yes, both of these methods will create the Parent and Child records as well with the ParentID foreign key set. If you run e.g. the first one, the result you will get will be the following:
And as another answer states you don't need the ParentID however Entity Framework will recognize it as the Foreign Key used for the Parent association and you don't have to use the virtual keyword neither at the Children nor at the Parent property to make your code work, however if you wan't Lazy Loading to work with these properties you will need them.
Yes both functions work correctly, adding both Parent and Child records with the correct association between them.
Running CreateParent will yield one new Parent record and three Child records associated with it (through ParentID).
Running CreateChild will create one new Child and one new Parent record, associated correctly.
I've got an entity that has a property that's an abstract type. This creates a one-to-one relationship that uses table-per-hierarchy inheritance. Everything seems like it's working correctly.
I can create an Item and set the Base property to ConcreteOne; everything saves correctly. However, when I try to update Base to ConcreteTwo, EF updates the Base record in the database with the new user value, it doesn't update the discriminator for the type. So the extra data for ConcreteTwo gets persisted, but the discriminator still says ConcreteOne.
The following is a simple example that exposes the problem
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
App_Start.EntityFrameworkProfilerBootstrapper.PreStart();
Database.SetInitializer(new DropCreateDatabaseAlways<DataContext>());
// Create our item with ConcreteOne for Base
using (var context = new DataContext())
{
var item = new Item
{
Base = new ConcreteOne { Name = "Item", Data = 3 }
};
context.Items.Add(item);
context.SaveChanges();
}
// Update Base with a new ConcreteTwo
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
var newBase = new ConcreteTwo()
{
Item = item,
Name = "Item 3",
User = new User { Name = "Foo" }
};
// If I don't set this to null, EF tries to create a new record in the DB which causes a PK exception
item.Base.Item = null;
item.Base = newBase;
// EF doesn't save the discriminator, but DOES save the User reference
context.SaveChanges();
}
// Retrieve the item -- EF thinks Base is still ConcreteOne
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
Console.WriteLine("{0}: {1}", item.Name, item.Base.Name);
}
Console.WriteLine("Done.");
Console.ReadLine();
}
}
public class DataContext : DbContext
{
public DbSet<Item> Items { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Base Base { get; set; }
}
public abstract class Base
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Item Item { get; set; }
}
public class ConcreteOne : Base
{
public int Data { get; set; }
}
public class ConcreteTwo : Base
{
public virtual User User { get; set; }
}
}
When the changes are saved, EF generates the following SQL:
update [dbo].[Bases]
set [Name] = 'Item 3' /* #0 */,
[User_Id] = 1 /* #1 */
where (([Id] = 1 /* #2 */)
and [User_Id] is null)
So it's almost correct, but I'd expect to see [Discriminator] = 'ConcreteTwo' in the update statement. Are my expectations unfounded or am I doing something wrong?
As a test, I tried using table-per-type and the the entry was removed from the ConcreteOne table and added to the ConcreteTwo table as I would expect. So it works, but my real application has at least seven sub-types and the SQL statement to retrieve the Base property got really nasty. So I'd certainly like to accomplish this using TPH, if possible.
Update:
I've verified that the problem exists in EF5 as well as EF6.
This question is based on the expectation of an update taking place, which is seemingly a debatable expectation. Currently your best bet if the TPH hierarchy is not functioning as expected, and considering EF6 is currently in beta, is to start a discussion on the Codeplex forums.
Add this to your model:
public enum BaseType
{
ConcreteOne = 1,
ConcreteTwo = 2
}
public abstract class Base
{
...
public BaseType BaseType { get; set; }
...
}
And in the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Base>()
.ToTable("Base");
modelBuilder.Entity<ConcreteOne>()
.Map(t => t.Requires(m => m.BaseType).Equals(BaseType.ConcreteOne))
.ToTable("ConcreteOne");
modelBuilder.Entity<ConcreteTwo>()
.Map(t => t.Requires(m => m.BaseType).Equals(BaseType.ConcreteTwo))
.ToTable("ConcreteTwo");
}
I would expect this to create a new instance (record) with a ConcreteTwo discriminator.
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
var newBase = new ConcreteTwo()
{
Name = "Item 3",
User = new User { Name = "Foo" }
};
item.Base = newBase;
context.SaveChanges();
}