I am trying to separate my domain model (which is responsible for my business logic) with my data model which is what EF 7 uses to build my schema.
I am running into a problem with regards to persisting changes made to my domain model to my database
So for example given that I have the Data Model PersonTable and the Domain model Person:
public class PersonTable
{
public virtual Guid Id { get; set; }
public virtual String Name { get; set; }
public virtual String Surname { get; set; }
}
public class Person
{
public virtual Guid Id { get; set; }
public virtual String Name { get; set; }
public virtual String Surname { get; set; }
//Other Domain Methods and Constructors...
}
And I want to persist my domain changes to my database by doing this:
public void Update(Person p)
{
var person = new PersonTable
{
Id = p.Id,
Name = p.Name,
Surname = p.Surname
}
PersonDbContext.Update(person);
PersonDbContext.SaveChanges();
}
When I try to apply these changes I get an InvalidOperationException saying
"The instance of entity type 'Tables.PersonTable' cannot be tracked because another instance of this type with the same key is already being tracked. For new entities consider using an IIdentityGenerator to generate unique key values."
I assume this has something to do with entity tracking but how does this work in EF 7? I want to be able to apply this pattern so that I can seperate these two models.
// Before updating we have fetch original data row from database.
// So we need to use AsNoTracking().
var originalPerson = PersonDbContext.Person.AsNoTracking().FirstOrDefault(p => p.Id == id);
and at time of save we need to use following code.
PersonDbContext.Entry(originalPerson).Context.Update(newPersonDetail);
The error occurs when your instance of PersonDbContext is already tracking an instance of Person with the same Id.
To debug this, inspect what you have tracking already in PersonDbContext.ChangeTracker.Entries(). I expect you will discover you already have an entry for PersonTable.Id that matches the one you are attempting to update.
To ensure you don't get these conflicts, don't reuse PersonDbContext across threads or multiple HTTP requests (if this is a web app.)
I was facing the same error. I found the answer in this other thread here
https://stackoverflow.com/a/31840042/1716912. I changed my repository DI to be scoped from being a singleton.
Related
Assuming this test model:
public class TestEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
}
When I generate a new instance of it, Id is 00000000-0000-0000-0000-000000000000.
Saving such an instance in the database as a new row, results in a Guid being generated (which is different from the empty one).
However, if I provide a valid Guid in TestEntity.Id, the new row is created with the provided Guid instead of a newly computed one.
I would like this behavior to exists only when editing a row, not when creating it. This is to ensure a database-layer protection from attacks where a user normally shouldn't get to choose which data to input.
Off course this protection is present in other layers, but I want it in the database too. Is this possible? How can I tell EF to ignore model data when creating a new row?
DatabaseGeneratedOption.Computed descriptions says
the database generates a value when a row is inserted or updated
So clearely that's not an option. I don't want to change Id when updating a row. I only want to be sure no one can create a row and choose the Id.
I'd try to keep things simple. Make your set method protected, then you have two ways to generate Ids, You can generate it by yourself inside a constructor:
public class TestEntity
{
// no need to decorate with `DatabasGenerated`, since it won't be generated by database...
[Key]
public Guid Id { get; protected set; }
public string Name { get; set; }
public TestEntity()
{
this.Id = Guid.NewGuid();
}
}
...or you can let the database generate it for you. At least for SQL Server, it will be able to generate for int and Guid as well:
public class TestEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; protected set; }
public string Name { get; set; }
// no need to generate a Guid by yourself....
}
This will avoid people from setting a value to Id outside the class (therefore no one can choose a Guid for new rows, or modify from existing ones).
Of course, your team could use reflection to by-pass class definitions, but if that's the case, you need to have a talk with your team.
If you still want to make sure they won't cheat, then you'd have to do check before saving changes to database, maybe overriding SaveChanges() in your DbContext.
As a side note, for both int and Guid, values are not generated by Entity Framework. Decorating the property with [DatabaseGenerated(DatabaseGeneratedOption.Identity)] will tell Entity Framework to generate a column with a default value coming from the own database provider.
I'm trying to fetch (in disconnected way) an entity with its all related entities and then trying to update the entity. But I'm getting the following error:
Attaching an entity of type 'Feature' failed because another entity of the same type already has the same primary key value.
public class Person
{
public int PersonId { get; set; }
public string Personname { get; set }
public ICollection Addresses { get; set; }
}
public class Address
{
public int AddressId { get; set; }
public int PersonId { get; set; }
public string Line1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public Person Person { get; set; }
public ICollection<Feature> Features { get; set; }
}
// Many to Many: Represented in database as AddressFeature (e.g Air Conditioning, Central Heating; User could select multiple features of a single address)
public class Feature
{
public int FeatureId { get; set; }
public string Featurename { get; set; }
public ICollection<Address> Addresses { get; set; } // Many-To-Many with Addresses
}
public Person GetCandidate(int id)
{
using (MyDbContext dbContext = new MyDbContext())
{
var person = dbContext.People.AsNoTracking().Where(x => x.PersonId == id);
person = person.Include(prop => prop.Addresses.Select(x => x.Country)).Include(prop => prop.Addresses.Select(x => x.Features));
return person.FirstOrDefault();
}
}
public void UpdateCandidate(Person newPerson)
{
Person existingPerson = GetPerson(person.Id); // Loading the existing candidate from database with ASNOTRACKING
dbContext.People.Attach(existingPerson); // This line is giving error
.....
.....
.....
}
Error:
Additional information: Attaching an entity of type 'Feature' failed because another entity of the same type already has the same primary key value.
It seems like (I may be wrong) GetCandidate is assigning every Feature within Person.Addresses a new instance. So, how could I modify the GetCandidate to make sure that the same instance (for same values) is bing assisgned to Person.Addresses --> Features.
Kindly suggest.
It seems like (I may be wrong) GetCandidate is assigning every Feature within Person.Addresses a new instance. So, how could I modify the GetCandidate to make sure that the same instance (for same values) is bing assisgned to Person.Addresses --> Features.
Since you are using a short lived DbContext for retrieving the data, all you need is to remove AsNoTracking(), thus allowing EF to use the context cache and consolidate the Feature entities. EF tracking serves different purposes. One is to allow consolidating the entity instances with the same PK which you are interested in this case, and the second is to detect the modifications in case you modify the entities and call SaveChanges(), which apparently you are not interested when using the context simply to retrieve the data. When you disable the tracking for a query, EF cannot use the cache, thus generates separate object instances.
What you really not want is to let EF create proxies which hold reference to the context used to obtain them and will cause issues when trying to attach to another context. I don't see virtual navigation properties in your models, so most likely EF will not create proxies, but in order to be absolutely sure, I would turn ProxyCreationEnabled off:
public Person GetCandidate(int id)
{
using (MyDbContext dbContext = new MyDbContext())
{
dbContext.Configuration.ProxyCreationEnabled = false;
var person = dbContext.People.Where(x => x.PersonId == id);
person = person.Include(prop => prop.Addresses.Select(x => x.Country)).Include(prop => prop.Addresses.Select(x => x.Features));
return person.FirstOrDefault();
}
}
When using Entity Framework Code First 4.3.1 it is possible to create relationships with a multiplicity of 1-to-1. That is, one entity on each end of the relationship.
It is possible to configure 1-to-1 relationships to be required-required or required-optional ^. However, when I switch between the two I do not see any differences in:
The database schema generated. I am targeting SQL Server 2008.
The runtime behaviour of EF.
As such, I am able to create a RequiredPrincipalAs record without a corresponding RequiredDependentAs record, despite the relationship being configured as required-required. This seems to contradict the documentation for HasRequired(...):
Configures a required relationship from this entity type. Instances of the entity type will not be able to be saved to the database unless this relationship is specified. The foreign key in the database will be non-nullable.
http://msdn.microsoft.com/en-us/library/gg671317
The required-required relationship entities:
public class RequiredPrincipalA
{
public int Id { get; set; }
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
The required-optional relationship entities:
public class RequiredPrincipalB
{
public int Id { get; set; }
public virtual OptionalDependentB DependentB { get; set; }
}
public class OptionalDependentB
{
public int Id { get; set; }
public virtual RequiredPrincipalB PrincipalB { get; set; }
}
The DbContext and model configuration:
public class AppContext : DbContext
{
public DbSet<RequiredPrincipalA> PrincipalAs { get; set; }
public DbSet<RequiredDependentA> DependentAs { get; set; }
public DbSet<RequiredPrincipalB> PrincipalBs { get; set; }
public DbSet<OptionalDependentB> DependentBs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RequiredPrincipalA>()
.HasRequired(o => o.DependentA)
.WithRequiredPrincipal(o => o.PrincipalA);
modelBuilder.Entity<RequiredPrincipalB>()
.HasOptional(o => o.DependentB)
.WithRequired(o => o.PrincipalB);
}
}
The test code:
Database.SetInitializer(new DropCreateDatabaseAlways<AppContext>());
using (var ctx = new AppContext())
{
ctx.Database.Initialize(force: false);
ctx.PrincipalAs.Add(new RequiredPrincipalA());
ctx.PrincipalBs.Add(new RequiredPrincipalB());
ctx.SaveChanges();
}
I am aware I could add a [Required] data attribute to the navigation properties of RequiredPrincipalA.DependentA and RequiredDependentA.PrincipalA. This would cause EF validation to prevent the scenario above. However, I do not want to do this because it also validates the navigation property is populated when updating an existing entity. This means the application has to pre-fetch the entity at the other end of the relationship for every update.
Why do I not see any difference in the behaviour of EF just when changing a relationship between required-required and required-optional?
^ Note that optional-optional is also supported but this doesn't form part of my question. There are obvious differences in the generated database schema and runtime behaviour when an optional-optional relationship is configured.
I don't know why required-required is allowed for this case but it cannot exist in the database because relation is build on primary keys. Required-required means that A cannot be inserted if related B doesn't exist and B cannot be inserted if related A doesn't exist => neither A or B can be inserted.
Database relation has always principal and dependent entity - principal can always exist without dependent.
Real required-required in EF can be achieved only when both A and B are mapped to the same table (table splitting) because in such case they are both inserted with single insert command.
Not really an answer but I have more to say than will fit in comments. But you know, I write 900 page books...it's just how I roll. :)
Oddly I would expect the fluent configuration to behave the same way as the data annotation and am confused that it's not doing it. (I've pinged Rowan Miller with a link to this thread to get his feedback.) And the behavior I mean is: validating the constraint during SaveChanges.
On the database side, I'm with Ladislav.In the model, EF defines the 1:1 using the keys of the related entities. But in the database, you can't have FKs in both tables, so only the dependent table in the database will require that constraint that it's PK maps to an existing PK in the principal table.
And finally, I understand your reason for not wanting EF to enforce the relationship if you aren't going to always deal with teh full graph. I think 1:1 relationships are the most confusing of the EF relationship mappings and I always find myself having to go back for reminders of the rules and how things should work.
Old question. But since EF6 is still used and even available for .Net standard and this issue can be a real nuisance, I think it's worth mentioning something I couldn't find in other answers.
It is true that both HasRequired - WithRequiredPrincipal and HasOptional - WithRequired produce the same database schema and the same runtime behavior. That is, with both mappings it's possible to save a principal without a dependent entity and to remove the dependent later. So much for HasRequired.
But there is a way to make EF validate the required relationship when creating the entities, which is by simply adding a [Required] attribute:
public class RequiredPrincipalA
{
public int Id { get; set; }
[Required] // <== here
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
As said, only when creating the entities. It's still possible to set RequiredPrincipalA.RequiredDependentA = null and save it successfully. But I think that, fortunately, the likelihood of that happening in code is far lower than forgetting to set the required dependent.
I'm using Entity Framework Code First. The class i'm trying to create contains two collections (of the same type). I'm having problem recovering my respective collections.
My classes look like this:
public class Destination
{
public int DestinationId { get; set; }
public string Name { get; set; }
public List<Lodging> Lodgings { get; set; }
public List<Lodging> Lodgings2 { get; set; }
}
public class Lodging
{
public int LodgingId { get; set; }
public string Name { get; set; }
public Destination Destination { get; set; }
}
I created a new Destination, then I reopened (closed & opened) the database connection. When I retrieve the destination, my collections (dest.Lodgings and dest.Lodgings2) are null. How do I restore the respective collections? If my class only has one collection of a particular type, I could do the following:
var lodgings = context.Lodgings.Where(l => l.Destination.DestinationId == destId).ToList();
I can see that the relationships are maintained in the database schema (Destination_DestinationId1 and Destination_DestinationId2) but I don't seem to be able to get to them.
Any suggestion would be appreciated.
In addition to using Include (as you've discovered) (which loads the related data from the db at the same time the destination is retrieved) you can also retreive the lodgings after the fact. So if you query for the destination and then you want the lodgings, that's possible. One way is called explicit loading where you will use a Load method. The other is with lazy loading, which requires that your classes be set up a particular way and just the mere mention of the Lodgings property will trigger the call to the database to retrieve them.
there's a great blog post on the Ef team blog about the various ways to load related data with DbContext : http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
hth
Julie
Say I have Project and Task EF Code first classes
public class Project
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int ID { get; set; }
public string Name { get; set; }
public int ProjectId { get; set; }
public bool IsDeleted {get; set;}
public virtual Project Project { get; set; }
}
Say I have
public void SomeAction()
{
Project p= repository.GetById(1);
var tasks = p.Tasks;
//var tasks = p.Tasks.Where(t=>t.IsDeleted==false);
}
I would like that my Tasks property on the Project class will always perform that filter on IsDeleted and just return that subset ... to avoid having to write that condition all over the place...
Any recommendations?
Edit:
Im using EF Code First
Add a discriminator to your model in the OnModelCreating method
modelBuilder.Entity<TEntity>().Map(m => m.Requires("IsDeleted").HasValue(false));
Caveats
You can no longer load deleted items (unless you map IsDeleted true to another entity, then you may lose your automatic filtering)
The poco class cannot have the IsDeleted property (discriminators cannot be mapped)
because the IsDeleted cannot be mapped you need to run raw SQL to delete the entity in the first place.
EF Code first = NO WAY. Just one from long list of features which is available in EDMX and it is completely missing in code first. Mapped condition from EDMX does this but it is still problematic because it is hardcoded and cannot be changed (= you will never be able to load deleted entities even if you want to unless you use another EDMX). The solution would be implementation of global filters in EF but EF doesn't have anything like that despite the fact that old Linq-to-entities have them at least for relations (DataLoadOptions.AssociateWith).
This is much more painful in relations where you cannot use eager or lazy loading without loading deleted entities to your application as well and do filtering in your application's memory.
In the Model Designer, select your Task entity, and bring up the Mapping Details window. This should show you the database table your entity is mapped to, and all the columns. Just under where it says "Maps to [YourTable]" you should see an option <Add a Condition>. This should let you set a condition like what you're looking for.