简体   繁体   English

EF Core 中断开连接的实体

[英]Disconnected Entities in EF Core

I have been looking to log changes on entities in a disconnected scenario.我一直在寻找在断开连接的情况下记录实体的更改。

Original Problem: I call DbContext.Update(Entity) on an updated complex entity and even though nothing has changed, everything is marked as Changed by the ChangeTracker and my concurrency RowVersion column counts up.原始问题:我在更新的复杂实体上调用DbContext.Update(Entity) ,即使没有任何更改,所有内容都被 ChangeTracker 标记为已更改,并且我的并发RowVersion列计数。

POST User with no changes没有更改的 POST 用户

"userSubPermissions": [],
"fidAdresseNavigation": {
    "idAdresse": 1,
    "fidPlzOrt": 3,
    "strasse": "Gerold Str.",
    "hausnr": "45",
    "rowVersion": 10,
    "isDeleted": 0,
    "fidPlzOrtNavigation": {
        "idPlzOrt": 3,
        "plz": "52062",
        "ort": "Aachen",
        "rowVersion": 9,
        "isDeleted": 0
    }
},
"idUser": 35,
"fidAnrede": null,
"fidAdresse": 1,
"fidAspnetuser": "a7ab78be-859f-4735-acd1-f06cd832be7e",
"vorname": "Max",
"nachmname": "Leckermann",
"eMail": "kunde@leasing.de",
"rowVersion": 11,
"isDeleted": 0

POST Returned user POST 返回用户

"userSubPermissions": [],
"fidAdresseNavigation": {
    "idAdresse": 1,
    "fidPlzOrt": 3,
    "strasse": "GeroldPenis Str.",
    "hausnr": "45",
    "rowVersion": 11,
    "isDeleted": 0,
    "fidPlzOrtNavigation": {
        "idPlzOrt": 3,
        "plz": "52062",
        "ort": "Aachen",
        "rowVersion": 10,
        "isDeleted": 0
    }
},
"idUser": 35,
"fidAnrede": null,
"fidAdresse": 1,
"fidAspnetuser": "a7ab78be-859f-4735-acd1-f06cd832be7e",
"vorname": "Max",
"nachmname": "Leckermann",
"eMail": "kunde@leasing.de",
"rowVersion": 12,
"isDeleted": 0

The RowVersion logic is in the DBContext and only changes the row version when the Entity State is Modified. RowVersion 逻辑位于DBContext中,并且仅在修改实体状态时更改行版本。

foreach (var entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified))
        {
            var saveEntity = entity.Entity as ISavingChanges;
            saveEntity.OnSavingChanges();
        }

From my investigation over the past 2 days there seem to be two alternatives for EF Core.从我过去 2 天的调查来看,EF Core 似乎有两种选择。

1.- As mentioned on this and many similar posts you can use the TrackGraph https://www.mikesdotnetting.com/article/303/entity-framework-core-trackgraph-for-disconnected-data 1.- 正如这篇文章和许多类似文章中提到的,您可以使用 TrackGraph https://www.mikesdotnetting.com/article/303/entity-framework-core-trackgraph-for-disconnected-data

Problem: This solution marks the entire entity as changed when the ID of the entity is present even though nothing has changed.问题:当实体的 ID 存在时,此解决方案将整个实体标记为已更改,即使没有任何更改。 I get the exact same result as described above, the RowVersion counts up meaning my Entities an all of its Properties have been marked as Modified.我得到与上述完全相同的结果, RowVersion计数意味着我的实体及其所有属性都已标记为已修改。

2.- As mentioned in this post https://blog.tonysneed.com/2017/10/01/trackable-entities-for-ef-core/ you can download a NuGet package and implement the IMergeable and the ITrackable interfaces on my Entities. 2.- 如这篇文章https://blog.tonysneed.com/2017/10/01/trackable-entities-for-ef-core/中所述,您可以下载 NuGet 包并在我的上实现IMergeableITrackable接口实体。

Problem: The Client needs to track the changes in the Model and pass them to the API, which I want to avoid since Angular does not seem to offer a good solution for this.问题:客户端需要跟踪模型中的更改并将它们传递给 API,我想避免这种情况,因为 Angular 似乎没有为此提供好的解决方案。

3.- There is another solution in the book Programming Entity Framework on how EF 6 handles disconnected scenarios, by recording original values Chapter 4: Recording Original Values . 3.- 关于 EF 6 如何处理断开连接的场景,编程实体框架一书中还有另一种解决方案,即记录原始值第 4 章:记录原始值 When an Update comes in, the context reads the entity from the DB and then compares the incoming Entity with the DB Entity to see if any changes have been made on the properties and then marks them as Modified.当更新进来时,上下文从数据库中读取实体,然后将传入的实体与数据库实体进行比较,以查看是否对属性进行了任何更改,然后将它们标记为已修改。

https://www.oreilly.com/library/view/programming-entity-framework/9781449331825/ch04.html https://www.oreilly.com/library/view/programming-entity-framework/9781449331825/ch04.html

Problem: Some of the code described in the book is not implementable in EF Core one example is.问题:书中描述的一些代码无法在 EF Core 中实现,一个例子就是。

((IObjectContextAdapter)this).ObjectContext
.ObjectMaterialized += (sender, args) =>
{
  var entity = args.Entity as IObjectWithState;
  if (entity != null)
  {
    entity.State = State.Unchanged;

    entity.OriginalValues =
      BuildOriginalValues(this.Entry(entity).OriginalValues);
  }
};

There must be a way to implement this clever solution of handling changes on the API instead of the Client in Disconnected scenarios, otherwise, this might be a very useful Feature to have in EF Core since most Developers are using it for Disconnected scenarios.必须有一种方法来实现这种在断开连接场景中处理 API 而不是客户端更改的巧妙解决方案,否则,这可能是 EF Core 中非常有用的功能,因为大多数开发人员都将它用于断开连接的场景。

My response is using TrackGraph我的回应是使用TrackGraph

Synopsis概要

I want to Modify only few columns of the entity as well as Add\Modify nested child entities我只想修改实体的几列以及添加\修改嵌套的子实体

  • Here, I am updating Scenario entity and modifying only ScenarioDate .在这里,我正在更新Scenario实体并仅修改ScenarioDate
  • In its child entity, ie navigation property TempScenario , I am adding a new record在它的子实体中,即导航属性TempScenario ,我添加了一条新记录
  • In the nested child entity, Scenariostation , I am adding as well modifying the records在嵌套的子实体Scenariostation中,我正在添加以及修改记录
public partial class Scenario
{
    public Scenario()
    {
        InverseTempscenario = new HashSet<Scenario>();
        Scenariostation = new HashSet<Scenariostation>();
    }
    public int Scenarioid { get; set; }
    public string Scenarioname { get; set; }
    public DateTime? Scenariodate { get; set; }
    public int Streetlayerid { get; set; }
    public string Scenarionotes { get; set; }
    public int? Modifiedbyuserid { get; set; }
    public DateTime? Modifieddate { get; set; }
    public int? Tempscenarioid { get; set; }

    
    public virtual Scenario Tempscenario { get; set; }
    public virtual ICollection<Scenario> InverseTempscenario { get; set; }
    public virtual ICollection<Scenariostation> Scenariostation { get; set; }
}


public partial class Scenariostation
{
    public Scenariostation()
    {
        Scenariounit = new HashSet<Scenariounit>();
    }

    public int Scenariostationid { get; set; }
    public int Scenarioid { get; set; }
    public int Stationid { get; set; }
    public bool? Isapplicable { get; set; }
    public int? Createdbyuserid { get; set; }
    public int? Modifiedbyuserid { get; set; }
    public DateTime? Modifieddate { get; set; }
    
    public virtual Scenario Scenario { get; set; }
    public virtual Station Station { get; set; }
}

public partial class Station
{
    public Station()
    {
        Scenariostation = new HashSet<Scenariostation>();
    }

    public int Stationid { get; set; }
    public string Stationname { get; set; }
    public string Address { get; set; }
    public NpgsqlPoint? Stationlocation { get; set; }
    public int? Modifiedbyuserid { get; set; }
    public DateTime? Modifieddate { get; set; }

    public virtual ICollection<Scenariostation> Scenariostation { get; set; }
}
  • With EF Core, data update in a disconnected scenario is tricky if you don't want to make 2 database round trips.使用 EF Core,如果您不想进行 2 次数据库往返,则在断开连接的情况下更新数据会很棘手。

  • Even though 2 database trips seems not significant, it can hamper performance if the data table has millions of records.尽管 2 次数据库访问似乎并不重要,但如果数据表有数百万条记录,它可能会影响性能。

  • Also, if there are only few columns to be updated, including columns of nested child entities, Usual Approach will not work此外,如果只有几列要更新,包括嵌套子实体的列, Usual Approach将不起作用

Usual approach通常的做法

public virtual void Update(T entity)
{
    if (entity == null)
        throw new ArgumentNullException("entity");

    var returnEntity = _dbSet.Attach(entity);
    _context.Entry(entity).State = EntityState.Modified;
}

But the problem here for a disconnected EF Core update, if you use this DbContext.Entry(entity).EntityState = EntityState.IsModified , all the columns will updated.但是这里的问题是断开连接的 EF Core 更新,如果您使用此DbContext.Entry(entity).EntityState = EntityState.IsModified ,所有列都将更新。 and so some columns will be updated to its default value ie null or default data type value.因此某些列将更新为其默认值,即 null 或默认数据类型值。

Further more, some records of ScenarioStation won't be updated at all because the entity state will be UnChanged .此外, ScenarioStation的一些记录根本不会更新,因为实体状态将是UnChanged

So inorder to update only the columns which are sent from the client, somehow EF Core needs to be told .因此,为了仅更新从客户端发送的列需要以某种方式告知 EF Core

Using ChangeTracker.TrackGraph使用 ChangeTracker.TrackGraph

Recently I found this DbConetxt.ChangeTracker.TrackGraph method which can be used to mark Added , UnChanged state for the entities.最近我发现了这个DbConetxt.ChangeTracker.TrackGraph方法,它可以用来标记实体的AddedUnChanged状态。

difference is that with TrackGraph , you can add custom logic, as it iteratively navigates through Navigation properties of the entity.不同之处在于,使用TrackGraph时,您可以添加自定义逻辑,因为它会迭代地浏览实体的 Navigation 属性。

My custom logic using TrackGraph我使用 TrackGraph 的自定义逻辑

public virtual void UpdateThroughGraph(T entity, Dictionary<string, List<string>> columnsToBeUpdated)
{
    if (entity == null)
        throw new ArgumentNullException("entity");

    _context.ChangeTracker.TrackGraph(entity, e =>
    {
        string navigationPropertyName = e.Entry.Entity.GetType().Name;

        if (e.Entry.IsKeySet)
        {
            e.Entry.State = EntityState.Unchanged;
            e.Entry.Property("Modifieddate").CurrentValue = DateTime.Now;

            if (columnsToBeUpdated.ContainsKey(navigationPropertyName))
            {
                foreach (var property in e.Entry.Properties)
                {
                    if (columnsToBeUpdated[e.Entry.Entity.GetType().Name].Contains(property.Metadata.Name))
                    {
                        property.IsModified = true;
                    }
                }
            }
        }
        else
        {
            e.Entry.State = EntityState.Added;
        }

    });

}

With this approach, I am able to easily handle only required column updates as well as new additions/modifications for any of the nested child entities and its columns.通过这种方法,我能够轻松地只处理所需的列更新以及任何嵌套子实体及其列的新添加/修改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM