0

我正在使用 EF5 并且在 POCO 类中有实体。我的第一个问题是实施业务规则和验证的最佳位置是什么?

我的第一个猜测是将它直接放入 POCO 类中的某个 Validate() 函数中,该函数在触发 SaveChanges() 时从 DBContext 调用。这很好用,但某些规则需要跨多个实体进行验证,例如 Invoice 类的示例:

if(this.Items.Where(i=>i.Price > 100).Count() > 0)
{
    //mark invoice for review
    this.IsForReview = true;
}

现在单元测试将测试 Validation 函数(针对每个业务规则),但还必须使用 Items 填充发票类(否则它将始终为空)

另一个想法是创建一个具有单独验证功能的 InvoiceValidation 类(甚至是每个规则的类?),这更容易进行单元测试,但它确实增加了要维护的文件/类的数量。

任何建议或现有解决方案的链接将不胜感激

4

3 回答 3

1

最好的方法将取决于您的依赖关系。POCO/核心组件是否依赖于 EF?您是否将 Access to DB 注入到您的核心库程序集中?等等

我个人使用 repository/luw 模式,其中各种存储库对象继承自通用的基本存储库对象。DAL 依赖于 EF,但核心中的 POCO 类不依赖。

存储库子类具有特定的类型,并执行 OTHER OBEJCT 业务检查。需要检查其他实体的 IE 业务规则,我在 DAL 中实现。

存储库类属于数据访问层项目,并且 DO 依赖于 EF 并注入了上下文。下面的例子。

特定于我在 POCO 上执行的实例的检查。我通过在基类存储库类上实现的接口执行需要数据库访问的检查,该存储库类又根据要求被覆盖。所以现在在添加更改对象时会触发CheckEntity调用。

例如...注意删除了一些代码以保持示例相关...

 public class RepositoryEntityBase<T> : IRepositoryEntityBase<T>, IRepositoryEF<T> where T : BaseObject
public virtual OperationStatus Add(T entity)
    {
        var opStatus = new OperationStatus(status: true, operation: OperationType.Add);
        try
        {
            if (OnBeforeAdd != null) // registered listeners of added event?
            {
                var evtArg = PrepareEventArgs(entity, MasterEventType.Create);
                OnBeforeAdd(this, evtArg);
            }
            opStatus = CheckBeforePersist(entity);
            if (opStatus.Status)
            {
                Initialize(entity);
                EntityDbSet.Add(entity);

                if (OnAfterAdd != null) // registered listeners of added event?
                {
                    var evtArg = PrepareEventArgs(entity, MasterEventType.Create);
                    OnAfterAdd(this, evtArg);
                }
            }
        }
        catch (Exception ex)
        {
            opStatus.SetFromException("Error Adding " + typeof(T), ex);
        }
        return opStatus;
    }

//... then in a specific repository class



//... irepositorybase expects Check before persist.

  public override OperationStatus CheckBeforePersist(MasterUser entity)
    {

        // base entity rule check first
        var opStatus = new OperationStatus(true, OperationType.Check);          
        opStatus.ValidationResults  = base.CheckEntity(entity);      
        if (opStatus.ValidationResults.Count > 0)
        {
            opStatus.Status = false;
            opStatus.Message = "Validation Errors";
            return opStatus;
        }


        //now check the local memory
        var masterUser = Context.Set<MasterUser>().Local   //in context
                                              .Where(mu => mu.Id != entity.Id // not this record
                                                    &&     mu.UserName == entity.UserName ) // same name
                                              .FirstOrDefault();
        if (masterUser != null)
        {
            opStatus.Status = false;
            opStatus.Message = "Duplicate UserName :" + masterUser.UserName + " UserId:"+ masterUser.Id.ToString();
            return opStatus;
        }
        masterUser = Context.Set<MasterUser>().Local   //in context
                                             .Where(mu => mu.Id != entity.Id // not this record
                                                   && mu.Email == entity.Email) // same email
                                             .FirstOrDefault();
        if (masterUser != null)
        {
            opStatus.Status = false;
            opStatus.Message = "Duplicate Email :" + masterUser.Email + " Username:" + masterUser.UserName;
            return opStatus;
        }                                               

        // now check DB
        masterUser = Get(mu => mu.Id != entity.Id             //not this record being checked
                          && mu.UserName == entity.UserName);     // has same username
        if (masterUser != null)
        {
            opStatus.Status = false;
            opStatus.Message = "Duplicate UserName :" + masterUser.UserName + " UserId:"+ masterUser.Id.ToString();
            return opStatus;
        }
        masterUser = Get(mu => mu.Id != entity.Id    // not this record
                      && mu.Email == entity.Email);  // but same email 
        if (masterUser != null)
        {
            opStatus.Status = false;
            opStatus.Message = "Duplicate Email:" + masterUser.Email + " UserName:"+ masterUser.UserName;
            return opStatus;
        }
        return opStatus;   
    }

}
于 2013-01-14T21:52:23.417 回答
0

我建议使用 Fluent Validation (http://fluentvalidation.codeplex.com/) 之类的方法,它允许您获取一组规则并将它们放在一个单独的上下文中,与它正在验证的 POCO 类分开。

于 2013-01-14T21:35:15.593 回答
0

如果有人感兴趣,这是我迄今为止发现的最好的例子:

http://codeinsanity.com/archive/2008/12/02/a-framework-for-validation-and-business-rules.aspx

于 2013-03-15T19:08:33.787 回答