Reputation: 1176
I'm having trouble with EF Core 3.1. with code that worked up until recently. I have this method that is called before SaveChanges is called on DbContext:
private void SetProps<TEntity>(TEntity dbEntity, EntityState newState)
where TEntity : class, new()
{
var entry = this.dbContext.Entry(dbEntity);
...
if (dbEntity is IDbEntityBase dbEntityBase)
{
var baseEntry = this.dbContext.Entry(dbEntityBase);
if (newState == EntityState.Added)
{
baseEntry.Property(t => t.Id).CurrentValue = Guid.NewGuid();
}
}
}
A lot of code is left out for easier reading, so any design changes would be hard to do.
The problem I'm having is in the call to baseEntry.Property(t => t.Id).CurrentValue = Guid.NewGuid();
. When that code is called, EF is throwing an exception 'this' type cannot be an interface itself.
.
IDbEntity is some base interface that all entity classes implement (it has some common properties).
EF call stack looks like this:
System.ArgumentException: 'this' type cannot be an interface itself.
at System.RuntimeTypeHandle.VerifyInterfaceIsImplemented(RuntimeTypeHandle interfaceHandle)
at System.RuntimeType.GetInterfaceMap(Type ifaceType)
at System.Reflection.RuntimeReflectionExtensions.GetRuntimeInterfaceMap(TypeInfo typeInfo, Type interfaceType)
at Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions.GetPropertyAccess(LambdaExpression propertyAccessExpression)
at Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry`1.Property[TProperty](Expression`1 propertyExpression)
at InDocEdge.Sap.Database.EfContext.SapDbContext.ProcessBeforeSaveChangesData() in C:\MyCode\MyDbContext.cs:line 2
at InDocEdge.Sap.Database.EfContext.SapDbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) in C:\MyCode\MyDbContext.cs:line 1
The code worked until recently, but somehow now EF is not happy (as I understand) that I provide interface type for Entry method, which I think is possible.
Does anybody know what could be wrong or how to solve this issue?
Upvotes: 2
Views: 696
Reputation: 89266
There's just no such thing as an EntityEntry for an Interface type. It needs to be an Entity Type. And EntityEntry<TEntity>.Property
needs to find the Entity Type Property, not the Interface Property (they are conceptually and possibly actually different).
But in the normal case, your Entity's property implementing IDbEntityBase.Id
will also be called Id
, and you can just pass that name to entry.Property(string)
, eg:
private void SetProps<TEntity>(TEntity dbEntity, EntityState newState) where TEntity : class, new()
{
var entry = Entry(dbEntity);
if (dbEntity is IDbEntityBase)
{
if (newState == EntityState.Added)
{
entry.Property(nameof(IDbEntityBase.Id)).CurrentValue = Guid.NewGuid();
}
}
}
Or, since you're only setting the EntityEntry.CurrentValue
, you can set it directly through the Entity object, through an IDbEntityBase reference, instead of the EntityEntry. EG:
private void SetProps<TEntity>(TEntity dbEntity, EntityState newState) where TEntity : class, new()
{
if (dbEntity is IDbEntityBase dbEntityBase)
{
if (newState == EntityState.Added)
{
dbEntityBase.Id = Guid.NewGuid();
}
}
}
Upvotes: 1