System.InvalidOperationException in EF core when trying to update - asp.net-core

Problem
I am creating a web app in asp.net core with ef core using with Repository pattern. Ii am trying to insert an entity it works fine when i trying to check whether it is already exist in db or then if yes than update the same entity in same api it gives me this error.
System.InvalidOperationException: 'The instance of entity type 'Consultation' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.'
Image
code
insert api
[HttpPost]
public ApiResponse InsertConsultation([FromBody] Consultation consultation)
{
if (!ModelState.IsValid)
{
return new ApiResponse(StatusCodes.Status400BadRequest, error: "error");
}
var consult = _consultationService.GetConsultationById(consultation.Id);
if (consult !=null)
{
_consultationService.UpdateConsultation(consultation);
return new ApiResponse(StatusCodes.Status200OK, success: "isSuccess");
}
_consultationService.InsertConsultation(consultation);
return new ApiResponse(StatusCodes.Status201Created, success: "isSuccess");
}

The update process is
1- Retrieve the object which is consult in your code:
var consult = _consultationService.GetConsultationById(consultation.Id);
2- make change to the retrieval object (copy content from consultation to consult) you don't have it.
3- update the object consult
4- save change.

Maybe try this for the line before SaveChanges().
_context.AddOrUpdate(entity);
This needs the System.Data.Entity.Migrations namespace.
Your question might already be answered here:
Update Row if it Exists Else Insert Logic with Entity Framework

Related

UserManager throws exception - Unable to track an entity because primary key property 'Id' is null - after upgrading from .Net Core 2.2 to 3.0

I've used the custom implementation of User which is derivated from IdentityUser using Asp.Net Core Identity:
public class AppUser : IdentityUser
{
...
}
If I call the method:
var identityResult = await UserManager.CreateAsync(user);
I get the error:
System.InvalidOperationException: Unable to track an entity of type 'AppUser' because primary key property 'Id' is null.
It works perfectly with version of Microsoft.AspNetCore.Identity.EntityFrameworkCore - 2.2.0, but after upgrading to 3.0.0 - it doesn't work.
I get same error during testing of the creation of user as well, there I use following UserManager configuration:
https://github.com/aspnet/AspNetCore/blob/c95ee2b051814b787b07f55ff224d03d550aafeb/src/Identity/test/Shared/MockHelpers.cs#L37
There is EF Core 3.0 introduced breaking change String and byte array keys are not client-generated by default which affects entities which use string PK like IdentityUser<string> and IdentityRole<string> and will cause that exception if the PK is not set explicitly.
However the default IdentityUser and IdentityRole classes were also modified to populate the Id property with Guid.NewGuid().ToString() inside class constructors. So normally you shouldn't have that issue except if some code is setting explicitly the Id to null. Anyway, you can switch to old behavior by using the suggestion from the link
Mitigations
The pre-3.0 behavior can be obtained by explicitly specifying that the key properties should use generated values if no other non-null value is set.
e.g. inside context OnModelCreating override, after calling base implementation:
modelBuilder.Entity<AppUser>()
.Property(e => e.Id)
.ValueGeneratedOnAdd();

adding new document to CouchDb using ArmChair throws exception Object reference not set to an instance of an object

Iam using couch-db version 2.2.0
and I want to make crud operations on couchdb database using .Net
so I installed Armchair.Core Nuget Package version 0.11.2
and in order to add d a new document I, followed the code that is mentioned in
not finished wiki yet
https://bitbucket.org/dboneslabs/arm-chair/wiki/main-api/session-api.md
Database mydatabase = new Database("TestDb",newConnection("http://localhost:5984"));
using (var session = mydatabase.CreateSession())
{
var author = new Person("Jone");
session.Add(author);// NOTE: If no Id has been assigned before the instance is added to the Session, then ArmChair will assign it. After the object is committed to the database, the revision will then be set onto the instance
session.Commit();
}
but I still getting the error
Object reference not set to an instance of an object.
also mydatabase variable mentioned in previous code has values null for Connection and DataBase Parameters even though i passed them in the constructor as it doesn't connect to couchdb database at all and never tries to create database TestDb
any help please ,are there any wrong calls in my code
ArmChair connects to an existing database and does not create one.
if you want to create a database, have a look a look at the sample application, in the Autofac registration there is a method which ensures that there is a database created.
https://bitbucket.org/dboneslabs/arm-chair/src/bd4e70d6c51d8b45cfb89eb65ecf81a4ecefb691/samples/todo/Todo.Service/Infrastructure/Modules/DataAccessModule.cs#lines-62
its not the pretty-est of code but works.

Breeze JS Query before calling SaveChanges causes failure

I have the following very standard Breeze API Controller endpoint in my app:
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
My backend database uses int IDs that are configured as identities.
When called from a client with a valid change set, all is well.
However, if, prior to the call to my _contextProvider.SaveChanges(saveBundle) function, I make a query of any sort. For example:
public SaveResult SaveChanges(JObject saveBundle)
{
int incomingUserId = Convert.ToInt32(saveBundle["entities"][0]["Id"]);
AspNetUser EditedUser = (from u in Context.AspNetUsers where u.Id == incomingUserId select u).FirstOrDefault();
// ......
// do something with the EditedUser (like validations of any sort)
// ......
return _contextProvider.SaveChanges(saveBundle);
}
the save fails with error:
Saving or accepting changes failed because more than one entity of type 'xxxx.App_Data.AspNetUser' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration.
Given what I am doing, is this expected behavior? Is there any other way to do an initial, separate query just prior to the SaveChanges call without upsetting something and making the query fail?
You'll need to create a second Context for performing the query. In the code you have above, the EditedUser object is being cached in the Context, because that's what EF does. Then, when _contextProvider.SaveChanges(saveBundle) is called, the contextProvider tries to create an object with the same type and same key in the same Context. Hence the error.
This SO post gives some hints about creating the second Context.

How to save and then update same class instance during one request with NHibernate?

I'm relatively new to NHibernate and I've got a question about it.
I use this code snippet in my MVC project in Controller's method:
MyClass entity = new MyClass
{
Foo = "bar"
};
_myRepository.Save(entity);
....
entity.Foo = "bar2";
_myRepository.Save(entity);
The first time entity saved in database succesfully. But the second time not a single request doesnt go to database. My method save in repository just does:
public void Save(T entity)
{
_session.SaveOrUpdate(entity);
}
What should I do to be able to save and then update this entity during one request? If I add _session.Flush(); after saving entity to database it works, but I'm not sure, if it's the right thing to do.
Thanks
This is the expected behavior.
Changes are only saved on Flush
Flush may be called explicitly or implicitly (see 9.6. Flush)
When using an identity generator (not recommended), inserts are sent immediately, because that's the only way to return the ID.
you should be using transactions.
a couple of good sources: here and here.
also, summer of nHibernate is how I first started with nHibernate. it's a very good resource for learning the basics.

Saving an NHibernate object after AutoMapping it to a DTO

We are trying to use NHibernate 1.1 to as the persistence layer behind a web service API. Nothing new there. We use Automapper to translate the domain objects we get from (Fluent-)NHibernate to DTO's which we send over the wire.
The problem we are seeing is the following scenario:
We read an object from the repository
We translate that object (via Automapper) into the DTO form.
After some, perhaps trivial fiddling, the object is mapped back from DTO to the Fluent-Nhibernate object (including the primary key which cannot change).
We save the object.
After step 4 we get a Nonuniqueobjectexception from NHibernate and it creates a new row to save the object (with a new primary key). We want to update the original row but instead, new rows get modified(!)
So, in this situation, how can we convince NHibernate to do an actual update instead of an insert operation?
BTW, if we skip the translation to the DTO and back, there is no problem with the update.
== Tevya ==
1) NHibernate 1.1 or Fluent NHib 1.1?
2) I think you have a session management problem. If you load one object in session1 and try to persist it without attaching the actual object to session2 you will end up with a new row in the DB. Try to read and update the object within one session and see the results.
You could try merging the object into your session.
// Assuming you already have a session open
// and you've already mapped your DTO back
using (var tx = session.BeginTransaction())
{
var nhibernateObject = (YourNhibernateObjectType)session.Merge(mappedBackFromDTO);
tx.Commit();
}
You should attach your (new) object after mapping from DTO to the current ISession. Attaching is the operation made by ISession.Update method.