Related data not saved when added after creation with EF Core 6 - c#

I have POCO objects that are exposed through a repository that uses EF Core 6 to access a database. I can persist "parent" objects to the database and related data that is added to the parent object before creating is persisted successfully as well. However, when trying to add children (SingleSimulationResult objects) to a parent object (SingleSimulation objects) after it has been created, the children are not persisted to the database.
Here is the code that tries to add and save children to the parent object.
singleSim.AddResultsToSimulation(allResults);
Console.WriteLine($"# results: {singleSim.Results.Count}"); // # results: 2
await scopedRepository.Save();
var test = await scopedRepository.GetById(singleSim.Id);
Console.WriteLine($"# results test: {test.Results.Count}"); // # results: 0
SingleSimulation class (BaseEntity just defines an Id property):
public class SingleSimulation : BaseEntity, IAggregateRoot
{
public string Name { get; private set; }
public string Description { get; private set; }
public double Capital { get; private set; }
public List<List<double>> Returns { get; private set; }
private readonly List<SingleSimulationStrategy> _strategies = new List<SingleSimulationStrategy>();
public IReadOnlyCollection<SingleSimulationStrategy> Strategies => _strategies.AsReadOnly();
private List<SingleSimulationResult> _results = new List<SingleSimulationResult>();
public IReadOnlyCollection<SingleSimulationResult> Results => _results.AsReadOnly();
public SingleSimulation()
{
}
public SingleSimulation(string name, string description, double capital, List<List<double>> returns, List<SingleSimulationStrategy> strategies)
{
Name = name;
Description = description;
Capital = capital;
Returns = returns;
_strategies = strategies;
}
public void AddResultsToSimulation(List<SingleSimulationResult> results)
{
if (_results is null)
return;
foreach (var result in results)
{
_results.Add(result);
}
}
}
Repository class:
public class SingleSimulationRepository : ISingleSimulationRepository
{
private SimulationDbContext _dbContext;
public SingleSimulationRepository(SimulationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task Add(SingleSimulation entity)
{
await _dbContext.AddAsync(entity);
}
public async Task<SingleSimulation> GetById(int id)
{
return await _dbContext.SingleSimulations.FindAsync(id);
}
...
public async Task Save()
{
await _dbContext.SaveChangesAsync();
}
}
DbContext:
public class SimulationDbContext : DbContext
{
public SimulationDbContext(DbContextOptions<SimulationDbContext> options)
: base(options)
{
}
public DbSet<SingleSimulation> SingleSimulations { get; set; }
public DbSet<SingleSimulationResult> SingleSimulationResults { get; set; }
public DbSet<SingleSimulationStrategy> SingleSimulationStrategies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Seed data and custom conversion functions
}
}
Here's what I have tried (to no avail):
Using Fluent API to configure One-to-Many relationship for Results (using .HasMany()).
modelBuilder.Entity<SingleSimulation>()
.HasMany(x => x.Results)
.WithOne();
Using AddRange() to add result objects to the DB before adding them to the parent and finally saving to DB (SaveChangesAsync).
Using Attach() to start tracking result objects before adding them to the parent.
Using Include() when loading the parent object from the database before adding children and trying to save them.
It feels like I'm missing something small, but after scouring the docs and other sources I cannot find the problem. What do I need to do to get children added to the parent after the parent has already been created to actually save to the DB?

After debugging by printing the EF Core change tracker's LongView, I noticed that no changes are detected on the object (even if changing a simple string property). It turns out the problem was that the singleSim object I was modifying was returned from a different dbContext than the one used by the scopedRepository.
The model setup wasn't the problem after all. Even without the Fluent API config the setup works as intended (even with the read only collections and private backing fields).

Related

What is the correct flow of requests when working with multiple viewModels on EF Core/MVVM?

Futher into my studies on MVVM I found an issue I can't understand, and I couldn't find (rather, I think I couldn't word my Google searches correctly) information specific to this situation:
I have the following entities:
public class Sale : EntityBase
{
public ICollection<SaleItem> SaleItems {get;set;}
}
public class SaleItem : EntityBase
{
public Sale Sale {get;set;}
public Stock Stock {get;set;}
public TaxType TaxType {get;set;}
}
public class Stock : EntityBase
{
public TaxType TaxType {get;set;}
public ICollection<Sale> SaleStocks {get;set;}
}
public class EntityBase
{
[Key]
public int ID {get;set;}
}
public TaxType
{
[Key]
public int TaxCode {get;set;}
public string Description {get;set;
}
TaxType is seeded by the database migrations. I'm using MySQL.
From what I've read on what is the Alternate for AddorUpdate method in EF Core?, to record a new entry on my database, I should just call _context.Update(Sale) and _context.SaveChangesAsync().
However I still can't understand what am I doing wrong on a simple CRUD:
User is directed to a viewModel:
SaleViewModel.cs
private SaleItemStore _saleItemStore;
public Sale Sale {get;set;} = new();
public ObservableCollection<SaleItem> SaleItems {get;set;} = new();
public SaleViewModel(IServiceProvider serviceProvider)
{
_saleItemStore = serviceProvider.GetRequiredService<SaleItemStore>();
SaveSale = new SaveSaleCommand(this, serviceProvider);
}
public class SaveSaleCommand()
{
public SaveSaleCommand(SaleViewModel saleViewModel, IServiceProvidere serviceProvider)
{
_parentViewModel = saleViewModel;
_saleDataService = serviceProvider.GetRequiredService<SalaDataService>();
}
public Execute()
{
foreach (SaleItem saleItem in SaleItems)
{
Sale.SaleItems.Add(saleItem);
}
_saleDataService.AddOrUpdate(Sale sale);
}
}
On a different viewModel, the user can select, among other properties, the TaxType, from a dropdown combobox. The combobox's ItemsSource is bound to TaxTypesList like:
SelectStockToSaleItemViewModel
private TaxTypeDataService _taxTypeDataService;
public SaleItem SaleItem {get;set;} = new();
public TaxType SelectedTaxType {get;set;}
public ObservableCollection<TaxType> TaxTypesList {get;} = new();
public SelectStockToSaleItemViewModel(IServiceProvider serviceProvider)
{
_taxTypeDataService = serviceProvider.GetRequiredService<TaxTypeDataService>();
SendSaleItemBack = new SendSaleItemBackCommand(this, serviceProvider);
Task.Run(FillLists);
}
public async Task FillLists()
{
foreach (TaxType taxType in await _taxTypeDataService.GetAllAsNoTracking())
{
TaxTypesList.Add(taxType);
}
}
public SendSaleItemBackCommand()
{
private SaleItemStore _saleItemStore;
public SendSaleItemBackCommand(SelectStockToSaleItemViewModel selectStockToSaleItemViewModel, IServiceProvider serviceProvider)
{
_parentViewModel = selectStockToSaleItemViewModel;
_saleItemStore = serviceProvider.GetRequiredService<SaleItemStore>();
}
public Execute()
{
_saleItemStore.Message = new SaleItem()
{
TaxType = _parentViewModel.SelectedTaxType
}
//Takes user back to previous ViewModel;
}
}
So, my idea is that on SelectStockToSaleItemViewModelthe user selects a TaxType from a combobox filled with _taxTypeDataService.GetAllAsNoTracking(), and when they execute SendSaleItemBackCommand, SaleItemStore has its property set to a SaleItem with the TaxType not null;
Afterwards, should the user add more SaleItems to Sale, they'll have to open SelectStockToSaleItemViewModel again, and do the process for each extra SaleItem they wand to add to Sale.
Finally, they will have to execute SaveSaleCommand, and I'd end up with a Sale and its associated SaleItems on my database.
My dataServices are:
public class SaleDataService
{
private MyDbContext _context;
public SaleDataService(IServiceProvider serviceProvider)
{
_context = serviceProvider.GetRequiredService<MyDbContext>();
}
public async Task<Sale> AddOrUpdate(Sale sale)
{
_context.Update(sale);
await _context.SaveChangesAsync();
return sale;
}
}
public class TaxTypeDataService
{
private MyDbContext _contex;
public TaxTypeDataService(IServiceProvider serviceProvider0
{
_context = serviceProvider.GetRequiredService<MyDbContext>();
}
public async Task<List<TaxType>> GetAllAsNoTracking()
{
return _context.Set<TaxType>().AsNoTracking().ToListAsync();
}
}
public class Messaging<TObject> : IMessaging<TObject>
{
public TObject Message { get; set; }
}
SaleItemStore is singleton. SelectStockToSaleItemViewModel, and SelectStockToSaleItemViewModel are transient; TaxTypeDataService and SaleDataService are scoped; As for the context,
public static IHostBuilder AddDbContext(this IHostBuilder host)
{
host.ConfigureServices((_, services) =>
{
string connString = $"MyConnString";
ServerVersion version = new MySqlServerVersion(new Version(8, 0, 23));
Action <DbContextOptionsBuilder> configureDbContext = c =>
{
c.UseMySql(connString, version, x =>
{
x.CommandTimeout(600);
x.EnableRetryOnFailure(3);
});
c.EnableSensitiveDataLogging();
};
services.AddSingleton(new AmbiStoreDbContextFactory(configureDbContext));
services.AddDbContext<AmbiStoreDbContext>(configureDbContext);
});
return host;
}
The issue happens when the user tries to save a Sale with two or more SaleItems using the same TaxType, as EF Core throws an "another instance of TaxType is already being tracked". I understand the TaxType starts being tracked when the first Sale.SaleItem is updated, but how do I deal with this issue?
According to The instance of entity type 'Product' cannot be tracked because another instance with the same key value is already being tracked and The instance of entity type cannot be tracked because another instance of this type with the same key is already being tracked I did fill the combobox with TaxType using an AsNoTracking call, but I don't think it applies to my situation. Also, they say to "flush" the tracked entries before updating by setting the context's tracked entries' states to Detached, but, again, TaxType starts being tracked while being saved. I've checked the context's tracked entries collection, and there is no mention of TaxTypebeing tracked at all before Update(Sale) is called.
The only way I can think of as a workaround is using Fluent API and setting the foreign key, rather than the property itself (i.e. TaxTypeId = SelectedTaxType.ID), which doesn't look like the best way to deal with this.
Alright, I read some more about EF Core and I think I understood "the recommended way".
Whenever working with data services and dependency injection, the entities must be loaded by the same context that will be using them to add or update the context's database.
My mistake was loading the collections used by the interface (comboboxes, drop downs and checkboxes) using AsNoTracking. What happened was that whenever I tried to add or update two entities that used the same AsNoTracking-obtained entity, EF Core (as it should) tried to add the entity to the context's change tracker. So when the second entity tried to add/update, it threw a The instance of entity type cannot be tracked because another instance of this type with the same key is already being tracked exception.
So the "correct flow" is to load a collection/list using the same context that will be using them. Either using a singleton dbContext for the whole application and not using AsNoTracking, which is not recommended due to the possibility of ending up with concurrent operations; or using scoped dbContext for each "unit-of-work", i.e. a single dialog to add a new record, or an updating window with a record pulled from the same instance of the context that will be saving it, and disposing of the context (and it's change tracker) after whatever you wanted to do has been dealt with.
One of the big mistakes I was making when using scoped contexts was creating one context to load a datagrid with entries, which, when double-clicked, would open a dialog (with a new scoped context) to edit it without releasing that record from the previous context, throwing the already being tracked exception.

Entity Framework Core 2.0 Many to Many Inserts before primary key is generated

I'm trying to create an entity object that has many to many relationships with other entities. The relationships are indicated as follows.
public class Change {
// Change Form Fields
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ChangeId { get; set; }
public string ChangeTitle { get; set; }
public string ChangeType { get; set; }
public DateTime DateSubmitted { get; set; }
public DateTime TargetDate { get; set; }
//Many to Many Collections
public virtual ICollection<Change_CriticalBankingApp> Change_CriticalBankingApps { get; set; } = new List<Change_CriticalBankingApp>();
public virtual ICollection<Change_ImpactedBusiness> Change_ImpactedBusinesses { get; set; } = new List<Change_ImpactedBusiness>();
public virtual ICollection<Change_ImpactedService> Change_ImpactedServices { get; set; } = new List<Change_ImpactedService>();
public virtual ICollection<Change_TestStage> Change_TestStages { get; set; } = new List<Change_TestStage>();
public virtual ICollection<Change_TypeOfChange> Change_TypeOfChanges { get; set; } = new List<Change_TypeOfChange>();
And the DbContext set up is as follows
public class ChangeContext : DbContext {
public ChangeContext(DbContextOptions<ChangeContext> options) : base(options) {
Database.Migrate();
}
public DbSet<Change> Change { get; set; }
public DbSet<TestStage> TestStage { get; set; }
public DbSet<TypeOfChange> TypeOfChange { get; set; }
public DbSet<CriticalBankingApp> CriticalBankingApp { get; set; }
public DbSet<ImpactedBusiness> ImpactedBusiness { get; set; }
public DbSet<ImpactedService> ImpactedService { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Change_CriticalBankingApp>().HasKey(t => new { t.ChangeId, t.CriticalBankingAppId });
modelBuilder.Entity<Change_ImpactedBusiness>().HasKey(t => new { t.ChangeId, t.ImpactedBusinessId });
modelBuilder.Entity<Change_ImpactedService>().HasKey(t => new { t.ChangeId, t.ImpactedServiceId });
modelBuilder.Entity<Change_TestStage>().HasKey(t => new { t.ChangeId, t.TestStageId });
modelBuilder.Entity<Change_TypeOfChange>().HasKey(t => new { t.ChangeId, t.TypeOfChangeId });
}
}
Where I start running into problems is I'm not generating an Id using Entity Framework, the primary key is an identity in SQL Server 2012 and I get that back once the insert is completed, as opposed to using a GUID (which I've read pretty much everywhere is super frowned upon in the DBA world).
So what ends up happening is I either try and do the insert and it tries to insert the many to many relationships with changeId in the junction table being null (because it isn't generated yet) or when I try what I have below to do an insert and an update in one post method. It errors out because the ChangeId key value is already being tracked. Here is what I'm attempting below.
Controller method
public IActionResult CreateChange([FromBody] ChangeModel change) {
if (change == null) {
return BadRequest();
}
//Remove many to many from Change to insert without them (as this can't be done until primary key is generated.
List<Change_CriticalBankingAppModel> criticalApps = new List<Change_CriticalBankingAppModel>();
criticalApps.AddRange(change.Change_CriticalBankingApps);
List<Change_ImpactedBusinessModel> impactedBusinesses = new List<Change_ImpactedBusinessModel>();
impactedBusinesses.AddRange(change.Change_ImpactedBusinesses);
List<Change_ImpactedServiceModel> impactedServices = new List<Change_ImpactedServiceModel>();
impactedServices.AddRange(change.Change_ImpactedServices);
List<Change_TestStageModel> testStages = new List<Change_TestStageModel>();
testStages.AddRange(change.Change_TestStages);
List<Change_TypeOfChangeModel> changeTypes = new List<Change_TypeOfChangeModel>();
changeTypes.AddRange(change.Change_TypeOfChanges);
change.Change_CriticalBankingApps.Clear();
change.Change_ImpactedBusinesses.Clear();
change.Change_ImpactedServices.Clear();
change.Change_TestStages.Clear();
change.Change_TypeOfChanges.Clear();
//Map Change model to change entity for inserting
var changeEntity = Mapper.Map<Change>(change);
_changeRepository.AddChange(changeEntity);
if (!_changeRepository.Save()) {
throw new Exception("Creating change failed on save.");
}
var changetoReturn = Mapper.Map<ChangeModel>(changeEntity);
//Iterate through Many to many Lists to add generated changeId
foreach (var criticalApp in criticalApps) {
criticalApp.ChangeId = changetoReturn.ChangeId;
}
foreach (var impactedBusiness in impactedBusinesses) {
impactedBusiness.ChangeId = changetoReturn.ChangeId;
}
foreach (var impactedService in impactedServices) {
impactedService.ChangeId = changetoReturn.ChangeId;
}
foreach (var testStage in testStages) {
testStage.ChangeId = changetoReturn.ChangeId;
}
foreach (var changeType in changeTypes) {
changeType.ChangeId = changetoReturn.ChangeId;
}
//Add many to many lists back to change to update
changetoReturn.Change_CriticalBankingApps = criticalApps;
changetoReturn.Change_ImpactedBusinesses = impactedBusinesses;
changetoReturn.Change_ImpactedServices = impactedServices;
changetoReturn.Change_TestStages = testStages;
changetoReturn.Change_TypeOfChanges = changeTypes;
changeEntity = Mapper.Map<Change>(changetoReturn);
_changeRepository.UpdateChange(changeEntity);
if (!_changeRepository.Save()) {
throw new Exception("Updating change with many to many relationships failed on save.");
}
changetoReturn = Mapper.Map<ChangeModel>(changeEntity);
return CreatedAtRoute("GetChange",
new { changeId = changetoReturn.ChangeId },
changetoReturn);
}
Relevant Repository methods
public Change GetChange(int changeId) {
return _context.Change.FirstOrDefault(c => c.ChangeId == changeId);
}
public void AddChange(Change change) {
_context.Change.Add(change);
}
public void UpdateChange(Change change) {
_context.Change.Update(change);
}
public bool ChangeExists(int changeId) {
return _context.Change.Any(c => c.ChangeId == changeId);
}
I encounter this error on the update attempt.
I understand that if I were to have entity framework generate the guid instead of having the database generate the identity int that I would have a much easier time with this but a requirement for this project is to not use Guid's.
Any help on how to successfully process this would be greatly appreciated.
EDIT: In case it helps, here is the http post I'm using with postman.
{
"changeTitle": "Test",
"changeType": "Test",
"dateSubmitted": "02/12/2018",
"targetDate": "02/12/2018",
"change_CriticalBankingApps": [
{
"criticalBankingAppId" : 1,
"description" : "Very critical"
},
{
"criticalBankingAppId" : 2,
"description" : "Moderately critical"
}
],
"change_impactedBusinesses": [
{
"ImpactedBusinessId" : 1
},
{
"ImpactedBusinessId" : 2
}
]
}
The error you are getting has nothing to do with the guid vs db identity.
You are getting it because you are:
Fetching an entity from the database
Creating new entity (not tracked) from within your controller (the mapper does this)
Try to update the entity that is not tracked by entity framework
The update will try to add the entity to the EF repository, but will fail because it already contains an entity with the given ID.
If you plan to make changes to an entity, you need to make sure entity framework tracks the entity prior to calling the update method.
If EF does not track your entity, it does not know which fields have been updated (if any).
Edit:
If you want to get rid of the error, you could detach your original entity. Make sure you do it prior to mapping the changetoReturn back into your changeEntity.
dbContext.Entry(entity).State = EntityState.Detached;
But since your new entity won't be tracked, I don't think anything will be updated (EF does not know what has been changed).
Edit 2:
Also take a look at this to get your changes back into your original entity.
Change this:
changeEntity = Mapper.Map<Change>(changetoReturn);
Into this:
Mapper.Map(changetoReturn, changeEntity);
Using Automapper to update an existing Entity POCO
add new entities via joint table...that way, entities are tracked both in the joint table and their individual respective tables
Ok, whether this is an elegant solution is up for debate, but I was able to detach the entity state from changeEntity after doing the initial insert as follows
_changeRepository.AddChange(changeEntity);
_changecontext.Entry(changeEntity).State = EntityState.Detached;
Then after reattaching all of the many to many lists back to changeToReturn, I created a new Change entity and added that entity state, and updated on that as follows.
var newChangeEntity = Mapper.Map<Change>(changeToReturn);
_changecontext.Entry(newChangeEntity).State = EntityState.Added;
_changeRepository.UpdateChange(newChangeEntity);
Then I returned this mapped back to a view model.
It seems hacky and perhaps through a deeper understanding of entity framework I'll discover a much better way of going about this but this works for now.

Entity Framework: Best way to delete an entity when another entity gets deleted?

I have orders and orders have lines. When one deletes the last order line the order should be deleted as well. I'm struggling to find the best place for this.
The developer would just call context.Remove(orderLine) to remove the line. So the logic to then delete the order if this was the last line should be in the remove call.
The current idea would be to create a OrderLineDbSet which inherits from the DbSet and overwrite the Remove call there. But the issue is, that I don't have access to the DataContext because dependency injection does not work here...
Id did a bit of digging into https://github.com/mono/entityframework/blob/master/src/EntityFramework/DbSet.cs but I couldn't figure it out.
The last code i tried was kind of this:
public class OrderLineDbSet : DbSet<OrderLine>
{
CourseContext context { get; set; }
public OrderLineDbSet(CourseContext context)
{
this.context = context;
}
public override OrderLine Add(OrderLine entity)
{
return base.Add(entity);
}
public override OrderLine Remove(OrderLine entity)
{
Order order = entity.Order;
var line = base.Remove(entity);
if (!order.OrderLines.Any())
{
context.Orders.Remove(order);
}
return line;
}
}
The problem is that you're trying to push business logic into data access layer. Usually, you have a class, which implements some business logic, e.g.:
public class OrdersService
{
public void RemoveOrderLine(OrderLine orderLine)
{
// get db context (or some repository)
var context = GetDbContext();
// attach or load entities, etc.
// this is _business logic_;
// it is not natural for relational database;
// it is not related to db context or repositiory
context.OrderLines.Remove(orderLine);
if (!order.OrderLines.Any())
{
context.Orders.Remove(order);
}
}
}
In other words. Imagine, that after last line was removed, user must receive SMS, that order was removed too. Here's an action, that is totally unrelated to database. Do you want to put SMS sending in DbSet?
This should be set up with mapping and let entity framework remove the whole object graph.
public class MyContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderLine> Lines { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.HasMany(a => a.Lines)
.WithRequired(b => b.Orders)
.WillCascadeOnDelete();
}
}

EF7 beta6: Error saving multiple entities

I am creating an API using ASP.NET5 and Entity Framework 7.0.0-beta 6, and when I try to execute various updates in several requests, I get this Exception:
'Company' 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.
This is my code:
public class MrBellhopContext : DbContext
{
public DbSet<Company> Company { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>(entity =>
{
entity.Key(c => c.CompanyId);
entity.Index(c => c.Name);
entity.Property(c => c.CompanyId).ValueGeneratedOnAdd();
});
modelBuilder.UseSqlServerIdentityColumns();
base.OnModelCreating(modelBuilder);
}
}
public class Company
{
public int CompanyId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public short StatusId { get; set; }
}
public class CompanyRepository : ICompanyRepository
{
MrBellhopContext _dbcontext;
public async Task UpdateAsync(Company company)
{
_dbcontext.Update(company);
await _dbcontext.SaveChangesAsync();
}
}
[Route("api/[controller]")]
public class CompanyController : Controller
{
[HttpPut]
public async void UpdateAsync([FromBody] Company company)
{
if ((!ModelState.IsValid) || (company == null))
{
Context.Response.StatusCode = 400;
return;
}
else
{
await _repository.UpdateAsync(company);
}
}
}
I have tried to solve it by removing ValueGeneratedOnAdd(), UseSqlServerIdentityColumns() or changing the mapping, but if I try to update several entities in several requests, I get the Exception:
First req: Update CompanyId 8
First req: Update CompanyId 9 !! ERROR
Does anyone know how to solve this issue?
Resolved: https://github.com/aspnet/EntityFramework/issues/2652
I was adding Repository as a Singleton:
services.AddSigleton<Data.Interfaces.Company.ICompanyRepository,Data.Repositories.Company.CompanyRepository>();
This means that all requests are sharing a single instance of the repository. You should reduce this to Scoped, so that you have a single repository instance per request. Aside from avoiding the issue you are hitting, it will also ensure you don't end up with a gigantic context instance tracking all the data from your database in memory.
To solve:
services.AddScoped<Data.Interfaces.Company.ICompanyRepository,Data.Repositories.Company.CompanyRepository>();
You are getting this error because your instance of _repository already has an instance of Company in memory with the same key. This can happen when you are reusing an instance of DbContext across threads. Your sample above doesn't include the code about how CompanyController._repository gets instantiated. Make sure this is not shared between HTTP requests.
Also, put this line before you configure the key.
entity.Property(c => c.CompanyId).ValueGeneratedOnAdd();
Even I was facing the same issue.
I was registering the Repositories as singleton in Configure method of startup.cs file. Changing that to AddScoped fixed the issue.
Just need to use the below code to update the records
_dbContext.Company.Update(company);
_dbContext.SaveChanges();

EF Code First Lazy loading Not Working

I am using code first with EF6 but cannot seem to get lazy loading to work. Eager loading is working fine. I have the following classes:
public class Merchant : User
{
...
public virtual ICollection<MerchantLocation> MerchantLocations { get; set; }
}
public class MerchantLocation : BaseEntity
{
...
public int MerchantId { get; set; }
public virtual Merchant Merchant { get; set; }
}
public class User : BaseEntity
{
...
}
public class BaseEntity
{
...
public int Id { get; set; }
}
I test my lazy loading of the locations via the following code (which fails):
public void Test_Lazy_Loading() {
using (var context = new MyDbContext()) {
var merchant = context.Users.OfType<Merchant>.First();
merchant.MerchantLocations.ShouldNotBeNull(); // fails
}
}
However eager loading works fine:
public void Test_Eager_Loading() {
using (var context = new MyDbContext()) {
var merchant = context.Users.OfType<Merchant>.Include("MerchantLocations").First();
merchant.MerchantLocations.ShouldNotBeNull(); // passes
}
}
MerchantLocations is marked as public virtual so I'm not sure what the problem is. I have also added the following in my DbContext constructor:
Configuration.LazyLoadingEnabled = true;
Configuration.ProxyCreationEnabled = true;
edit: I have also noticed that the merchant object being returned in the above tests is not an EF proxy. It is a plain Merchant. I suspect that this is causing the problem.
I realized that the problem was that the Merchant class did not meet requirements for proxy generation. Specifically, I needed to add a protected parameterless constructor. I only had a private one.
Another thing that can cause lazy loading to fail is navigation properties that are not virtual. That was not the case for OP, but this question is a top Google result so it may help some.
And yet another possible cause is a mapped database column that doesn't exist. I was surprised to see that break lazy loading rather than throw a database exception.

Categories