摘要
我有一个类,它应该跟踪一个属性的值是否被用户更改了,记住它的原始值以允许重置或提交它。因此,我编写了一个类Historian<T>,它用作包装器,并将在应用程序的业务模型中使用。但是,当实体框架核心()从数据库(DB)获取数据时,我的方法将导致指示编辑后的值,并存储了一个不正确的原始值,即default(T)。
关于这个问题的细节
这是用来记住属性的原始值和当前值的包装器:
public class Historian<T>
{
public T OriginalValue { get; private set; }
public T CurrentValue { get; set; }
public bool IsEdited => object.Equals(OriginalValue, CurrentValue); // avoids NullPointerExceptions
// some events ValueChanged, EditsDiscarded, EditsCommitted
public void CommitChanges()
=> OriginalValue = CurrentValue;
public void DiscardChanges()
=> CurrentValue = OriginalValue;
}为了说明起见,让我们继续使用经典的库示例。这是我的Book课。属性Title应该记住它从通过EFC访问的DB中的原始值,并允许在内部或从其他类重置它。这样做的想法是,用户在编辑应用程序中的文本框时将显示原始值,并允许重置各个字段。
public class Book
{
private Historian<string> _Title = new Historian<string>();
public int BookID { get; set; }
public string Title
{
get => _Title.CurrentValue;
set => _Title.CurrentValue = value;
}
public string Description { get; set; }
public bool IsPopularBook { get; set; }
// other methods, properties, ..., this is an example
}现在的问题是,当EFC从数据库获取数据时,它使用默认的构造函数,将Book.Title初始化为string.Empty (同时设置Historian.OriginalValue和Historian.CurrentValue),然后在构造函数完成后,将Book.Title设置为一个值,比如"Markdown -a Pocket Guide“。然而,这使得应用程序觉得值被更改了,尽管它没有被更改。
是否有办法区分对象是由EFC构造的,还是由应用程序的其他部分构造的?我能否拦截由EFC调用的构造函数来正确初始化我的Historian.OriginalValue?或者,在初始化对象以设置Historian.OriginalValue之后,我可以告诉EFC调用一个方法,例如调用Historian.CommitChanges() (虽然语义错误,但我认为您已经知道了)。
非解决方案-(失败)尝试
添加带有属性参数的构造函数的工作方式是EFC然后调用此构造函数,即
public Book(string title)
{
Title = title;
_Title.CommitChanges();
}但是,一旦有了无参数构造函数(我在应用程序中的位置需要它),EFC就会忽略带有参数的构造函数。
我尝试过更改属性的getter和setter,它也是工作的。但是,现在我不能轻易地订阅Historian 以前的null事件。
private Historian<string> _Title ;
public string Title
{
get
{
if (_Title == null)
_Title = new Historian<string>();
return _Title.CurrentValue;
}
set
{
if (_Title == null)
{
_Title = new Historian<string>();
_Title.CurrentValue = value;
_Title.CommitChanges();
}
else
_Title.CurrentValue = value;
}
}然后,我的想法是有一个固定的Historian实例,在那里我可以订阅事件并添加初始化模式,例如通过一个bool FirstSetterInitialises。但是在这里,我无法区分我是从EFC初始化还是从其他初始化,所以Book的新实例也将表明它们不是为第一次编辑而编辑的,即使它们是编辑的。
发布于 2022-01-21 14:24:45
您可以通过实体框架更改跟踪来完成此操作。考虑下面的史学家类,它已经被重构以使用实体框架更改跟踪。对这种方法的一个警告是,历史学家必须通过引用创建他们的EF上下文来实例化,因此这是您必须从实体框架之上的层调用的额外步骤。这种方法的一个好处是它不需要任何额外的开销来执行更改跟踪- EF无论如何都会执行这项工作(除非您使用.AsNoTracking进行查询)。
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public Historian<Role, string> NameHistorian;
/// <summary>
/// call this function after an EF context has returned this entity to build the historian
/// </summary>
/// <param name="context">the Entity Framework context that returned this entity</param>
public void ConfigureHistorians(DbContext context)
{
NameHistorian = new Historian<Role, string>(context, this, entity => entity.Name);
}
}
/// <summary>
/// a utility to view whether an property on a tracked entity has changed as well as the previous value
/// </summary>
/// <typeparam name="EntityType">the entity model class</typeparam>
/// <typeparam name="DataType">the type of the property to be tracked</typeparam>
public class Historian<EntityType, DataType> where EntityType : class where DataType : IEquatable<DataType>
{
private Microsoft.EntityFrameworkCore.ChangeTracking.PropertyEntry<EntityType, DataType> efEntry;
/// <summary>
/// create a historian for a specific property on a given entity
/// </summary>
/// <param name="context">the Entity Framework context from which the entity was retrieved</param>
/// <param name="entity">the instance of the entity this historian is tracking</param>
/// <param name="propertySelector">the property of the entity this historian is tracking</param>
public Historian(DbContext context, EntityType entity,
System.Linq.Expressions.Expression<Func<EntityType, DataType>> propertySelector)
{
efEntry = context.Entry<EntityType>(entity).Property<DataType>(propertySelector);
}
public DataType OriginalValue => efEntry.OriginalValue;
public DataType CurrentValue => efEntry.CurrentValue;
public bool IsEdited => !CurrentValue.Equals(OriginalValue);
public void DiscardChanges()
=> efEntry.CurrentValue = efEntry.OriginalValue;
}https://stackoverflow.com/questions/70801659
复制相似问题