EF Core add/save by Id OR entity reference - c#

I have an entity I'm initiating to be saved as a record via EF Core code-first.
Sometimes I will have the related entity model - usually when I'm creating a new related entity at the same time, so it won't have an Id yet.
But sometimes I will only have the ForeignKey Id of the related entity, particularly when I'm adding just this new record related to an existing record.
I haven't been able to find anything on this specific dual-create-mode pattern, is this the correct convention in this situation?
public class ClownModel {
public ClownModel (){}
public ClownModel (CarModel car){
Car = car;
}
public Clown (int carId){
CarId = carId;
}
public int Id {get;set;}
public int? CarId {get;set;}
[ForeignKey("CarId")]
public CarModel Car {get;set;} = null!;
}
My thought is that the FK can be nullable on the entity, so when it's also creating a new related entity it will be assigned during the save?

Long story short, all your reasonable expectations will be met:
If you create a new Clown and assign CarId=1, then EF will assume you know what car you wanted to assign even if it's never seen Car 1 be downloaded before; it will create a Clown with CarId 1. If you have a relationship DB side then Car 1 will need to exist otherwise the DB will reject it
If you create a new Clown and a new Car assigned to its Car property, and that new car has the default value for its CarId, the EF will save the Car first, retrieve the ID and wire it into the Clown before the Clown is saved so that the relationship is satisfied on the DB side
Don't create a new Clown with a new Car that has a CarId that already exists; EF will try to insert a new Car (because it knows the entity is new) with the given ID rather than leaving the DB to create oen, which would probably result in a "cannot insert PK value to a table with IDENTITY_INSERT OFF" type error. If yo uknow the Car Id, use Route 1 above and just assign it to the Clown.CarId

I recommend using one or the other, not both. Typically when you are doing something like creating a row and want to associate another existing row to it, you should explicitly validate that the ID of the associated entity exists to handle things more gracefully than simply waiting for a SaveChanges to fail. Creating a new Clown would pass the details for the clown and a CarId when assigning them to a specific car. In this case my Clown entity would use a Car reference with a shadow property for the CarId. I request the Car from the DbContext via the CarId and handle the situation where a Car might not be found. (Another user deleted it, a bug in the code, or someone is hacking at the request payload)
Alternatively in situations where I either need raw performance or don't care about the entity association, I will just map the Id with no navigation property. Examples of this would be a bounded context that handles background batch-like jobs, or references to FKs that I don't ever need more than the Key for anyways. Such as something like a "Status" where I do have a Status table and FK for referential integrity, but since those statuses have relevance through business logic decisions I would use an enumeration in the code to represent them. I don't care about a Status entity, just it's key.
The reason I don't recommend using both is that it presents a situation where you have two sources of truth. Clown.CarId, and Clown.Car.CarId. If you want to change a clown's car, setting Clown.CarId would have a different behaviour whether Clown.Car was loaded or not. (whether eager loaded, lazy loaded, or populated by the DbContext if it was already tracking the car when the clown was requested)

Related

How do I map a single Id in my domain model to multiple Ids in my data model using AutoMapper?

I am attempting to create a new Web API using ASP.Net Core 3, Entity Framework Core, and AutoMapper against a previously existing database. I will try to explain my problem briefly.
For the sake of example, assume the database has the following tables:
Person
--------------------
person_id int PK
first_name varchar
last_name varchar
...other fields common to all Persons...
Owner
--------------------
owner_id int PK, FK Person.person_id
...fields specific to Owners...
Renter
--------------------
renter_id int PK, FK Person.person_id
...fields specific to Renters...
Note: The original developers of the database did not make the person_id an identity column. They use a [UniqueIds] table and a stored procedure to fetch and increment Ids for tables in the database.
In the context of the data model objects, Person, Owner, and Renter are all distinct classes with their on DbSet<> properties in the DbContext. The owner_id or renter_id is a person_id, and ties the tables together through a foreign key constraint.
From a Domain object perspective, I've designed the Owner and Renter domain classes as sub-classes of a Person class.
class Person { ... }
class Owner : Person { ... }
class Renter : Person { ... }
I'm still learning the plumbing of how to put all this together, which is proving to be difficult given the plethora of information available, which is sometimes incomplete, lacking context, or outdated. So I really could use some up-to-date guidance. What isn't clear to me:
If I were using the data model directly, my application would be responsible for knowing that in order to create a new Owner record (i.e., add a new class instance to DbSet<Owner>), a new Person record with the same ID must be added to DbSet<Person>. So, I am assuming that a domain model in the layer that sits atop the data/persistence layer has to do something similar, and that AutoMapper will take care of ensuring the domain object's properties are properly mapped to the data objects if properly configured.
With that in mind, when defining the domain model, should I define a separate OwnerId field in the Owner class and somehow map it in AutoMapper? This seems rather sloppy to me, and relies on the consumer of my domain to ensure that OwnerId and PersonId from the base class hold the same value when creating a new Owner.
It would seem to me that AutoMapper should support the ability to represent the inheritance (which it does with IMappingExression.Include or IMappingExression.IncludeBase) and map Owner.PersonId (Domain) to both Owner.OwnerId and Person.PersonId in the data objects. But I cannot find any practical examples of this. Perhaps my Google Fu is failing me and similar question has been asked, but I could not find one. Any help or guidance would be much appreciated.

Extract one specific entry from a table?

I am currently looking for a way I can pass a foreing key to a table entry that is listed in one table,
and should be extracted in another table.
for example purposes I created this ?
public class Parent
{
public string Name {get; set;}
public virtual ICollection<child> Children
Public virtual ICollection<School> Schools {get; set;}
}
public class Child
{
public string Name {get; set;}
Public School Schoola{get; set;} // Which should be a school Name that the Parent Should know?
}
public class School
{
//ParentID
//ChildID
public string SchoolName {get; set;}
}
How do i give my Child instance a SchoolName that the Parent contains within the SchoolNames?
Children and SchoolNames are seperate tables - but child only need to know a specific entry..
Caveat
Your code does not work, since EF does not serialize collections of primitive types. EF Core does have value conversions but it is unclear what you're exactly looking for. I'm going to assume you meant to store these as actual School entities, since your question asks how to "extract one entry from a table".
For the sake of answering your question, I assume that your child should have a reference to the school entity, not a string property that's technically unrelated to the school entity itself, which would make it a question not related to Entity Framework and thus the question tags would be wrong.
I'll address both my assumption and your literal question, just to be sure.
If you need a relationship between a child and a school
From a purely database standpoint, there is no way to specify that an entity's (Child) foreign key should refer to an entity (School) which in and of itself has a foreign key to another entity (Parent). It simply doesn't exist in SQL and therefore EF cannot generate this behavior for you.
What you can do, is implement business validation on your code and refuse to store any child with a school that doesn't belong to its parent. Keep in mind, this requires you to load the parent and their schools every time you want to save a child to the database (because otherwise you can't check if the selected school is allowed for this child), so it will become a somewhat expensive operation.
However, that doesn't prevent the possibility for someone to introduce data into the database (circumventing your business logic, e.g. by a DBA) where this rule is violated but the FK constraint itself is upheld.
How you handle these bad data states is up to you. Do you remove those entries when you stumble upon them? Do you proactively scan the database once in a while? Do you allow it to exist but restrict your application's users to only choosing schools from the parent's scope? These are all business decisions that we cannot make for you.
If a child needs a school name without a relation to the school itself
At first sight, this seems to me to be a bad solution. What happens when the school's name changes? Wouldn't you expect the child's schoolname to also change? Because that's not going to happen in your current setup.
In either case, if you are looking to set a string property, that's trivial, you simply set the property. Presumably, your question is how to restrict the user's options to the child's parent's schools.
This restrictive list can be fetched from the database using the child's identifier:
var childID = 123;
var schoolsFromParent = db
.Children
.Where(c => c.Id == childId)
.Select(c => c.Parent.Schools)
.FirstOrDefault();
Note that this code works regardless of whether you have a School entity or a list of strings - though the type of schoolsFromParent will be different.
And then restrict your end user to only being able to pick from the presented options. Note that to prevent bad data, you should doublecheck the chosen name after the user has selected it.

Entity Framework Core: Update relation with Id only without extra call

I'm trying to figure out how to deal with 'Single navigation property case' described in this doc:
Let's say we have 2 models.
class School
{
public ICollection<Child> Childrens {get; set;}
...
}
and
class Child
{
public int Id {get; set;}
...
}
So it's many-to-one relationship created by convention, without explicit foreign key in a Child.
So the question is if we have Child instance and know School.Id is there a way to update this relation without extra call to database to obtain School instance.
So the question is if we have Child instance and know School.Id is there a way to update this relation without extra call to database to obtain School instance.
Yes, it's possible. You can create a fake stub School entity instance with Id only, Attach it to the DbContext (this way telling the EF that it is existing), Attach the Child instance for the same reason, and then add the Child to the parent collection and call SaveChanges:
Child child = ...;
var schoolId = ...;
var school = new School { Id = schoolId };
context.Attach(school);
context.Attach(child);
school.Childrens.Add(child);
context.SaveChanges();
Update: Actually there is another cleaner way, since even if the entity has no navigation or FK property, EF Core allows you to access/modify the so called Shadow Properties
Shadow properties are properties that do not exist in your entity class. The value and state of these properties is maintained purely in the Change Tracker.
as soon as you know the name. Which in your case, without configuration would be by convention "SchoolId".
So no fake School entity instance is needed, just make sure the Child is attached and then simply set the shadow property through ChangeTracker API:
context.Attach(child);
context.Entry(child).Property("SchoolId").CurrentValue = schoolId;
context.SaveChanges();
Based on the updated question
No, there isn't ANY way you could do that by using ORM and strong typing that the ORM offers you, w/o
Two-Way Navigation Property
At least a ForeignKey/Principal property(SchoolId on Child)
Having a shadow foreign key to the parent
performing a raw query (which beats the idea of having ORM for strong typing) and being DB agnostic at the same time
// Bad!! Database specific dialect, no strong typing
ctx.Database.ExecuteSqlCommandAsync("UPDATE Childs SET schoolId = {0}", schoolId);
When you choose to use an ORM you have to accept certain technical limitations of the ORM framework in question.
If you want to follow Domain Driven Design (DDD) and remove all db specific fields form your entities, it won't be easy to use your domain models as entities.
DDD and ORM don't have very good synergies, there are way better approaches for this, but require a different architectural approach (namely: CQRS+ES (Command Query Responsibility Segregation with Event Sourcing).
This works much better with DDD, since the Events from the EventSourcing are just simple (and immutable) message classes which can be stored as serialized JSON in the database and replayed to reconstruct the domain entity's state. But that's a different story and one could write whole books about this topic.
Old Answer
The above scenario is only possible in a single DB operation, if your Child objects a navigation property/"back reference" to the parent.
class School
{
public ICollection<Child> Childrens {get; set;}
...
}
and
class Child
{
public int Id {get; set;}
// this is required if you want do it in a single operation
public int SchoolId { get; set; }
// this one is optional
public School { get; set; }
...
}
Then you can do something like:
ctx.Childs.Add(new Child { Id = 7352, SchoolId = 5, ... });
Of course you first have to know the school Id and know it's valid, otherwise the operation will throw an exception if SchoolId is an invalid value, so I wouldn't recommend this approach.
If you only have the childId and not adding a whole new child you'll still have to get the child first.
// childId = 7352
var child = ctx.Childs.FirstOrDefault(c => c.Id == childId);
// or use ctx.Childs.Find(childId); if there is a chance that
// some other operation already loaded this child and it's tracked
// schoolId = 5 for example
child.SchoolId = schoolId;
ctx.SaveChanges();

(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;

Do unidirectional associations lead to non-required foreign key fields through NHibernate

Update
Added mappings below
Question summary
I have a database with many required foreign key fields and a code base with many unidirectional associations. I want to use NHibernate, but as far as I can tell, I either have to make the foreign key fields in the database NULLable (not a realistic option) or change the associations to bidirectional (not ideal either). Any other options that I've missed?
Backgrounds
I've joined a project that uses NHibernate to map tables 1:1 to so-called "technical" objects. After data retrieval, the objects are mapped to the actual domain model (AutoMapper style,implemented differently). I know that this is an unnecessary step and I want to propose removing it to the team. However, I'm running into an issue.
The domain model contains many unidirectional associations: the Case object has a list of Persons associated with the case, but the Persons do not hold a reference to the Case object. In the underlying database scheme, the Person table has a required foreign key field that references the case Id. The data model:
[ERD]
PERSON
CASE Id* Ids are generated by the DB
Id* <--FK-- CaseId* * denotes required fields
(other) (other)
The domain model looks like this:
public class Person : DomainEntity
{ // DomainEntity implements Id. Non-essential members left out }
public class Case : DomainEntity
{
public virtual IList<Person> Persons { get; set; }
}
Calling session.Save() on a Case leads to a database error (CaseId required when inserting into Person), because NHibernate starts with inserting the Person entries, followed by the Case entry and finishes by updating the CaseId column in the Person entries. If the CaseId column in the database is altered to non-required (allow NULLs), everything works as it should... however, that change is not an option at the moment (the database model is shared by several apps for at least another year).
The only way I have found to get NHibernate to execute the database actions correctly is by changing the association to bidirectional, i.e., by changing Person to
public class Person : DomainEntity
{
public virtual Case Case { get; set; }
}
This would involve significant changes to the existing codebase however, so I would prefer alternatives, if they exist. I've played around with component mappings, but that is a bad fit since most associations in our model are not actual (UML) compositions. Are there any other options that I've missed? TIA!
EDIT
The (Fluent) mapping for Case looks like this:
public class CaseMapping : ClassMap<Case>
{
public CaseMapping()
{
Not.LazyLoad();
Id(c => c.Id).GeneratedBy.Identity();
Map(x => x.Code).Not.Nullable().Length(20);
Map(x => x.Name).Not.Nullable().Length(100);
HasMany<Person>(x => x.Persons)
.AsBag()
.KeyColumn("CaseId")
.ForeignKeyConstraintName("FK_Person_Case")
.Cascade.AllDeleteOrphan();
}
}
If I use SessionSource.BuildSchema for a test database, this generates a Person table with a nullable CaseId column. I have not found a way for this to work with a non-nullable CaseId field without bidirectional associations. The executed (pseudo) SQL statements:
INSERT INTO Case...
select ##identity
INSERT INTO Person /* (all columns except CaseId) */
select ##identity
UPDATE Person SET CaseId = ? WHERE Id = ?;#p0 = 2, #p1 = 1
I think you may be out of luck here. The docs at http://nhibernate.info/doc/nh/en/index.html#collections-onetomany state:
If the column of a association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true"

Categories