Saving related entities in EF with transient dbContext fails - asp.net-core

I have a asp net core 3.1 web application. We use repository pattern with entity framework.
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(_configuration.GetConnectionString("MyConnString")), ServiceLifetime.Scoped);
Made up scenario, just bec everybody knows the "Customer-orders"-scenario:
When I create a new Order and want to connect it to an existing Customer I write something like this. Works ok.
var order = new Order(){...}
order.Customer = _dbContext.Customers.FirstOrDefault(c=>c.CustomerId = 42);
_dbContext.Orders.Add(order);
_dbContext.SaveChanges();
However. For other reasons we need to have dbContext transient, initiated like below:
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(_configuration.GetConnectionString("MyConnString")), ServiceLifetime.Transient);
This makes the code above fail. Error message is the Customer is already attached to dbContext ("The instance of entity type 'Customer' cannot be tracked because another instance with the same key value for {'CustomerId'} is already being tracked. When attaching existing entities, , ensure that only one entity instance with a given key value is attached")
or, if I test setting order.Customer = null:
The error message says "cant insert Id on identity column" (which it shouldnt, the customer already exists). ("Cannot insert explicit value for identity column in table 'Users' when IDENTITY_INSERT is set to OFF.")

Related

Entity Framework Core GetOrAdd Method

I'm working on a post method that should have a really good performance.
I have a value in the request body that will look in the database for the row that is connected with that value and return it and add it as a foreign key.
So how it is now:
Look in the database and check if the data already exists
If no add it to the database
Look that added or already existing data in the database and join it to the entity
So now there are 3 calls to the database
I was wondering if there is some kind of GetOrAdd method that will connect the table to my data if it exists and if it not exists add it to the database so it will most of the time only have 1 call to the database instead of always 3 calls
Please read the following doc
Here is an "Insert or Update" pattern:
public void InsertOrUpdate(Blog blog)
{
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}
Of note, once you hit SaveChanges() you can expect your in memory object (blog, in this case) to be the same object that is stored in the database, and would not have to make a 3rd call to retrieve it again. EF Core will update the Primary Key with the actual persisted Id.
using (var context = new BloggingContext())
{
var blog = new Blog {Id = 1, Url = "blablabla" };
context.Blogs.Update(blog);
await context.SaveChangesAsync();
}
If a reachable entity has its primary key value set then it will be tracked in the Microsoft.EntityFrameworkCore.EntityState.Modified state. If the primary key value is not set then it will be tracked in the Microsoft.EntityFrameworkCore.EntityState.Add state.
This comment is from Entity Framework Core's Update method. You just need to call Update method if entity is exist in Database it will be updated otherwise will be created as new record.

Include operator could not be bound in Entity Framework Core with Identity DB

I am writing a application using ASP.Net Core + EF Core + Identity 2.0 + DB First + Repository Pattern
From DB first I am scaffolding an existing database (with Identity tables) and do some clean up steps in Context to make it work for Identity.
Inherit it from IdentityDbContext instead of DbContext
public partial class SampleDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
Remove "OnConfiguring" method override
Add a new constructor for the SampleDbContext class public SampleDbContext (DbContextOptions<SampleDbContext> options):base(options){}
Remove all DbSet configuration for EF Core Identity tables such as,
AspNetRoleClaims
AspNetRoles
AspNetUserClaims
AspNetUserLogins
AspNetUserRoles
AspNetUserTokens
AspNetUsers
In "OnModelCreating" override function add below line base.OnModelCreating(modelBuilder);
Remove all modelBuilder.Entity configs for all EF Core Identity tables mentioned above.
Add below line in OnModelCreating() function,
modelBuilder.Entity<AspNetUserLogins>().HasKey(i => i.LoginProvider);
modelBuilder.Entity<AspNetUserLogins>().HasKey(i => i.ProviderKey);
modelBuilder.Entity<AspNetUserRoles>().HasKey(i => i.UserId);
modelBuilder.Entity<AspNetUserRoles>().HasKey(i => i.RoleId);
modelBuilder.Ignore<AspNetRoleClaims>();
modelBuilder.Ignore<AspNetRoles>();
modelBuilder.Ignore<AspNetUserClaims>();
modelBuilder.Ignore<AspNetUserLogins>();
modelBuilder.Ignore<AspNetUserRoles>();
modelBuilder.Ignore<AspNetUsers>();
So far all works fine. Able to read/write data to DB.
Now, attempting to read a table data which has a foreign key reference to one of Identity tables (ASPNetUsers) as follows,
var dbEntries = await _addressRepository.GetAllAsync(x => x.CreatedBy);
where CreatedBy is foreign key reference to "Id" columns in AspNetUsers table passed as include property to my repository.
Now the above GetAll() command throws exception,
InvalidOperationException: The expression '[x].CreatedBy' passed to the Include operator could not be bound.
Could any one help me to resolve this issue? I spent over a day to find solution online, but nothing got.
Additional Note: All are working fine if I refer my own tables in Include objects. Issue only with Identity tables.
Thanks in advance.

Entity Framework 5 foreign key values not updating (VB.NET)

I realise many similar questions have been asked but after attempting tens of examples I still cannot get this to work. The problem I am having involves the updating of foreign key values in the Users table.
I am creating a website using ASP WebForms, .net 4.5, Entity Framework 5, SQL server 2012.
I created the EF using database first, here you can see a sample of the model:
I am using a 3 tier architecture - my data access layer queries the database using LINQ and returns a User object (as defined by T4).
This means the object is disconnected from the context when it is modified. (right?)
I modify the object in the page's code behind, updating the UserRole by doing this:
(in page load)
Dim _userAcces As UserLayer = UserLayer.getInstance
mUser = _userAcces.getUserWithCustomerAndRole(_ID)
Dim _rolesAccess As UserRolesLayer = UserRolesLayer.getInstance
mRoles = _rolesAccess.listUserRoles
(on button click)
mUser.UserRole = mRoles.Item(dropDownRole.SelectedIndex)
At this point the mUser object holds the correct (updated) UserRole object. I then pass the mUser object back into my data layer to update the database. The simnplest examples I have seen do this:
Using _context As New SaasPortalContext
_context.Entry(updatedUser).State = EntityState.Modified
_context.SaveChanges()
End Using
I have also tried using:
_context.users.attach
getting the original from the context using _context.users.find(id) and updating the original from the updated User object
getting the dbentityentry object from _context.entry(updatedUser) and setting the property("UserRole").isModified property to true
Using any of these methods I expect the foreign key value for UserRole in the Users table to update to be the ID of a different role in the UserRole table, reflecting the changes in the mUser object, but this does not happen and the value does not change.
What am I doing wrong? Why will the ID not update in the Users table?
Any help would be greatly appreciated
I finally worked out what I was doing wrong, I did not check the box for "include foreign key relationships" when setting up the model, meaning the relationships were not properly defined (I assume).

Trying to update entities from a disconnected entity

Ok, each and every time I get into this situation, I struggle back and forth until I find a way to solve it (and that is usually not the way I would have liked to solve it).
What I'm talking about is disconnected entities in EF that should update existing entities in the database.
I'll give an example of my problem here (this example is the last time I got into this problem that caused me to write this question).
I have a WCF service that uses Entity Framework as well. The other program that have added a service reference to my service have gotten proxy versions of the Entities as normal.
The case is that the consumer of the service now construct a object of this proxy class, and call the method UpdateEntity on the WCF service. This entity has a foreign key to another type of entities, and the primary key of the entity I want to link this new entity to is also sent as a parameter to this method. In this case, I want the entity with the same primary key in the database to be updated. It seems simple enough right?
My method looks something like this now:
public bool ChangeEntity(MyEntity entity, int otherTableForignKey)
{
//first I verify that the entity to update exist in the system
var entitytochange = entityContext.MyEntities.FirstOrDefault(e => e.Name == entity.Name);
if (systemtochange == null) return false;
try
{
entity.ForignEntity = entityContext.ForeignEntities.FirstOrDefault(f => f.Key == otherTableForignKey);
//code for updating the entity should go here, but I'm nor sure what
entityContext.SaveChanges();
return true;
}
catch (Exception exc)
{
return false;
}
}
I tried many different combinations of ApplyCurrentValues, Attach, setting ObjectState to Modified and so on, but I get either the error message that I can't add a new entity with the same key as an existing entity, that the object state of the new object can't be Added and so on.
So my question is: What is the best way to do this without writing code that looks like a big hack.
The only way I got this working now was to just set the properties of entitytochange manually with the properties of entity, but it is a bad solution since any added properties to MyEntity will break the code if I don't remember to add code in this method as well, and it seems there really should be another way that is better.
EDIT
When I put entityContext.MyEntities.ApplyCurrentValues(entity); where my comment is put above, I get the following exception on this line:
The existing object in the ObjectContext is in the Added state. Changes can only be applied when the existing object is in an unchanged or modified state.
However, if I remove this line above entity.ForignEntity = entityContext.ForeignEntities.FirstOrDefault(f => f.Key == otherTableForignKey); then the ApplyCurrentValues works without any problems.
Why would me setting the ForeignEntity of the object set it to Added state? So it seems that setting a Property on the Detached entity, attaches it to the context with a state of added?

Why does NHibernate need to know the ID of an auto ID based entity before flush is called?

With my only ORM knowledge being L2S/EF, I was surprised when the following code inserted a row into the database before I called repo.Save:
var repo = new UserRepository();
var user = new User { Name = "test" }
repo.Add(user);
//repo.Save();
Repo looks like this:
public void Add(T entity)
{
session.Save(entity);
}
public void Save()
{
session.Flush();
}
After some digging, it seems NHibernate needs to make the insert happen right away in order to get the ID of the new entity (since it's using an auto increment ID). But L2S/EF doesn't work like this; I can add many entities and save them all at the end.
Question is: is there a way to achieve the same thing with NHibernate, while still using auto increment IDs, and out of interest does anyone know why it works like this?
Fabio Maulo already blogged about the usage of identity generator a few times. The answer is: use hilo, guid.comb or something like this.
NHibernate needs the identity because every entity in the session (they are called "persistent entities") needs to be identified. The identity is also normally used to determine if the record already exists in the database (unsaved value).
session.Save actually only makes a transient entity persistent. When the database is generating the id, it needs to be stored to get the id. If NH can create the id itself (eg using hilo), it could be stored next time when the session gets flushed.