在实体框架中保存实体的通用方法 [英] A generic way to save an entity in Entity Framework

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

问题描述

我正在尝试编写一个GenericEFRepository,供其他存储库使用。我有一个Save方法,如下所示。

I am trying to write a GenericEFRepository which will be used by other Repositories. I have a Save method as below.

public virtual void Save(T entity) // where T : class, IEntity, new() And IEntity enforces long Id { get; set; }
{
    var entry = _dbContext.Entry(entity);

    if (entry.State != EntityState.Detached)
        return; // context already knows about entity, don't do anything

    if (entity.Id < 1)
    {
        _dbSet.Add(entity);
        return;
    }

    var attachedEntity = _dbSet.Local.SingleOrDefault(e => e.Id == entity.Id);
    if (attachedEntity != null)
        _dbContext.Entry(attachedEntity).State = EntityState.Detached;
    entry.State = EntityState.Modified;
}

您可以在以下代码的注释中找到问题

You can find the problem in comments of below code

 using (var uow = ObjectFactory.GetInstance<IUnitOfWork>()) // uow is implemented like EFUnitOfWork which gives the DbContext instance to repositories in GetRepository
 {
    var userRepo = uow.GetRepository<IUserRepository>();

    var user = userRepo.Get(1);
    user.Name += " Updated";

    userRepo.Save(user);
    uow.Save(); // OK only the Name of User is Updated 
 }

 using (var uow = ObjectFactory.GetInstance<IUnitOfWork>())
 {
    var userRepo = uow.GetRepository<IUserRepository>();

    var user = new User 
    {
        Id = 1,
        Name = "Brand New Name"
    };

    userRepo.Save(user);
    uow.Save();

    // NOT OK
    // All fields (Name, Surname, BirthDate etc.) in User are updated
    // which causes unassigned fields to be cleared on db
 }

我能想到的唯一解决方案是通过 userRepo.CreateEntity(id:1),存储库将返回一个附加到DbContext的实体。但这似乎容易出错,仍然任何开发人员都可以使用 new 关键字创建实体。

The only solution I can think of is creating Entities via repository like userRepo.CreateEntity(id: 1) and repository will return an Entity which is attached to DbContext. But this seems error prone, still any developer may create an entity using new keyword.

您的解决方案建议是什么

What are your solution suggestions about this particular problem?

注意:我已经知道使用GenericRepository和IEntity接口的利弊。因此,不要使用GenericRepository,不要使用IEntity,不要在每个实体中添加长ID,不要执行您要执行的操作。

Note: I already know about cons and pros of using a GenericRepository and an IEntity interface. So, "Don't use a GenericRepository, don't use an IEntity, don't put a long Id in every Entity, don't do what you are trying to do" comments will not help.

推荐答案

是的,它容易出错,但简单来说就是EF和存储库的问题。您必须先创建实体并附加它,然后再设置要更新的任何数据(本例中为 Name ),或者必须为每个要保留的属性设置修改状态

Yes it is error prone but simply that is the problem with EF and repositories. You must either create entity and attach it before you set any data you want to update (Name in your case) or you must set modified state for each property you want to persist instead of whole entity (as you can imagine again developer can forget to do that).

第一个解决方案导致存储库中的特殊方法执行以下操作:

The first solution leads to special method on your repository doing just this:

public T Create(long id) {
    T entity = _dbContext.Set<T>().Create();
    entity.Id = id;
    _dbContext.Set<T>().Attach(entity);
    return entity;
}

第二个解决方案需要类似

The second solution needs something like

public void Save(T entity, params Expression<Func<T, TProperty>>[] properties) {

    ...

    _dbContext.Set<T>().Attach(entity);
    if (properties.Length > 0) {
        foreach (var propertyAccessor in properties) {
            _dbContext.Entry(entity).Property(propertyAccessor).IsModified = true;
        }
    } else {
        _dbContext.Entry(entity).State = EntityState.Modified;
    }
}

,您将这样称呼它:

userRepository(user, u => u.Name);

这篇关于在实体框架中保存实体的通用方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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