Foreign key values without navigation properties - c#

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;

Related

EFCore adding temporary ID by AddAsync()

I am using EFCore 5.0.0.
When I AddAsync (person); I should be getting a temporary ID, and I use this ID to add the PersonId for School (shown in code below). FInally, I will SaveChangesAsync() where everything will be saved. However, the PersonId is set to 0. I want to get the temporary ID stored instead. How can I do this.
await _dbContext.AddAsync(person);
School school = mySchool;
school.PersonId = person.Id;
await _dbContext.AddAsync(school);
await _dbContext.SaveChangesAsync();
Note: There are many SO post that talks about the temporary ID, but none is related to this post.
Currently accepted answer is valid, but technically incorrect. Assigning navigation property is valid approach, but not mandatory. It's even perfectly valid to not have navigation property at all. As well as explicit FK property. But there is always at least shadow FK property which can be used to setup/maintain the relationship.
So the temporary key concept is part of the EF Core from the very beginning. However EF Core 3.0 introduced a breaking change - Temporary key values are no longer set onto entity instances. The link contains an explanation of the old and new behaviors, the reason and possible solutions:
Applications that assign primary key values onto foreign keys to form associations between entities may depend on the old behavior if the primary keys are store-generated and belong to entities in the Added state. This can be avoided by:
Not using store-generated keys.
Setting navigation properties to form relationships instead of setting foreign key values.
Obtain the actual temporary key values from the entity's tracking information. For example, context.Entry(blog).Property(e => e.Id).CurrentValue will return the temporary value even though blog.Id itself hasn't been set.
Bullet #1 makes no sense, Bullet #2 is what is suggested in the other answer. Bullet #3 is the direct answer/solution to your question.
And applying it to your example requires just changing
school.PersonId = person.Id;
to
school.PersonId = _contexy.Entry(person).Property(e => e.Id).CurrentValue;
Of course when you have navigation property and the related entity instance, it's better to use it and let EF Core do its magic. The temporary key is really useful when you don't have navigation property, or you don't have related entity instance and know the key, but don't want to do roundtrip to load it from database (and using fake stub entity instance can lead to unexpected side effects/behaviors). It works well with both explicit and shadow FK properties.
I've never seen linking entities in EF Core using the temporary id.
Typically what you would do is assign the entity and let EF sort out the ids and relationships.
i.e. in this instance, the School will be linked to the Person.
await _dbContext.AddAsync(person);
School school = mySchool;
school.Person = person;
await _dbContext.AddAsync(school);
await _dbContext.SaveChangesAsync();

(EF6 Model first) How can I get the value of a foreign key without accessing the navigation property so that I don't need to load the full entity?

How can I get the value of a foreign key without accessing the navigation property so that I don't need to load the full entity?
public class A
{
public int Id {get;set;}
// ...
}
public class B
{
public virtual A A {get;set;}
// ...
}
int idOfA = MyB.A.Id; // slow way of doing it.
I'm using Entity Framework 6 ModelFirst+DbContext in VS2012.
Previously I used an old EF Version+ModelFirst+ObjectContext.
I have bitten the bullet and migrated by creating the model from the old database.
Getting the entity key was possible with the old ObjectContext:
EntityReference<EntityType> reference = MyB.AReference; // AReference was generated by EF
int id = (int)reference.EntityKey.EntityKeyValue[0].Value;
But now the code generator no longer generates EntityReferences for each One or ZeroOrOne navigation property.
So how can I get the foreign key in EF6+DbContext?
Here are some ideas of what I tried/could do but failed/didn't want:
I could just ignore the bad performance and load the full entity to get its primary key.
Using the [ForeignKey]attribute or EF's Fluent API. But I don't know how I can or if I should do that. Maybe it doesn't even work with a model first approach, because "OnModelCreated" isn't called.
I modify the database-entity mapping (the xml stuff in the edmx code) so that for each foreign key an additional property (public int *Id {get;set;}) will be mapped. But I never did that and don't know where to start reading.
When I created the first EF 6.0 model from my old database, there was an option "Include foreign key columns in the model". I didn't activate that last time, which in hindsight was wrong. But doing it again would be a lot of work (manually setting entity inheritance, etc.).
Using the relationship manager. But my generated entities no longer seem to implement the IEntityWithRelationships interface. Even though I think I satisfy the conditions for proxies and I checked in the debugger that proxies classes are created.
(edit: Someone else had a similar problem with IEntityWithRelationships+IEntitiyWithChangeTracker. See solution in the comments of this question.)
Here's the code that I use (granted it's not Model First, but there's nothing specific in it to either approaches).
In the sample I have products, and to each product belongs a category. Here's how I can query the CategoryId in a Product entity wihtout loading the Category:
Model1 context = new Model1();
var product = context.Products.First();
RelationshipManager relMgr = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetRelationshipManager(product);
IEnumerable<IRelatedEnd> relEnds = relMgr.GetAllRelatedEnds();
IRelatedEnd relEnd = relEnds.Where(r => r.RelationshipName.EndsWith("Product_Category")).Single();
EntityReference<Category> entityRef = relEnd as EntityReference<Category>;
var entityKey = entityRef.EntityKey;
int categoryId = (int)entityKey.EntityKeyValues[0].Value;

Updating foreign key in Entity Framework 7

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.

Assign a yet unassigned ID to a new entity

We're trying to set up a shadow copy system for auditing some of the tables in our projects database. For Any change (Add, Update, Delete) a copy of that record get's saved to it's shadow table (we're using the term Version).
If we have a table called Profiles with columns (int)ProfileId, (varchar(50))Name, (date)UpdateDate, etc... we would have another table called ProfilesVersion with columns (int)ProfileVersionId, (int)ProfileId, (varchar(50))Name, (date)UpdateDate, etc...
I'm working on the system to make the copies. In the past I have used triggers in the database to catch Insert, Update, Delete. But now we're trying to do it using Entity Framework and Linq.
I can override the SaveChanges on DbContext, and get a second copy into the Version table. However, the key Id that get's populated on the first table does not end up in the Version table.
With Entity Framework, you can have two inserts to the database with data from one entity getting applied to the second. For instance:
var res = new Resource{
SomeValue = someParameter
};
_db.Resource.Add(res);
var newProfile = new Profile{
ProfileValue = anotherParameter,
ForeignResourceId = res.ResourceId // ResourceId is autogenerated
};
_db.Profile.Add(newProfile);
_db.SaveChanges();
var forResourceId = newProfile.ForeignResourceId;
Since Profile.ForeignResourceId and Resource.ResourceId are mapped in the model, the newProfile object has the ForeignResourceId that was assigned by the database after SaveChanges(). Somehow entity framework knows to put res.ResourceId into ForeignResourceId once it has been generated from the database.
My code which dynamically copies values from one entity into the Version table does not do that. It simply copies data from the first entity into the new record for the Version entity, but doesn't setup the relationship to populate the key field with the foreign key.
public int SaveChanges(Guid userId)
{
// ... some other code
// entityEntry is DbEntityEntry, the entity with changes we want to replicate
// Create audit entity.
DbSet set = this.Set(auditTypeInfo.AuditEntityType);
IAuditEntity auditEntity = set.Create() as IAuditEntity;
set.Add(auditEntity);
// Copy the properties.
DbEntityEntry auditEntityEntry = this.Entry(auditEntity);
foreach (string propertyName in auditTypeInfo.AuditProperties)
{
// This copies just the raw value, if any
auditEntityEntry.Property(propertyName).CurrentValue = entityEntry.Property(propertyName).CurrentValue;
}
// ...
return base.SaveChanges();
}
So, following with our example, if we add a Profile record, it get's it's ProfileId, but the ProfileVersion record does not.
How in the above code can I have entity framework set that value in the 'auditentity' that we are copying to?
If I understood you case correctly, then:
This will have to do with properties for your entity. If you entity has property (which, I suppose, is a key for you entity) has DatabaseGeneratedOption.Identity (assigned at OnModelCreating), which translates to IDENTITY (1,1) at sql level, there's nothing you can do, because all of that is being handled at database, not ORM level.
What you could do in this case, use IDENTITY_INSERT, which would allow you to assign Ids, but, it means that you would also have to generate Ids manually.
In short - get rid of automatic identity generation.

Entity Framework 4.1 - default EntityState for a FK?

I'm having a small problem with ASP.NET MVC and Entity Framework 4. I have an entity called "UF" and another one called "Pais", and they have this relation:
UF [* ... 0..1] Pais
I can access the Pais object directly from UF using a navigation property:
UF.Pais.Whatever = "foobar";
Currently I have a View which inserts a new item into the database, and it has an editor for "Pais.Codigo" ("Codigo" is the primary key for Pais). So when I insert a new UF, the framework creates an instance of class UF with a reference to an instance of class Pais. Then this is done:
if (ModelState.IsValid)
{
db.UFs.AddObject(uf);
db.SaveChanges();
return View();
}
The problem is that the EF is inserting a new Pais into the database, so it basically ignores the existing one.
For example, if let's say my object UF has a Pais with an ID of 1. The current value of uf.Pais.Codigo is 1. Other attributes, like the description, is currently null. When I execute the SaveChanges, both "uf" and "uf.Pais" are with the state of Added. The correct state for "uf.Pais" should be Unchanged, since it already exists on the database.
My question is: there's some way of changing the default relationship EntityState for Unchanged? The following code solves the problem, but adding it to each function with adds a new entry to the database (and for each FK) is overkill!
db.ObjectStateManager.ChangeObjectState(uf.Pais, EntityState.Unchanged);
That's it. I'm not sure if I was clear enough. Feel free to ask more information if needed. And sorry for any english mistakes!
Thanks,
Ricardo
PS: "Pais" stands for Country and "UF" for State.
My question is: there's some way of changing the default relationship
EntityState for Unchanged?
Yes by calling Attach instead of Unchanged.
The following code solves the problem, but adding it to each function
with adds a new entry to the database (and for each FK) is overkill!
No it is not overkill, it is a solution because either Attach or AddObject will always make the operation for all entities and associations in entity graph. That means that calling AddObject will make everything what context is not aware of yet as Added and Attach will make everything what context is not aware of as Unchanged (so you will in turn have to set each modified or inserted entity to its correct state). That is how EF works if you are using detached entities.
Another solution for the problem is making the connection after the UF is added:
// Here UF.Pais is null
db.UFs.AddObject(uf);
// Create dummy Pais
var pais = new Pais { Id = "Codigo" };
// Make context aware of Pais
db.Pais.Attach(pais);
// Now make the relation
uf.Pais = pais;
db.SaveChanges();
If you are working with detached entities you are always responsible for setting the correct state for each entity (and independent association). So you will either use attached entities to let EF make the magic for you (as shown in the example) or you will use the approach you dislike. In more complex scenarios you can find out that best approach is to load entity graph again and merge incoming changes into the attached graph.

Categories