【问题标题】:When using a repository is it possible for a type to return a Func that the repository uses to test for existing entities?使用存储库时,类型是否可以返回存储库用于测试现有实体的 Func?
【发布时间】:2011-01-12 15:01:46
【问题描述】:

例如给定一个带有方法的工厂

public static T Save<T>(T item) where T : Base, new()
{
  /* item.Id == Guid.Empty therefore item is new */
  if (item.Id == Guid.Empty && repository.GetAll<T>(t => t.Name == item.Name))
  {
    throw new Exception("Name is not unique");
  }
}

如何创建Base(比如MustNotAlreadyExist)的属性,以便我可以将上面的方法更改为

public static T Save<T>(T item) where T : Base, new()
{
  /* item.Id == Guid.Empty therefore item is new */
  if (item.Id == Guid.Empty && repository.GetAll<T>(t.MustNotAlreadyExist))
  {
    throw new Exception("Name is not unique");
  }
}

public class Base
{
  ...
  public virtual Expression<Func<T, bool>> MustNotAlreadyExist()
  {
    return (b => b.Name == name); /* <- this clearly doesn't work */
  }
}

然后我如何覆盖 Account : Base 中的 MustNotAlreadyExist

public class Account : Base
{
  ...
  public override Expression<Func<T, bool>> MustNotAlreadyExist()
  {
    return (b => b.Name == name && b.AccountCode == accountCode); /* <- this doesn't work */
  }
  ...
}

【问题讨论】:

  • 我认为我可能有方法签名,尽管我正在努力解决它: public virtual Expression> MustNotAlreadyExists(T item) where T : Base { return (t => t.Name == item.Name); }
  • 在下面查看我自己的答案;使用泛型接口并为类型中的特定类型实现此接口

标签: c# generics lambda virtual


【解决方案1】:

试试这个:

public class Account : Base
{
  ...
  public override Expression<Func<T, bool>> MustNotAlreadyExist()
  {
    return (b => b.Name == name && b.AccountCode == accountCode).Any();
  }
  ...
}

如果任何记录与谓词匹配,Any() 方法将返回 true。可以争辩说,在保存之前检查记录的存在是存储库的责任之外的。

更新:
CodeProject 上有一篇很棒的文章描述了实体框架的通用存储库:

http://www.codeproject.com/KB/database/ImplRepositoryPatternEF.aspx

这可以应用于非实体框架数据上下文。这是一个摘录,它提供了一种非常灵活的方法,通过接受字段名称、值和键值来检查现有值。您可以将此应用于任何实体类型,并在尝试保存之前使用它来检查实体是否存在。

/// <summary>
    /// Check if value of specific field is already exist
    /// </summary>
    /// <typeparam name="E"></typeparam>
    /// <param name="fieldName">name of the Field</param>
    /// <param name="fieldValue">Field value</param>
    /// <param name="key">Primary key value</param>
    /// <returns>True or False</returns>
    public bool TrySameValueExist(string fieldName, object fieldValue, string key)
    {
        // First we define the parameter that we are going to use the clause. 
        var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
        MemberExpression leftExprFieldCheck = 
        MemberExpression.Property(xParam, fieldName);
        Expression rightExprFieldCheck = Expression.Constant(fieldValue);
        BinaryExpression binaryExprFieldCheck = 
        MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);

        MemberExpression leftExprKeyCheck = 
        MemberExpression.Property(xParam, this._KeyProperty);
        Expression rightExprKeyCheck = Expression.Constant(key);
        BinaryExpression binaryExprKeyCheck = 
        MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
        BinaryExpression finalBinaryExpr = 
        Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);

        //Create Lambda Expression for the selection 
        Expression<Func<E, bool>> lambdaExpr = 
        Expression.Lambda<Func<E, bool>>(finalBinaryExpr, 
        new ParameterExpression[] { xParam });
        //Searching ....            
        return ((IRepository<E, C>)this).TryEntity(new Specification<E>(lambdaExpr));
    }
    /// <summary>
    /// Check if Entities exist with Condition
    /// </summary>
    /// <param name="selectExpression">Selection Condition</param>
    /// <returns>True or False</returns>
    public bool TryEntity(ISpecification<E> selectSpec)
    {
        return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Any<E>
                    (selectSpec.EvalPredicate);
    }

【讨论】:

  • 这不会编译...我想从此方法返回一个表达式(或 Func),我可以将它传递给 GetAll().Any(...) 调用。
【解决方案2】:

我不确定您的问题是否可以解决,因为您需要访问存储库和要检查的新项目。要检查的新项目在单独的方法中不可用。

但是,您可以将调用外包给 GetAll,以便您的代码类似于 (未测试)

public static T Save<T>(T item) where T : Base, new()
{
  if (item.Id == Guid.Empty && (Check(repository, item)))
  {
    throw new Exception("Name is not unique");
  }
}

public class Base
{
  ...
  public Func<Enumerable<T>, T, bool> Check { get; set;}

  public Base()
  {
    Check = (col, newItem) => (null != col.FirstOrDefault<T>(
                                       item => item.Name == newItem.Name));
  }
}

【讨论】:

  • 这看起来像是有一些腿......我将报告我实现这种模式的尝试。谢谢。
【解决方案3】:

好的,这就是答案,这是 Dave Swersky 发布的代码和一些常识的组合。

public interface IUniqueable<T>
{
  Expression<Func<T, bool>> Unique { get; }
}

public class Base, IUniqueable<Base>
{
  ...
  public Expression<Func<Base, bool>> Unique
  {
    get
    {
      var xParam = Expression.Parameter(typeof(Base), typeof(Base).Name);
      MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "Name");
      Expression rightExprFieldCheck = Expression.Constant(this.Name);
      BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
      return Expression.Lambda<Func<Base, bool>>(binaryExprFieldCheck, new ParameterExpression[] { xParam });
    }
  }
  ...
}

public class Account : Base, IUniqueable<Account>
{
  ...
  public new Expression<Func<Account, bool>> Unique
  {
    get
    {
      var xParam = Expression.Parameter(typeof(Account), typeof(Account).Name);
      MemberExpression leftExprNameCheck = MemberExpression.Property(xParam, "Name");
      Expression rightExprNameCheck = Expression.Constant(this.Name);
      BinaryExpression binaryExprNameCheck = MemberExpression.Equal(leftExprNameCheck, rightExprNameCheck);

      MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "AccountCode");
      Expression rightExprFieldCheck = Expression.Constant(this.AccountCode);
      BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);

      BinaryExpression binaryExprAllCheck = Expression.OrElse(binaryExprNameCheck, binaryExprFieldCheck);

      return Expression.Lambda<Func<Account, bool>>(binaryExprAllCheck, new ParameterExpression[] { xParam });
    }
  }
  ...
}

public static class Manager
{
  ...
  public static T Save<T>(T item) where T : Base, new()
  {
    if (!item.IsValid)
    {
      throw new ValidationException("Unable to save item, item is not valid", item.GetRuleViolations());
    }

    if (item.Id == Guid.Empty && repository.GetAll<T>().Any(((IUniqueable<T>)item).Unique))
    {
      throw new Exception("Item is not unique");
    }

    return repository.Save<T>(item);
  }
  ...
}

基本上通过为特定类型实现IUniqueable 接口,我可以为每种类型返回不同的Expression。一切都好:-)

【讨论】:

    猜你喜欢
    • 2020-06-24
    • 1970-01-01
    • 2017-08-26
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    • 2020-01-17
    相关资源
    最近更新 更多