C
C#ā€¢4mo ago
Spugnetta

Updating Immutable object with ef

Hi guys, i have an immutable object, so if i want to update it i have to crate a new istance of it, when doing so i create conflicts with the original tracked object key id, can someone help me out with this? šŸ˜„
35 Replies
Angius
Angiusā€¢4mo ago
.ExecuteUpdateAsync() maybe If it's immutable, then the change tracker... cannot change changes So the usual fetch -> modify -> save won't work
var rowsAffected = await _context.Things
.Where(...)
.ExecuteUpdateAsync(setters => setters
.SetProperty(t => t.Foo, "abc")
.SetProperty(t => t.Bar, 781));
var rowsAffected = await _context.Things
.Where(...)
.ExecuteUpdateAsync(setters => setters
.SetProperty(t => t.Foo, "abc")
.SetProperty(t => t.Bar, 781));
Patrick
Patrickā€¢4mo ago
like i said:
var worker = await _context.Workers.FirstOrDefaultAsync(x => x.Id == upsertWorker.Id);

if(worker is null)
{
worker = Worker.CreateWithId(...);
_context.Workers.Add(worker);
}

worker.FooBar = baz;

await _context.SaveChangesAsync();
var worker = await _context.Workers.FirstOrDefaultAsync(x => x.Id == upsertWorker.Id);

if(worker is null)
{
worker = Worker.CreateWithId(...);
_context.Workers.Add(worker);
}

worker.FooBar = baz;

await _context.SaveChangesAsync();
you need to remove AsNoTracking and do it right šŸ™‚
Spugnetta
Spugnettaā€¢4mo ago
hei sorry i dont understand this will leave me with the problem that i get key conflict
Patrick
Patrickā€¢4mo ago
no it won't
Spugnetta
Spugnettaā€¢4mo ago
when creating a new one it wont but when updating the old one it does :C
Patrick
Patrickā€¢4mo ago
no it won't
Spugnetta
Spugnettaā€¢4mo ago
i must doing somthing wrong then wait i sec i implement it
Patrick
Patrickā€¢4mo ago
yes, that's what I'm explaining.
leowest
leowestā€¢4mo ago
that is the difference of AsNoTracking now its tracking the Entity with Patricks code that's what he wanted u to notice ;x (I think)
Spugnetta
Spugnettaā€¢4mo ago
with this implementation, ef wont update the worker:
No description
Patrick
Patrickā€¢4mo ago
you need to read the code I posted and understand it you need to use the tracked existing entity OR make a new one not both. post that code here. i dont edit screenshots.
Spugnetta
Spugnettaā€¢4mo ago
public async Task<(bool Created, WorkerResponse Worker)> UpsertWorkerAsync(UpsertWorkerRequest upsertWorker) { var worker = _context.Workers.FirstOrDefault(w => w.Id == upsertWorker.Id); var updatedWorker = Worker.CreateWithId(upsertWorker.Id, upsertWorker.Name, upsertWorker.Role); if (worker is not null) { worker = updatedWorker; await _context.SaveChangesAsync(); return (false, updatedWorker); } else { _context.Workers.Add(updatedWorker); await _context.SaveChangesAsync(); return (true, updatedWorker); } }
Patrick
Patrickā€¢4mo ago
public async Task<(bool Created, WorkerResponse Worker)> UpsertWorkerAsync(UpsertWorkerRequest upsertWorker)
{
isCreated = false;
var worker = _context.Workers.FirstOrDefault(w => w.Id == upsertWorker.Id);


if (worker is null)
{
worker = Worker.CreateWithId(upsertWorker.Id, upsertWorker.Name, upsertWorker.Role);
_context.Workers.Add(worker);
isCreated = true;
}

worker.Name = upsertWorker.Name;

return (isCreated, worker);
}
public async Task<(bool Created, WorkerResponse Worker)> UpsertWorkerAsync(UpsertWorkerRequest upsertWorker)
{
isCreated = false;
var worker = _context.Workers.FirstOrDefault(w => w.Id == upsertWorker.Id);


if (worker is null)
{
worker = Worker.CreateWithId(upsertWorker.Id, upsertWorker.Name, upsertWorker.Role);
_context.Workers.Add(worker);
isCreated = true;
}

worker.Name = upsertWorker.Name;

return (isCreated, worker);
}
you only want to create a new entity where it's actually required, otherwise use the existing one. i suspect the problem you have is trying to reuse Worker.CreateWithId when you shouldn't be.
Spugnetta
Spugnettaā€¢4mo ago
i want to do an upsertoperation that's my goal, if the entity does not exist create it if it does update it im sorry i still dont understand :Smadge: what this should do? i cant update properties like that directly, the object is immutable i must create a new istance
Patrick
Patrickā€¢4mo ago
1. it tries to query an existing worker:
var worker = _context.Workers.FirstOrDefault(w => w.Id == upsertWorker.Id);
var worker = _context.Workers.FirstOrDefault(w => w.Id == upsertWorker.Id);
2. if the worker does not exist, it instantiates a new Worker entity and adds it to the context 3. if the worker does exist, and has been queried, no new worker is created 4. the relevant properties of the worker are updated in all pathways that's not really how EF works šŸ™‚
Spugnetta
Spugnettaā€¢4mo ago
the worker record has private setters and private parameterless ctor that's why im doing this
No description
Spugnetta
Spugnettaā€¢4mo ago
what could go wrong leaving this implementation like this? cause im getting the desired result this way
Patrick
Patrickā€¢4mo ago
if they're private setters, make a method
worker.Update(otherWorker);
worker.Update(otherWorker);
Spugnetta
Spugnettaā€¢4mo ago
there's some validation and calculation going on in the creation
Patrick
Patrickā€¢4mo ago
ok the object is already created so šŸ™‚
Spugnetta
Spugnettaā€¢4mo ago
maybe i just did some horrible disign decisions, should i just make an anemic class with public props as my models?
Patrick
Patrickā€¢4mo ago
no you don't get my point the object already exists at the point of calling update you either create it or query it
Spugnetta
Spugnettaā€¢4mo ago
then should i insted of update it, first delete it and then create a new one if exists?
Patrick
Patrickā€¢4mo ago
no....
Spugnetta
Spugnettaā€¢4mo ago
:Smadge:
Patrick
Patrickā€¢4mo ago
you realise that whatever CreateWithId does, EF doesn't do when you query the entity, right? so these "calculations" have already occurred when you already inserted the entity, assuming you persist them to the db. You don't need to do them again if you're querying an existing object from the database. if the problem is that CreateWithId is actually performing calculations for stuff that isn't persisted in the database, that is a design problem.
Spugnetta
Spugnettaā€¢4mo ago
i need to understand something, what if i have a record as an enitity with just and Id and Name, how can i update that record with ef?
Patrick
Patrickā€¢4mo ago
you shouldn't use records as entities if you absolutely must, I suppose you would need to use ExecuteUpdateAsync.
Spugnetta
Spugnettaā€¢4mo ago
context.Blogs .Where(b => b.Rating < 3) .ExecuteUpdate(setters => setters.SetProperty(b => b.IsVisible, false)); how does it works? does it use some kind of reflection to access even readonly fields?
Angius
Angiusā€¢4mo ago
If it's just a
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
?
var person = await _ctx.People.FirstOrDefaultAsync(p => p.Id == id);
person.Name = "Bob";
person.Age += 5;
await _ctx.SaveChangesAsync();
var person = await _ctx.People.FirstOrDefaultAsync(p => p.Id == id);
person.Name = "Bob";
person.Age += 5;
await _ctx.SaveChangesAsync();
or, nowadays,
var affectedRows = await _ctx.People
.Where(p => p.Id == id);
.ExecuteUpdateAsync(s => s
.SetProperty(p => p.Name, "Bob")
.SetProperty(p => p.Age, p => p.Age + 5));
var affectedRows = await _ctx.People
.Where(p => p.Id == id);
.ExecuteUpdateAsync(s => s
.SetProperty(p => p.Name, "Bob")
.SetProperty(p => p.Age, p => p.Age + 5));
Yes
Patrick
Patrickā€¢4mo ago
they're asking about records not classes
Angius
Angiusā€¢4mo ago
For a record, then yeah, ExecuteUpdate() But as you said, you usually wouldn't use immutable stuff like records for EF It's easier to just mutate and save
Spugnetta
Spugnettaā€¢4mo ago
ok thx šŸ˜„ then i need a class as a model and if i want something to be immutable i should use it in its properties
Spugnetta
Spugnettaā€¢4mo ago
@Patrick @ZZZZZZZZZZZZZZZZZZZZZZZZZ what about this? this look reasonable?
No description
Patrick
Patrickā€¢4mo ago
doesn't matter what i say you won't do what i suggest anyway so sure šŸ‘