实体框架通过主键获取实体 [英] Entity Framework Get Entity By Primary Key

查看:144
本文介绍了实体框架通过主键获取实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前段时间,我想实现一种方法,该方法能够确定是对给定实体执行插入还是更新操作,因此我不必公开"Insert"和"Update"方法,而只需要公开一个"Insert"和"Update"方法即可.简单的"InsertOrUpdate".

Some time ago, I wanted to implement a method that was able to determine whether do an insert or an update on a given entity, so I didn't have to expose "Insert" and "Update" methods, but just a simple "InsertOrUpdate".

找出实体是否为新的代码部分是:

The part of code that finds out if the entity is new or not, is this:

    public virtual T GetEntityByPrimaryKey<T>(T entity) where T : class
    {
        var entityType = entity.GetType();
        var objectSet = ((IObjectContextAdapter)this.DatabaseContext).ObjectContext.CreateObjectSet<T>();
        var keyNames = objectSet.EntitySet.ElementType.KeyMembers.Select(edmMember => edmMember.Name);
        var keyValues = keyNames.Select(name => entityType.GetProperty(name).GetValue(entity, null)).ToArray();

        return this.DatabaseContext.Set<T>().Find(keyValues);
    }

InsertOrUpdate方法是这样的:

And the InsertOrUpdate method is this:

    public virtual T InsertOrUpdate<T>(T entity) where T : class
    {
        var databaseEntity = this.GetEntityByPrimaryKey(entity);

        if (databaseEntity == null)
        {
            var entry = this.DatabaseContext.Entry(entity);

            entry.State = EntityState.Added;

            databaseEntity = entry.Entity;
        }
        else
        {
            this.DatabaseContext.Entry(databaseEntity).CurrentValues.SetValues(entity);
        }

        return databaseEntity;
    }

现在,只要对象的主键"由代码确定,此方法就能产生奇迹.有效示例包括GUID,HI-LO算法,自然键等.

Now, this approach works wonders as long as the "primary key" of the object is determined by the code. Valid examples are GUIDs, HI-LO algorithms, natural keys, etc.

但是,这对于数据库生成的身份"方案来说是可怕的,原因很简单:由于代码中的"Id"对于我要插入的所有对象均为0,因此该方法会认为它们相同.如果我要添加10个对象,则第一个对象的结果将为新",但接下来的9个对象的结果将为已存在".这是由于EF的查找"方法从对象上下文读取数据的事实,并且只有在不存在的情况下,它才进入数据库进行查询.

This, however, is horribly broken for the "database generated identity" scenario, and the reason is quite simple: since my "Id" in the code will be 0 for all the objects I'm going to insert, the method will consider them the same. If I'm adding 10 objects, the first will result "new", but the next nine will result "already existing". This is due to the fact that the "Find" method of EF reads data from the objectcontext and only if it's not present it goes down to the database to make the query.

在第一个对象之后,将跟踪ID为0的给定类型的实体.连续调用将导致更新",这是错误的.

After the first object, an entity of the given type with Id 0 will be tracked. Successive calls will result in an "update", and this is just wrong.

现在,我知道数据库生成的ID是有害的,并且绝对不适合任何ORM,但是我坚持使用这些ID,我需要修复此方法或将其完全删除,然后回退以分开使用插入"和更新"方法,并将任务委托给调用方以确定该做什么.由于我们有一个高度分离的解决方案,所以我宁愿避免这样做.

Now, I know that database generated ids are evil and absolutely not good for any ORM, but I'm stuck with those and I need to either fix this method or remove it entirely and fall back to separate "Insert" and "Update" methods and delegate to the caller the task to determine what to do. Since we have a highly decoupled solution I'd rather avoid to do this.

如果任何人都可以帮助并找到修复GetEntityByPrimaryKey方法的方法,那就太好了.

If anyone can help and find a way to fix the GetEntityByPrimaryKey method, that would be awesome.

谢谢.

推荐答案

我有以下建议:

1:
我会将类似IsTransient的属性添加到实体.如果PK为0,则返回true;否则,返回false.
您可以使用此属性来更改您的方法,如下所示:

1:
I would add something like a IsTransient property to the entities. It returns true if the PK is 0, otherwise it returns false.
You could use this property to change your method as follows:

  1. IsTransient ==是吗? ->插入
  2. IsTransient ==否? ->使用数据库检查您现有的代码
  1. IsTransient == true? -> Insert
  2. IsTransient == false? -> Your existing code with the database check

将该属性设为虚拟,甚至可以通过覆盖IsTransient来支持具有奇怪" PK的实体.

Make that property virtual and you will even be able to support entities with "strange" PK by overriding IsTransient.

2:
如果您不喜欢将其添加到您的实体中,则仍然可以创建一个封装此逻辑的扩展方法.甚至直接将该支票添加到您的InsertOrUpdate中.

2:
If you don't like adding this to your entities, you still could create an extension method that encapsulates this logic. Or even add that check directly into your InsertOrUpdate.

由于您的实体没有通用的基类,因此这些建议将变得有些乏味.基本上,每个实体必须具有一个扩展方法.

As you don't have a common base class for your entities those suggestions will become a bit tedious. You basically would have to have one extension method per entity.

3:
如果您有PK的约定,则可以使用dynamic访问ID属性:

3:
If you have a convention in place for the PK, you could use dynamic to access the ID property:

dynamic dynamicEntity = entity;
if(dynamicEntity.Id == 0)
{
    // Insert
}
else
{
    // Current code.
}

4 :
看到将临时实体添加到上下文会破坏所有后续临时项目的内容,因此最好将临时项目添加到列表中而不是上下文中.
仅在要提交时将它们添加到上下文中.我敢肯定有一个钩子可以使用:

4:
Seeing that adding a transient entity to the context breaks things for all following transient items, it might be a good idea to add the transient items to a list instead of the context.
Only add them to the context when it is going to be committed. I am sure there is a hook for this, which you can use:

List<object> _newEntities;

private override OnCommit()
{
    foreach(var newEntity in newEntities)
        DatabaseContext.Entry(newEntity).State = EntityState.Added;
}

public virtual T InsertOrUpdate<T>(T entity) where T : class
{
    var databaseEntity = this.GetEntityByPrimaryKey(entity);

    if (databaseEntity == null)
        _newEntities.Add(entity);
    else
        this.DatabaseContext.Entry(databaseEntity).CurrentValues.SetValues(entity);

    return databaseEntity;
}

这篇关于实体框架通过主键获取实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆