Exception when accessing Original property
Closed this issue · 5 comments
The following code does not work, when Entity is a proxy:
Triggers<EntityBase>.Updating += entity => {
if (entity.Original.Version != entity.Entity.Version) { <== Exception
entity.Cancel = true;
// ReSharper disable once PossibleNullReferenceException
var entityName = entity.Entity.GetType().BaseType != null
&& entity.Entity.GetType().Name.StartsWith(entity.Entity.GetType().BaseType.Name)
// ReSharper disable once PossibleNullReferenceException
? entity.Entity.GetType().BaseType.Name
: entity.Entity.GetType().Name;
throw new DbUpdateConcurrencyException(
$"Validation failed for entitiy {entityName} with id {entity.Entity.Id}. Version in Database in different from version in Entity.");
}
entity.Entity.Updated = DateTime.UtcNow;
entity.Entity.Version += 1;
};
Exception is:
System.TypeLoadException
HResult=0x80131522
Message=Could not load type 'Hotel_FEB49A97C767CB2D75BD11061E9CD3A5AC5E68203057224FA38195FCC1D93530__OriginalValuesWrapper' from assembly 'Hotel_FEB49A97C767CB2D75BD11061E9CD3A5AC5E68203057224FA38195FCC1D93530__OriginalValuesWrapperAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' because the parent type is sealed.
Source=<Cannot evaluate the exception source>
StackTrace:
<Cannot evaluate the exception stack trace>
I could solve the problem by changing the line
if (entity.Original.Version != entity.Entity.Version) {
to
if (entity.Context.Entry(entity.Entity).OriginalValues.ToObject() is EntityBase org && org.Version != entity.Entity.Version) {
It may be a problem of EntityFramework.TypedOriginalValues...
Could you post your EntityBase
class definition?
Below is the complete EntityBase class:
using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
using EntityFramework.Triggers;
namespace DotNetFire.Data.Entity {
public abstract class EntityBase {
static EntityBase() {
Triggers<EntityBase>.Inserting += entity => {
entity.Entity.Created = entity.Entity.Updated = DateTime.UtcNow;
entity.Entity.Version = 1;
};
Triggers<EntityBase>.Updating += entity => {
//if (entity.Original.Version != entity.Entity.Version) {
if (entity.Context.Entry(entity.Entity).OriginalValues.ToObject() is EntityBase org && org.Version != entity.Entity.Version) {
entity.Cancel = true;
// ReSharper disable once PossibleNullReferenceException
var entityName = entity.Entity.GetType().BaseType != null
&& entity.Entity.GetType().Name.StartsWith(entity.Entity.GetType().BaseType.Name)
// ReSharper disable once PossibleNullReferenceException
? entity.Entity.GetType().BaseType.Name
: entity.Entity.GetType().Name;
throw new DbUpdateConcurrencyException(
$"Validation failed for entitiy {entityName} with id {entity.Entity.Id}. Version in Database in different from version in Entity.");
}
entity.Entity.Updated = DateTime.UtcNow;
entity.Entity.Version += 1;
};
}
[Key]
//TODO: Generate from Sequence with MandantId!!
public long Id { get; set; }
public int Version { get; set; } = 1;
public DateTime Created { get; set; } = DateTime.UtcNow;
public DateTime Updated { get; set; } = DateTime.UtcNow;
}
}
Thx
Stefan
It appears that the original values cannot be retrieved using the proxy objects created by EF for lazy loading.
The solution appears to be mentioned here: #24
Might want to leave this one open so you don't get the same question again, just mark it as solved or something...
Googling on the error only points towards this post, until you only search for "OriginalValuesWrapperAssembly", then you'll find the old closed ones aswell.
After further research, i found that your TypedOriginalValues project found here which is where the problem is.
The below is my fixed entity:
namespace ADDER.VAT.Data.Models
{
public abstract class EntityBase
{
static EntityBase()
{
Triggers<EntityBase>.Inserting += OnInserting;
Triggers<EntityBase>.Updating += OnUpdating;
}
private static void OnUpdating(IUpdatingEntry<EntityBase, DbContext> e)
{
// These make sure that the value of CreatedAt and CreatedById stay the same
e.Entity.CreatedAt = (DateTime)e.Context.Entry(e.Entity).Property(nameof(CreatedAt)).OriginalValue;
e.Entity.CreatedById = (string)e.Context.Entry(e.Entity).Property(nameof(CreatedById)).OriginalValue;
// These update the UpdatedAt and UpdatedById to whoever is responsible for the current update
e.Entity.UpdatedAt = DateTime.Now;
e.Entity.UpdatedById = Thread.CurrentPrincipal.Identity.GetUserId();
}
private static void OnInserting(IInsertingEntry<EntityBase, DbContext> e)
{
e.Entity.CreatedAt = e.Entity.UpdatedAt = DateTime.Now;
e.Entity.CreatedById = e.Entity.UpdatedById = Thread.CurrentPrincipal.Identity.GetUserId();
}
[Key]
[Column("ID")]
public virtual int Id { get; set; }
public virtual DateTime CreatedAt { get; set; }
[Required]
public virtual string CreatedById { get; set; }
public virtual DateTime UpdatedAt { get; set; }
[Required]
public virtual string UpdatedById { get; set; }
// Navigation properties
[ForeignKey(nameof(CreatedById))]
public virtual ApplicationUser CreatedBy { get; set; }
[ForeignKey(nameof(UpdatedById))]
public virtual ApplicationUser UpdatedBy { get; set; }
}
}
Originally i had these 2 for lines 22/23:
e.Entity.CreatedAt = e.Original.CreatedAt;
e.Entity.CreatedById = e.Original.CreatedById;