简体   繁体   English

EF6 - 有条件地更新SaveChanges()上的Entity属性

[英]EF6 - Conditionally updating an Entity property on SaveChanges()

I am using EF6 code-first (from existing db) to do CRUD operations. 我使用EF6代码优先(从现有数据库)进行CRUD操作。 I am going to write some mock code here to explain since I can't paste the real code due to some copy right issues. 我将在这里写一些模拟代码来解释,因为由于一些版权问题我无法粘贴真实代码。 Here is my entity: 这是我的实体:

    public partial class Person : EntityBase
{
    public long Id { get; set; } // ID (Primary key)
    public string LastName { get; set; } // LastName (length: 50)
    public string FirstName { get; set; } // FirstName (length: 50)
    public string SocialSecurity { get; set; } // SocialSecurity (length: 50)
    public long CreatedById { get; set; } // CreatedByID
    public System.DateTime CreatedDate { get; set; } // CreatedDate
    public long UpdatedById { get; set; } // UpdatedByID
    public System.DateTime UpdatedDate { get; set; } // UpdatedDate
    public byte[] TimeStamped { get; set; } // TimeStamped (length: 8)
}

I have a generic repository to do the CRUD operations. 我有一个通用的存储库来执行CRUD操作。

    public class Repository<T> : IRepositoryAsync<T> where T : class, IObjectState
{
    private readonly IDataContextAsync context;
    private readonly DbSet<T> dbSet;
    //private read-only IUnitOfWorkAsync uow;

    public Repository( IDataContextAsync _context )
    {
        context = _context;
        dbSet = ( (DbContext)context ).Set<T>();
    }

    public virtual IDataContextAsync Context { get { return context; } }
    public IDbSet<T> DbSet { get { return dbSet; } }
    public T FindOne( Expression<Func<T, bool>> predicate )
    {
        return dbSet.SingleOrDefault( predicate );
    }
    public IQueryable<T> FindBy( Expression<Func<T, bool>> predicate )
    {
        return dbSet.Where( predicate );
    }
    public void Add( T entity )
    {
        //entity.ObjectStateEnum = ObjectStateEnum.Added;
        dbSet.Add( entity );
    }
    public void Delete( dynamic id )
    {
        var entity = dbSet.Find(id);
        Delete( entity );
    }
    public void Update( T entity )
    {
        //entity.ObjectStateEnum = ObjectStateEnum.Modified;
        dbSet.Add( entity );
    }
}

and here is my UnitOfWork implementation. 这是我的UnitOfWork实现。 Again, the code fragments are just fragments. 同样,代码片段只是片段。 Not complete code. 不完整的代码。

    public class UnitOfWork : IUnitOfWorkAsync
{
    private IDataContextAsync context;
    private IOperationStatus opStatus;
    private Dictionary<string, dynamic> repositories;
    private ObjectContext objectContext;
    private DbTransaction transaction;
    private bool disposed;

    public IDataContextAsync DataContext { get { return context; } }

    //public UnitOfWork()
    //{
    //  context = new RedStoneDbContext(); //???? _context;
    //  opStatus = new OperationStatus(); //???? _opStatus;
    //  repositories = new Dictionary<string, dynamic>();
    //}
    public UnitOfWork( IDataContextAsync _context, IOperationStatus _opStatus )
    {
        context = _context;
        opStatus = _opStatus;
        repositories = new Dictionary<string, dynamic>();
    }

    public IOperationStatus SaveChanges()
    {
        opStatus.Success = false;
        try
        {
            int numRec = context.SaveChanges();
            opStatus.Success = true;
            opStatus.RecordsAffected = numRec;
        }
        catch ( SystemException ex )
        {
            opStatus = opStatus.CreateFromException( ex );
        }
        return opStatus;
    }
    public void Dispose()
    {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    public virtual void Dispose( bool disposing )
    {
        if ( disposed )
            return;

        if ( disposing )
        {
            try
            {
                if ( objectContext != null && objectContext.Connection.State == ConnectionState.Open )
                    objectContext.Connection.Close();
            }
            catch ( ObjectDisposedException )
            {
                // do nothing
            }
            if ( context != null )
            {
                context.Dispose();
                context = null;
            }
        }
        disposed = true;
    }
    public IRepository<T> Repository<T>() where T : class, IObjectState
    {
        return RepositoryAsync<T>();
    }

    public async Task<IOperationStatus> SaveChangesAsync()
    {
        opStatus.Success = false;
        try
        {
            int numRec = await context.SaveChangesAsync();
            opStatus.Success = true;
            opStatus.Message = "Record successfully saved!";
            opStatus.RecordsAffected = numRec;
        }
        catch ( DbUpdateConcurrencyException ex )
        {
            opStatus = opStatus.CreateFromException( ex );
        }
        catch ( SystemException ex )
        {
            opStatus = opStatus.CreateFromException( ex );
        }
        return opStatus;
    }
}

so, here is my question. 所以,这是我的问题。 When savechanges is called, I want to see if the entity contains a column "SocialSecurity" and if there is, I look at the user's role and permissions. 当调用savechanges时,我想查看实体是否包含“SocialSecurity”列,如果有,我会查看用户的角色和权限。 If everything is OK, want to let incoming SSN to be persisted in the DB. 如果一切正常,想让传入的SSN持久存储在数据库中。 If not, I just want SaveChanges to simply ignore SocialSecurity property but update everything else as usual. 如果没有,我只想让SaveChanges忽略SocialSecurity属性,但像往常一样更新其他所有内容。 I can't figure out an easy and efficient way to do this. 我无法找到一种简单有效的方法来做到这一点。 Any help is highly appreciated. 任何帮助都非常感谢。

Babu. 巴布。

You have all of the logical reasoning for the required actions here, it is just a matter of where you put the logic itself - and what you need to do. 在这里,您拥有所需操作的所有逻辑推理,这只是您放置逻辑本身的位置 - 以及您需要执行的操作。

You should have some sort of Service for each type that client code calls to retrieve data, be it related, filtered etc. Direct calls to the IQueryable of the database via the Repository should not be allowed - I can't tell from your code if you are implementing them or not. 您应该为客户端代码调用以检索数据的每种类型提供某种服务,无论是相关的,过滤的等等。不应该允许通过存储库直接调用数据库的IQueryable - 我无法从您的代码中看出你是否正在实施它们。

If you have these Services, then you could directly check for permissions in any that have entites with SocialSecurity. 如果您有这些服务,那么您可以直接检查任何与SocialSecurity有关的权限。 Alternatively, you could create an Interface that defines SocialSecurity, and mark all entities with it appropriately. 或者,您可以创建一个定义SocialSecurity的接口,并适当地标记所有实体。 Either way, you can perform the role check when applicable. 无论哪种方式,您都可以在适用时执行角色检查。

If the role check fails, you will need to load the current entity from storage and either: 如果角色检查失败,则需要从存储中加载当前实体,并且:

Replace the SocialSecurity property on the currently held entity with the one loaded from storage. 将当前保留的实体上的SocialSecurity属性替换为从存储中加载的属性。

Or 要么

Update all relevant properties in the newly loaded entity (except the SocialSecurity value) 更新新加载的实体中的所有相关属性(SocialSecurity值除外)

Now you can perform the Update(entity). 现在您可以执行更新(实体)。

There is no short way to do this, there is logic behind the Update operation and it needs to save directly (if permission is granted), or save while ensuring the current (from storage) SocialSecurity value is used instead of any changed one. 没有简短的方法可以做到这一点,Update操作背后有逻辑,它需要直接保存(如果授予了权限),或者在确保当前(来自存储)使用SocialSecurity值而不是任何更改的值时保存。

However, thinking about it, you could invert this, and not allow the user to edit the SocialSecurity value at all if they don't have permission when the page is loaded. 但是,考虑到这一点,您可以将其反转,如果在加载页面时没有权限,则根本不允许用户编辑SocialSecurity值。 It is usually bad practice to display data to, or give the impression of editing to, a user that does not have the permission to do so. 向没有权限的用户显示数据或给予编辑印象通常是不好的做法。 This may be the better way to handle the problem. 这可能是处理问题的更好方法。

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

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