Reputation: 23
This is my repository with Entity Framework:
public class MetricRepository : IMetricRepository
{
private readonly AccountStatsContext _context;
public MetricRepository(AccountStatsContext context)
{
_context = context;
}
public async Task<bool> Add(MetricEntity metric)
{
metric.UpdatedOnUtc = DateTime.UtcNow;
await _context.Metrics.AddAsync(metric).ConfigureAwait(false);
await _context.SaveChangesAsync().ConfigureAwait(false);
return true;
}
public async Task<bool> Update(MetricEntity metric)
{
metric.UpdatedOnUtc = DateTime.UtcNow;
await _context.SaveChangesAsync();
return true;
}
public async Task<MetricEntity> FindAsync(Expression<Func<MetricEntity, bool>> predicate)
{
return await _context.Metrics.SingleOrDefaultAsync(predicate);
}
}
And in my table I have a composite primary key with 4 columns like this:
CREATE TABLE public.metrics
(
category character (30) COLLATE pg_catalog."default" NOT NULL,
code character (30) COLLATE pg_catalog."default" NOT NULL,
repartition_key character (30) COLLATE pg_catalog."default" NOT NULL,
date timestamp without time zone NOT NULL,
value numeric(18,2),
type character (5) COLLATE pg_catalog."default" NOT NULL,
updated_on_utc timestamp without time zone NOT NULL,
CONSTRAINT metrics_pkey PRIMARY KEY (category, code, repartition_key, date)
);
And the primary key is set in my Entity Framework repository
private void BuildEntity(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MetricEntity>()
.ToTable("metrics")
.HasKey(c => new { c.Category, c.Code, c.RepartitionKey, c.Date });
}
When I make an insert it's ok but when I try to update a value, I get this error:
Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded
Here is my code for the update:
public async Task<BaseResponse> Handle(MetricsCommand request, CancellationToken cancellationToken)
{
var requestMapped = _mapper.Map<MetricEntity>(request);
var result = await _metricRepository.FindAsync(m => m.Category == requestMapped.Category && m.Code == requestMapped.Code && m.RepartitionKey == requestMapped.RepartitionKey && m.Date == requestMapped.Date);
bool response = false;
if (result == null)
response = await _metricRepository.Add(requestMapped);
else
{
result.Value = requestMapped.Value;
result.Type = requestMapped.Type;
response = await _metricRepository.Update(result);
}
return new BaseResponse(response);
}
Upvotes: 0
Views: 228
Reputation: 43850
I always use this algoritm to update only several properties, it has never failed
var metric = _mapper.Map<MetricEntity>(request); //I hope your code works properly
var existedMetric = await _context.FindAsync(m => m.Category == requestMapped.Category && m.Code == requestMapped.Code && m.RepartitionKey == requestMapped.RepartitionKey && m.Date == requestMapped.Date);
if (existedMetric!=null)
{
context.Entry(existedMetric).Property(m=> m.Value ).CurrentValue = metric.Value;
context.Entry(existedMetric).Property(m=> m.Type).CurrentValue = metric.Type;
context.Entry(existedMetric).Property(m=> m.UpdatedOnUtc).CurrentValue = DateTime.UtcNow;
await _context.SaveChangesAsync();
}
Upvotes: 1
Reputation: 1577
Change your Generic Update method like this. You should set the State of your Entity to Modified
public async Task<bool> Update(MetricEntity metric)
{
metric.UpdatedOnUtc = DateTime.UtcNow;
_context.Entry(metric).State = EntityState.Modified;
await _context.SaveChangesAsync();
return true;
}
Upvotes: -1
Reputation: 38
The best way to use a database context
is to create and dispose it right before and after it is used. But since you are already using another approach, I may only suppose that the following code can help you.
Insert the provided line after metric.UpdatedOnUtc = DateTime.UtcNow;
in your Update
method of MetricRepository
class:
_context.Entry(metric).State = EntityState.Modified;
UPD:
If it doesn't help, there is another way of modifying entities in a dbcontext
. Insert it in same place as the previous hint:
_context.Update(metric);
Upvotes: 1