【问题标题】:Either get local entity or attach a new one获取本地实体或附加一个新实体
【发布时间】:2021-10-16 02:21:13
【问题描述】:

我的实体如下所示:

public class User
{
    public int Id {get; set;}
}

我不想每次获得特定用户时都查询数据库,而我知道该 ID 存在用户。似乎 Attach 适用于这种情况,但如果 DbContext 已经在本地存储了该特定用户的实体,它将引发异常。

举例说明我想做什么:

var user1 = ctx.GetLocalOrAttach(new User{Id = 1});
var user2 = ctx.GetLocalOrAttach(new User{Id = 2});
AddUserRelation(user1, user2);

有解决办法吗?如果不是,那么检查实体是否在本地存在的理想方法是什么。

【问题讨论】:

    标签: c# entity-framework-core


    【解决方案1】:

    我已经使用 EF 多年,但我从未使用过附加机制,它通常只会给你带来麻烦。
    如果我查看代码,我猜您想在 2 个用户记录之间创建关系,但您想通过不查询两个用户记录来优化性能。 (就我个人而言,我不关心获取用户对象会花费我 20 毫秒的开销,但我想这可能很重要)。

    EF 允许您在不加载外部实体的情况下使用外键创建记录。
    因此,您可以更改以下代码:

    var user1 = context.Users.Find(1);
    var user2 = context.Users.Find(2);
    var userRelation = new UserRelation();
    userRelation.FromUser = user1;
    userRelation.ToUser = user2;
    context.UserRelations.Add(userRelation);
    

    到:

    var userRelation = new UserRelation();
    userRelation.FromUserId = 1;
    userRelation.ToUserId = 2;
    context.UserRelations.Add(userRelation);
    

    请注意,在我的上一个代码示例中,我没有查询两个用户对象,但 EF 将使用 2 个有效外键创建 UserRelation 记录。

    【讨论】:

    • 我将大多数数据库逻辑打包到存储库中。一旦我处理两个或更多不同的实体,创建一个像 AddRelation(int fromId, int toId) 这样的函数会很糟糕。在调用方交换参数将导致一个很难找到 IMO 的错误。另外我忘了指出我想要一种有效的方法来更新关系中的单个属性,这与 Attach 配合得很好。
    【解决方案2】:

    您可以搜索DbSet<T>.Local 属性,但这样做效率低。

    IMO 更好的方法是使用我对 Delete loaded and unloaded objects by ID in EntityFrameworkCore 的回答中的 FindTracked 自定义扩展方法

    using Microsoft.EntityFrameworkCore.Internal;
    
    namespace Microsoft.EntityFrameworkCore
    {
        public static partial class CustomExtensions
        {
            public static TEntity FindTracked<TEntity>(this DbContext context, params object[] keyValues)
                where TEntity : class
            {
                var entityType = context.Model.FindEntityType(typeof(TEntity));
                var key = entityType.FindPrimaryKey();
                var stateManager = context.GetDependencies().StateManager;
                var entry = stateManager.TryGetEntry(key, keyValues);
                return entry?.Entity as TEntity;
            }
        }
    }
    

    类似于 EF Core Find 方法,但如果本地不存在实体,则不会从数据库中加载它。

    你的情况的用法是这样的:

    var user1 = ctx.FindTracked(1) ?? ctx.Attach(new User { Id = 1 }).Entity;
    var user2 = ctx.FindTracked(2) ?? ctx.Attach(new User { Id = 2 }).Entity;
    AddUserRelation(user1, user2);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-28
      • 1970-01-01
      • 2017-10-31
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      • 1970-01-01
      相关资源
      最近更新 更多