I am trying to update a foreign key using Entity Framework 7. But it is giving error: The property 'Y' could not be found in object 'X'. I have tried many different solution but still not working. The sample code:
class X
{
property Y {get; set;} -> property Y is a foreign key and also a complex type
}
In table 'X' we have a column 'Y_ID' which is the foreign key.
Note: I just want to update the foreign key. E.g. Initially class 'X' is pointing to 'NULL', I want to update class 'X' to point to 'Y1'
The Entity Framework 7 code:
var x = this.GetX();
this.mainContext.Xs.Attach(x);
var xEntry = this.mainContext.Entry(x);
xEntry.Property("Y").CurrentValue = "Y1"; // Error at this line
await this.mainContext.SaveChangesAsync().ConfigureAwait(false);
Detailed Error:
The property 'Y' on entity type 'X' could not be found. Ensure that the property exists and has been included in the model.
Edit
The approach Fabien suggested in his comment works fine. But the problem is we only know about which property to update is at runtime. If I use reflection to achieve this, the problem is entity framework treats the object as new and tries to create it (INSERT) and then throws Primary Key violation (No duplicate entries allowed)
So, is there a way where I can't still update an object property which acts like a foreign key in EF? (I don't know exact property at compile time).
If you get the entities "X" and "Y" from your context, then they're automatically tracked by the ChangeTracker. So if you assign "Y" property of the "X" object with an "Y" instance retrieved from your context and call SaveChanges or SaveChangesAsync, EntityFramework will automically do the stuff for you.
var x = this.GetX();
x.Y = "Y1";
await this.mainContext.SaveChangesAsync().ConfigureAwait(false);
By convention, your property "Y" on object "X" should be virtual to indicate that it's an foreign key.
Edit 1 :
If I understand correctly, you want to update properties of your object dynamically at runtime, with values that comme from a web api.
1st way :
Like you did, you can attach your "X" object to your context instance to begin tracking of the entity with EntityState.Unchanged, and then flag each property that need to be updated :
this.mainContext.Xs.Attach(x);
var entry = this.mainContext.entry(x);
entry.Property(p => p.Y).CurrentValue = "Y1";
await this.mainContext.SaveChangesAsync().ConfigureAwait(false);
When attaching an entity, you can specify the GraphBehavior, it tell EntityFramework if navigation properties should traversed or not.
2nd way :
Using the DbSet.Update() method :
this.mainContext.Xs.Update(x);
await this.mainContext.SaveChangesAsync().ConfigureAwait(false);
It's automatically begin tracking of the entity with the state EntityState.Modified, all properties will be marked as modified. You should watch out when using this method, because all properties will be updated, if some of them are not inititialized in your "X" object, you could lost some data. To prevent that case, you should always validate inputs.
If you want to keep your domain models de-coupled form any ORM, then you should think to separate entity types and domain types. You can use an object mapper like Automapper to map entity to domain type and vice versa. In that way you clearly separate what you do at data access layer and business logic layer.
Related
I have a POCO class (OPERATION) that is used as an Entity Framework entity. This class has a navigation property (OP) and a foreign key into the same related entity (OP_ID).
In a method, I get an OPERATION and on this OPERATION the OP_ID and OP are both null. When I set the OP_ID to a valid value for this foreign key, the OP navigation property remains null. When I explicitly detect changes in the context, the OP navigation property is now assigned with the correct value.
Sample code
public bool UpdateOperation(operationID)
{
IQueryable<OPERATION> operations = from o in base.ctx.OPERATION
select o;
OPERATION operation = operations
.Where(o => o.OPERATION_ID == operationID)
.Include("OP")
.FirstOrDefault();
if (operation != null)
{
operation.OP_ID = opId;
}
// operation.OP is null here
operation.GetContext().ChangeTracker.DetectChanges();
// operation.OP is populated here
}
I have confirmed that the operation is, in fact, a dynamic proxy. For what it's worth, once I detect changes, operation.OP also becomes a dynamic proxy. However, even then, assigning a different value to operation.OP_ID still requires an explicit DetectChanges() call in order to update the value of operation.OP.
Update
In response to the comment from #ErikPhilips, the documentation here seems to imply that this should happen. Specifically:
The following examples show how to use the foreign key properties and navigation properties to associate the related objects. With foreign key associations, you can use either method to change, create, or modify relationships. With independent associations, you cannot use the foreign key property.
By assigning a new value to a foreign key property, as in the following example.
course.DepartmentID = newCourse.DepartmentID;
...
When you change the relationship of the objects attached to the context by using one of the methods described above, Entity Framework needs to keep foreign keys, references, and collections in sync. Entity Framework automatically manages this synchronization (also known as relationship fix-up) for the POCO entities with proxies.
If you are using POCO entities without proxies, you must make sure that the DetectChanges method is called to synchronize the related objects in the context.
Some additional context may be useful, as well. This is a legacy application that used to work directly with an ObjectContext instead of a DbContext, though even then using EF 6. We are now migrating to the DbContext API. This particular code, without any modifications, used to demonstrate the behavior I'm expecting. Specifically, when OP_ID is assigned, I can see in the debugger that the OP property is automatically populated to point to the correct OPERATION.
In the end, I was doing exactly what the documentation described. I was
assigning a new value to a foreign key property.
Yes, Entity Framework does manage this in fix-up. And yes, the documentation does state this.
It turns out, though, that the egg is ultimately on my face. I had checked the classes generated from my T4 template, and seen that all navigation properties were marked virtual. I had not checked thoroughly enough to note that the foreign key properties were not marked virtual, however. It appears that this is the default behavior of the EF-provided T4 template used when working model- or database-first. I've addressed this by changing this line in the CodeStringGenerator.Property() method in the T4 template
Accessibility.ForProperty(edmProperty)
to
AccessibilityAndVirtual(Accessibility.ForProperty(edmProperty))
In the end, as usual, following the documentation (here, the requirements for EF change tracking on POCOs) often results in dependent code behaving as it is documented. Shame on me.
I am trying to log changes to the database so that the user can see who changed what. I am using the DbEntityEntry to go through and log the DbPropertyEntity that have been modified. I am running into a problem when I want to log changes to a navigation property. I use the Reference() method to get reference to the navigation property, however unlike DbPropertyEntity, DbReferenceEntry does not have a OriginalValue only a CurrentValue attribute. How do you get the OriginalValue of a navigation property?
//Get the field that hold the id of the foreign key
var field = entry.Property(x => x.field);
//Check to see if the user changed the value
if (field.IsModified)
{
//Get the reference property associated with the field
var fieldRef = entry.Reference(x => x.fieldRef);
//Log the id change
Log(field.Name, field.CurrentValue, field.OriginalValue);
//Can't get the OriginalValue
Log(fieldRef.Name, fieldRef.CurrentValue, ???);
}
What exactly do you expect to log for the reference?
If you want to log changes in relation (= changes in foreign key) you should map the foreign key as separate property and it will be logged as normal field. If you don't map the foreign key you have an independent association. Support for independent associations in DbContext API is limited. The full support is only in ObjectContext API (you can use it from DbContext API) but it will still not solve your problem - independent association cannot be in modified state. It can be either in unchanged, added or deleted state. If you change the reference the old association is marked as deleted and new is added = there are two separate association objects tracked by the context.
.Net4 Entity Framework, N-Tier (so objects are detatched)
I have 2 objects generated Database first such that object1 has a Navigation Property (1 - 1) to object 2.
I can successfully make changes to other properties of object 1, but when I try to change object2 I get an error.
My webpage has a drop down list of object2 names and indices.
I have tried setting the object1.object2Id property and saving it and I get a referential Key error. (I can see that this may be because the object still holds the original object2).
If however I load in the new object2 and attempt to update object1 I get the object could not be added or attached because its EntityReference has an Entity Key Property that does not match.
So I seem to be going round in circles.
So using Detached objects and Entity Framework, what is the correct way of updating a child object / foreign key?
Ok, Figured it out. Its a result of me working in detatched mode. If I wait until I am at the Business tier about to write the changes, and change the fk once I have re attached the object to the context it works.
Just one of things I needed to learn I guess !
Is it possible to assign foreign key values manually when inserting records?
I do not want to use a TransactionScope or similar construct. But i do want to set the foreignkey value before calling SaveChanges()
For example:
EntityX x = new EntityX();
x.Name = "test";
ctx.AddToEntityX(x);
EntityY y = new EntityY();
y.Name = "Test";
y.EntityXID = x.ID; // <--- I want this. Not using a navigation property, but its 0.
ctx.AddToEntityY(y);
ctx.SaveChanges();
yes it is possible but a lot of trouble you have to assign it trough EntityReference :
y.EntityXReference.EntityKey = new EntityKey("Enitites.YSet", "Id", x.id);
see EntityKey Constructor
for details of parameters
for other reference see Tip 7 - How to fake Foreign Key Properties in .NET 3.5 SP1
I don't see any problem with using navigation property in your example. Also there is no need for transaction scope because SaveChanges uses transaction internally.
Theoretically if you delete all associations in your conceptual model (EDMX designer) and manually delete all associations in SSDL part of EDMX file and then map FKs to new scalar properties you should be able to do that. But you will degrade EF so much that you should even not use it and revert back to ADO.NET or Linq-to-sql. Moreover once you touch SSDL part of EDMX you can't use Update from database anymore.
If you create a new entity it won't have an ID until it get's persisted. Then you would have to retrieve it from the DB and get the idea. Using navigation properties is definitely your best choice in this example. So instead of:
y.EntityXID = x.ID;
you would use
y.EntityX = x;
Im dealing with code first .NET 4 and i'm having trouble with a 1 to 1 relation.
breifing of database:
-POccurrence
-Id
-POccurrenceRiskAssessment
-OccurrenceId
in my class Poccurrence I have a property named RiskAsessment, of the type POccurrenceRiskAssessment. Not all POccurrences have riskassessments, so it needs to be nullable.
I tried
modelBuilder.Entity<POccurrence>().HasOptional(item => item.RiskAssessment).HasConstraint((o, r) => r.OccurrenceId == o.Id);
but that gives me
The navigation property
'RiskAssessment' declared on type
'AM.Pris.Classes.POccurrence' has been
configured as optional. Based on a
declared constraint, the navigation
property is required. Either make some
dependent key property nullable or
configure the navigation as required.
and if i try
modelBuilder.Entity<POccurrence>().HasRequired(item => item.RiskAssessment).HasConstraint((o, r) => r.OccurrenceId == o.Id);
i get
A referential integrity constraint
violation occurred: A primary key
property that is a part of referential
integrity constraint cannot be changed
when the dependent object is Unchanged
unless it is being set to the
association's principal object. The
principal object must be tracked and
not marked for deletion.
and i have no idea what to do. I even tried to delete the real relation in the DB but nothing seems to make any difference. Any idea? I guess its the first try with HasOptional i'm looking for, but how do i make it nullalbe?
Have you considered rolling this up into an Table Per Type inheritance scenario where POccurrenceRiskAssessment : POccurrence? That way you only need query POccurrenceRiskAssessment.