首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在实体框架核心初始化C#对象之后立即调用方法

在实体框架核心初始化C#对象之后立即调用方法
EN

Stack Overflow用户
提问于 2022-01-21 12:57:54
回答 1查看 132关注 0票数 1

摘要

我有一个类,它应该跟踪一个属性的值是否被用户更改了记住它的原始值以允许重置或提交它。因此,我编写了一个类Historian<T>,它用作包装器,并将在应用程序的业务模型中使用。但是,当实体框架核心()从数据库(DB)获取数据时,我的方法将导致指示编辑后的值,并存储了一个不正确的原始值,即default(T)

关于这个问题的细节

这是用来记住属性的原始值和当前值的包装器:

代码语言:javascript
复制
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中的原始值,并允许在内部或从其他类重置它。这样做的想法是,用户在编辑应用程序中的文本框时将显示原始值,并允许重置各个字段。

代码语言:javascript
复制
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.OriginalValueHistorian.CurrentValue),然后在构造函数完成后,将Book.Title设置为一个值,比如"Markdown -a Pocket Guide“。然而,这使得应用程序觉得值被更改了,尽管它没有被更改。

是否有办法区分对象是由EFC构造的,还是由应用程序的其他部分构造的?我能否拦截由EFC调用的构造函数来正确初始化我的Historian.OriginalValue?或者,在初始化对象以设置Historian.OriginalValue之后,我可以告诉EFC调用一个方法,例如调用Historian.CommitChanges() (虽然语义错误,但我认为您已经知道了)。

非解决方案-(失败)尝试

添加带有属性参数的构造函数的工作方式是EFC然后调用此构造函数,即

代码语言:javascript
复制
public Book(string title)
{
    Title = title;
    _Title.CommitChanges();
}

但是,一旦有了无参数构造函数(我在应用程序中的位置需要它),EFC就会忽略带有参数的构造函数。

我尝试过更改属性的getter和setter,它也是工作的。但是,现在我不能轻易地订阅Historian 以前的null事件。

代码语言:javascript
复制
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的新实例也将表明它们不是为第一次编辑而编辑的,即使它们是编辑的。

EN

回答 1

Stack Overflow用户

发布于 2022-01-21 14:24:45

您可以通过实体框架更改跟踪来完成此操作。考虑下面的史学家类,它已经被重构以使用实体框架更改跟踪。对这种方法的一个警告是,历史学家必须通过引用创建他们的EF上下文来实例化,因此这是您必须从实体框架之上的层调用的额外步骤。这种方法的一个好处是它不需要任何额外的开销来执行更改跟踪- EF无论如何都会执行这项工作(除非您使用.AsNoTracking进行查询)。

代码语言:javascript
复制
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;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70801659

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档