Exception The value of 'X' is unknown when attempting to save changes - sql-server

There are these two entities:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public CompanyVehicle CompanyVehicle { get; set; }
}
and
public class CompanyVehicle
{
public int Id { get; set; }
public string Name { get; set; }
public Employee Employee { get; set; }
}
Using Entity Framework Core 5.0.8 on SQL Server 2019, the configuration for CompanyVehicle is:
entityBuilder.HasOne(t => t.Employee)
.WithOne(t => t.CompanyVehicle)
.HasForeignKey<Employee>(t => t.Id)
.IsRequired();
And we'll try to insert something:
public void Create(Employee employee)
{
employee.CompanyVehicle = new CompanyVehicle();
dbContext.Add<Employee>(employee);
dbContext.SaveChanges();
}
The above code used to work fine in EF6. Two new records in both Employee and CompanyVehicle tables were created with the same Id. After migrating to EF Core 5.0.8, dbContext.SaveChanges() throws an exception:
System.InvalidOperationException: 'The value of 'Employee.Id' is unknown when attempting to save changes. This is because the property is also part of a foreign key for which the principal entity in the relationship is not known.'
Note that these entities are just examples and the database design should not be altered in my case.
Update
After some more investigation, I've found out my problem is:
Having X (principal) and Y (dependent) as two tables where X.Id is PK for X and Y.Id is PK for Y and also FK to X, in EF Core a record of X cannot be inserted.

So I finally found the problem, configuring a Property to be both PK and FK is possible and very easy. We had our old codes after migrating to EFCore from EF6 in an assembly. The project is a framework so in OnModelCreating we use modelBuilder.ApplyConfigurationsFromAssembly in our base DbContext to register configurations in the guest projects. The project will automatically find all the configurations in all of assemblies referenced by the project or DLLs in the application path.
The key point is: In EF Core explicit fluent FK configuration is in the reverse order compared to EF6. So in EF6 for Employee we used to write:
this.HasRequired(t => t.CompanyVehicle)
.WithRequiredDependent(t => t.Employee)
.HasForeignKey(d => d.Id);
and in EF Core we should write:
b.HasOne(t => t.CompanyVehicle)
.WithOne(t => t.Employee)
.HasForeignKey<Employee>(t => t.Id).IsRequired();
The parameter d used in the first part is of type CompanyVehicle. So our migrator converted the old code to:
b.HasOne(t => t.CompanyVehicle)
.WithOne(t => t.Employee)
.HasForeignKey<CompanyVehicle>(t => t.Id).IsRequired();
Which is incorrect. The generic parameter should be the dependent table type. We later fixed the issue in a new namespace but the ApplyConfigurationsFromAssembly method kept applying the obsolete code after our configuration too.
I used the following block of code at the end of OnModelCreating to investigate the issue:
foreach (var entity in modelBuilder.Model.GetEntityTypes())
foreach(var key in entity.GetForeignKeys())
{
//Check what is in the key...
}
and noticed that there are duplicated keys configured for my entities.

Entity Framework Core configures one to one relationships by being able to detect the foreign key property, and thereby identify which is the principal and which is the dependent entity in the relationship.
First look at the existing database and check what is the dependant table, assuming it is the Employee, it should have a foriegn key to CompanyVehicle table. (It could be other way around in your case.)
1. Using EF Core convestions.
If Employee is the depentant table, add that exact foriegn key property name (let's assume it's Vehicle_Id) to your Employee entity. Follow 2nd method if you don't want to add a property to the class.
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Vehicle_Id { get; set; } // <-- This right here.
public CompanyVehicle CompanyVehicle { get; set; }
}
Without this property, as I mentioned earlier, child/dependent side could not be determined for the one-to-one relationship. (check what is yours in the db and add that property, otherwise you will get two foreign keys in the Employee table)
And using fluent API, configure the relation like this. (Notice how a and b were used to separate two navigation properties, in your implementation you have used t, for both, and when you say .HasForeignKey<Employee>(t => t.Id), you're setting the foriegn key to primary key Id of Employee table, which could be the reason behind your error).
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CompanyVehicle>()
.HasOne(a => a.Employee)
.WithOne(b => b.CompanyVehicle)
.HasForeignKey<Employee>(b => b.Vehicle_Id);
}
2. Not using EF Core conventions.
If you do not like to add a property to the dependant table, use the exsisting foriegn key in the database (let's assume it's Vehicle_Id), fluent API config should look like this.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CompanyVehicle>()
.HasOne(a => a.Employee)
.WithOne(b => b.CompanyVehicle)
.HasForeignKey<Employee>("Vehicle_Id");
}
Edit:
The Has/With pattern is used to close the loop and fully define a relationship. In this case, since the relationship to be configured is a one-to-one, the HasOne method is chained with the WithOne method. Then the dependent entity (Employee) is identified by passing it in as a type parameter to the HasForeignKey method, which takes a lambda specifying which property in the dependent type is the foreign key.
So if you want the Employee Id to act as the foriegn key to the CompanyVehicle table, ammend your Fluent API as this, again notice a and b when specifying lambdas.
modelBuilder.Entity<CompanyVehicle>()
.HasOne(a => a.Employee)
.WithOne(b => b.CompanyVehicle)
.HasForeignKey<Employee>(b => b.Id);

I had the same issue that A. Morel had.
When manually inserting a custom join table for ManyToMany, and a foreign key was 0, I was getting this error.
Fixed by changing the seed value of the parent table to start at 2:
DBCC CHECKIDENT ('program_contact', RESEED, 1);
Because of this issue
DBCC CHECKIDENT Sets Identity to 0

For me this seems to be caused by creating "blank" entities that I don't add to the context. In EF6 these were ignored since they were not added, but in EF Core they seem to be added automatically.
I corrected the issue by reducing the scope of my "writable" context down to just the single line where a change was made, and used a separate "read only" context for everything else.
I could further correct this by not using entity types directly in my view so that I can make blank entries that are not entities.

Related

Why am I getting DbUpdateException: OptimisticConcurrencyException?

I have a Category class:
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
I also have a Subcategory class:
public class Subcategory
{
public int SubcategoryId { get; set; }
public Category Category { get; set; }
public string SubcategoryName { get; set; }
}
And a Flavor class:
public class Flavor
{
public int FlavorId { get; set; }
public Subcategory Subcategory { get; set; }
public string FlavorName { get; set; }
}
Then I also have Filling and Frosting classes just like the Flavor class that also have Category and Subcategory navigation properties.
I have a Product class that has a Flavor navigation property.
An OrderItem class represents each row in an order:
public class OrderItem
{
public int OrderItemId { get; set; }
public string OrderNo { get; set; }
public Product Product { get; set; }
public Frosting Frosting { get; set; }
public Filling Filling { get; set; }
public int Quantity { get; set; }
}
I'm having issues when trying to save an OrderItem object. I keep getting DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. with the Inner Exception being OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. I've stepped through my code several times and I can't find anything that modifies or deletes any entities loaded from the database. I've been able to save the OrderItem, but it creates duplicate entries of Product, Flavor, Subcategory and Category items in the DB. I changed the EntityState of the OrderItem to Modified, but that throws the above exception. I thought it might have been the fact that I have Product, Frosting and Filling objects all referencing the same Subcategory and Category objects, so I tried Detaching Frosting and Filling, saving, attaching, changing OrderItem entity state to Modified and saving again, but that also throws the above exception.
The following statement creates duplicates in the database:
db.OrderItems.Add(orderItem);
Adding any of the following statements after the above line all cause db.SaveChanges(); to throw the mentioned exception (both Modified and Detached states):
db.Entry(item).State = EntityState.Modified;
db.Entry(item.Product.Flavor.Subcategory.Category).State = EntityState.Modified;
db.Entry(item.Product.Flavor.Subcategory).State = EntityState.Modified;
db.Entry(item.Product.Flavor).State = EntityState.Modified;
db.Entry(item.Product).State = EntityState.Modified;
Can someone please give me some insight? Are my classes badly designed?
The first thing to check would be how the entity relationships are mapped. Generally the navigation properties should be marked as virtual to ensure EF can proxy them. One other optimization is that if the entities reference SubCategory then since SubCats reference a Category, those entities do not need both. You would only need both if sub categories are optional. Having both won't necessarily cause issues, but it can lead to scenarios where a Frosting's Category does not match the category of the Frosting's SubCategory. (Seen more than enough bugs like this depending on whether the code went frosting.CategoryId vs. frosting.SubCategory.CategoryId) Your Flavor definition seemed to only use SubCategory which is good, just something to be cautious of.
The error detail seems to point at EF knowing about the entities but not being told about their relationships. You'll want to ensure that you have mapping details to tell EF about how Frosting and SubCategory are related. EF can deduce some of these automatically but my preference is always to be explicit. (I hate surprises!)
public class FrostingConfiguration : EntityTypeConfiguration<Frosting>
{
public FlavorConfiguration()
{
ToTable("Flavors");
HasKey(x => x.FlavorId)
.Property(x => x.FlavorId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.SubCategory)
.WithMany()
.Map(x => x.MapKey("SubCategoryId");
}
}
Given your Flavor entity didn't appear to have a property for the SubCategoryId, it helps to tell EF about it. EF may be able to deduce this, but with IDs and the automatic naming conventions it looks for, I don't bother trying to remember what works automagically.
Now if this is EF Core, you can replace the .Map() statement with:
.ForeignKey("SubCategoryId");
which will set up a shadow property for the FK.
If SubCats are optional, then replace HasRequired with HasOptional. The WithMany() just denotes that while a Flavor references a sub category, SubCategory does not maintain a list of flavours.
The next point of caution is passing entities outside of the scope of the DBContext that they were loaded. While EF does support detaching entities from one context and reattaching them to another, I would argue that this practice is almost always far more trouble than it is worth. Mapping entities to POCO ViewModels/DTOs, then loading them on demand again when performing updates is simpler, and less error-prone then attempting to reattach them. Data state may have changed between the time they were initially loaded and when you go to re-attach them, so fail-safe code needs to handle that scenario anyways. It also saves the hassle of messing around with modified state in the entity sets. While it may seem efficient to not load the entities a second time, by adopting view models you can optimize reads far more efficiently by only pulling back and transporting the meaningful data rather than entire entity graphs. (Systems generally read far more than they update) Even for update-heavy operations you can utilize bounded contexts to represent large tables as smaller, simple entities to load and update a few key fields more efficiently.

How do I use a composite key for a one to many relationship in Code First EF

I am using EF Code First.
I need two tables, LedgerCategories and LedgerSubCategories with a one-to-many relationship (Categories -> SubCategories), with the keys in each being codes (strings) - i.e. LedgerCategoryCode and LedgerSubCategoryCode respectively. However, I need to allow the SubCategoryCode values to be the same for different Categories.
E.g. CategoryCode = REHAB, SubCategoryCodes = MATL, CONTR, and FEES; and CategoryCode = MAINT, SubCategoryCodes = MATL, CONTR, and FEES.
I'm thinking I need to use a composite key and include both the CategoryCode and SubCategoryCode fields in the LedgerSubCategories table. Currently I have:
public class LedgerCategory
{
[Key]
public string LedgerCategoryCode { get; set; }
public string Description { get; set; }
public List<LedgerSubCategory> LedgerSubCategories { get; set; }
}
public class LedgerSubCategory
{
[Key, Column(Order = 0)]
public string LedgerCategoryCode { get; set; }
[Key, Column(Order = 1)]
public string LedgerSubCategoryCode { get; set; }
public string Description { get; set; }
}
I am seeding these tables using only instances of the LedgerCategory class, having each contain a List of appropriately instantiated LedgerSubCategory classes. This appears to both set up the DB schema correctly (in my perception), and populate both tables appropriately.
But, when I reinstantiate a simple List of LedgerCategory, i.e.
using (var db = new BusinessLedgerDBContext())
{
var LedgerCategories = db.LedgerCategories.ToList();
}
The LedgerCategory instances don't contain their respective List of associated LedgerSubCategory instances.
I am trying to avoid, what seems like a kludge, to introduce a unique number or Guid ID field in LedgerSubCategories as a PK and just index off the other Code fields. I haven't tried this, but I'm not sure it would cause any different results for reinstantiating the LedgerCategories and getting associated LedgerSubCategories.
Any advice on how to do this appropriately and get proper results is appreciated.
To, I suppose, answer my own question, I have found that overriding OnModelCreating() in the respective DbContext with Fluent API to establish the one to many relationship and foreign key when the Code First framework establishes the desired DB Schema. There appears no other way to do this, such as with Attributes. By many accounts of others, including MSDN, Fluent API appears to be what is needed. However, that has led me to a new issue, or set of issues, which I've posed as a question here.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configures the one-many relationship between Categories and
// SubCategories, and established the Foreign Key in SubCategories
modelBuilder.Entity<Category>()
.HasMany<SubCategory>(c => c.SubCategories)
.WithRequired(s => s.Category)
.HasForeignKey<string>(s => s.CategoryCode);
}

DbSet.Load() method is too slow

I have an SQLite database, which contains one table named "Main". Each record of this table contains only two fields: ID (integer, primary key) and name (string). There are 100 records in the database.
Using Entity Framework Power Tools I've created the Code First model from the existing database. The model is rather simple:
// MainMap.cs
public class MainMap : EntityTypeConfiguration<Main>
{
public MainMap()
{
// Primary Key
this.HasKey(t => t.ID);
// Properties
this.Property(t => t.name)
.IsRequired()
.HasMaxLength(50);
// Table & Column Mappings
this.ToTable("Main");
this.Property(t => t.ID).HasColumnName("ID");
this.Property(t => t.name).HasColumnName("name");
}
}
// Main.cs
public partial class Main
{
public long ID { get; set; }
public string name { get; set; }
}
// mainContext.cs
public partial class mainContext : DbContext
{
static mainContext()
{
Database.SetInitializer<mainContext>(null);
}
public mainContext()
: base("Name=mainContext")
{
}
public DbSet<Main> Mains { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MainMap());
}
}
Now I'm trying to get the records from the database:
mainContext context = new mainContext();
context.Mains.Load();
Now I can use context.Mains.Local with a comfort for different purposes (actually, I bind it to ListView's ItemsSource).
The problem is that context.Main.Load() line executes for about 2.7 seconds. I think, it is too much time as for retrieving about 100 records from a simple database. Although, I'm a newcomer to databases, so, maybe I'm not right and 2.7 seconds is rather suitable period of time. My CPU is Intel i3-3220 (2x3.30 GHz), Entity Framework's version is 6.0.
Maybe, my Code First model is poor, or maybe EF doesn't provide high performance, or maybe there is no need to call Load() method to obtain records (but if I don't call it, context.Mains.Local is empty).
So, how can I increase the performance of getting the records from the database?
Any help and hints will be appreciated.
i ran some tests with both SQLite and SQL Server. on my laptop (corei7 2630QM 2.00GHZ & win7 64bit) the load time for both was ~1.5sec.
then i tried to warm it up with something like
context.Database.Exists();
and the time reduced to ~700ms for both.
i used "Prefer 32-bit" and "Optimize code" options in build tab of the project properties. these options produced best results.
try these and see if the load time changes.

DateCreated or Modified Column - Entity Framework or using triggers on SQL Server

After I read one question in attached link, I got a sense of how to set DateCreated and DateModified columns in Entity Framework and use it in my application. In the old SQL way though, the trigger way is more popular because is more secure from DBA point of view.
So any advice on which way is the best practice? should it be set in entity framework for the purpose of application integrity? or should use trigger as it make more sense from data security point of view? Or is there a way to compose trigger in entity framework? Thanks.
EF CodeFirst: Rails-style created and modified columns
BTW, even though it doesn't matter much, I am building this app using ASP.NET MVC C#.
Opinion: Triggers are like hidden behaviour, unless you go looking for them you usually won't realise they are there. I also like to keep the DB as 'dumb' as possible when using EF, since I'm using EF so my team wont need to maintain SQL code.
For my solution (mix of ASP.NET WebForms and MVC in C# with Business Logic in another project that also contains the DataContext):
I recently had a similar issue, and although for my situation it was more complex (DatabaseFirst, so required a custom TT file), the solution is mostly the same.
I created an interface:
public interface ITrackableEntity
{
DateTime CreatedDateTime { get; set; }
int CreatedUserID { get; set; }
DateTime ModifiedDateTime { get; set; }
int ModifiedUserID { get; set; }
}
Then I just implemented that interface on any entities I needed to (because my solution was DatabaseFirst, I updated the TT file to check if the table had those four columns, and if so added the interface to the output).
UPDATE: here's my changes to the TT file, where I updated the EntityClassOpening() method:
public string EntityClassOpening(EntityType entity)
{
var trackableEntityPropNames = new string[] { "CreatedUserID", "CreatedDateTime", "ModifiedUserID", "ModifiedDateTime" };
var propNames = entity.Properties.Select(p => p.Name);
var isTrackable = trackableEntityPropNames.All(s => propNames.Contains(s));
var inherits = new List<string>();
if (!String.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)))
{
inherits.Add(_typeMapper.GetTypeName(entity.BaseType));
}
if (isTrackable)
{
inherits.Add("ITrackableEntity");
}
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", String.Join(", ", inherits)));
}
The only thing left was to add the following to my partial DataContext class:
public override int SaveChanges()
{
// fix trackable entities
var trackables = ChangeTracker.Entries<ITrackableEntity>();
if (trackables != null)
{
// added
foreach (var item in trackables.Where(t => t.State == EntityState.Added))
{
item.Entity.CreatedDateTime = System.DateTime.Now;
item.Entity.CreatedUserID = _userID;
item.Entity.ModifiedDateTime = System.DateTime.Now;
item.Entity.ModifiedUserID = _userID;
}
// modified
foreach (var item in trackables.Where(t => t.State == EntityState.Modified))
{
item.Entity.ModifiedDateTime = System.DateTime.Now;
item.Entity.ModifiedUserID = _userID;
}
}
return base.SaveChanges();
}
Note that I saved the current user ID in a private field on the DataContext class each time I created it.
As for DateCreated, I would just add a default constraint on that column set to SYSDATETIME() that takes effect when inserting a new row into the table.
For DateModified, personally, I would probably use triggers on those tables.
In my opinion, the trigger approach:
makes it easier; I don't have to worry about and remember every time I save an entity to set that DateModified
makes it "safer" in that it will also apply the DateModified if someone finds a way around my application to modify data in the database directly (using e.g. Access or Excel or something).
Entity Framework 6 has interceptors which can be used to set created and modified. I wrote an article how to do it: http://marisks.net/2016/02/27/entity-framework-soft-delete-and-automatic-created-modified-dates/
I agree with marc_s - much safer to have the trigger(s) in the database. In my company's databases, I require each field to have a Date_Modified, Date_Created field, and I even have a utility function to automatically create the necessary triggers.
When using with Entity Framework, I found I needed to use the [DatabaseGenerated] annotation with my POCO classes:
[Column(TypeName = "datetime2")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? Date_Modified { get; set; }
[Column(TypeName = "datetime2")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? Date_Created { get; set; }
I was attempting to use stored procedure mapping on an entity, and EF was creating #Date_Modified, #Date_Created parameters on my insert/update sprocs getting the error
Procedure or function has too many arguments specified.
Most of the examples show using [NotMapped], which will allow select/insert to work, but then those fields will not show up when that entity is loaded!
Alternately you can just make sure any sprocs contain the #Date_Modified, #Date_Created parameters, but this goes against the design of using triggers in the first place.

Why can't SQL Server handle cascading deletes for two foreign keys pointing to the same table?

I'm using Entity Framework - Code First, and I've run into an issue where I have to disable cascading deletes for a particular foreign key.
Here is my entity class:
public class ChallengeMatch
{
public int Id { get; set; }
public int ChallengerClubMemberId { get; set; }
public int ChallengeeClubMemberId { get; set; }
public bool ChallengerWon { get; set; }
public string Score { get; set; }
public virtual ClubMember ChallengerClubMember { get; set; }
public virtual ClubMember ChallengeeClubMember { get; set; }
}
If I allow Code First to generate the database for this table with all default settings (which includes cascading deletes), it will throw an exception when it tries to generate the database objects.
If I add the following code to my DbContext class, I no longer get an exception, but now cascading deletes will only somewhat work:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ChallengeMatch>()
.HasRequired(cm => cm.ChallengeeClubMember)
.WithMany()
.HasForeignKey(cm => cm.ChallengeeClubMemberId)
.WillCascadeOnDelete(false);
}
Because of this, I have to write code by hand. Specifically, if I'm about to delete ClubMember 13, I have to delete any ChallengeMatch whose ChallengeeClubMemberId is 13.
What I don't understand is why any of this should be necessary. Why can't SQL Server handle a cascading delete even if there are two foreign keys pointing to the same table? I can't think of any reason why this would fail.
It should be a simple three-step process (psuedo code):
Delete all challenge matches where ChallengerClubMemberId == 13
Delete all challenge matches where ChallengeeClubMemberId == 13
Delete the club member where Id = 13.
Why can't SQL Server do this or why does it choose not to?
The problem is that your configuration allows multiple cascade delete paths. This can happen if both your properties will point to the same ClubMember. SQL Server doesn't allow that. It is more about internal implementation of SQL server and more details can be found in this answer. I think that this check is simple and safe solution to avoid some race conditions during deletes cascading in parallel.

Resources