使用LINQ使搜索方法通用

时间:2011-05-04 07:33:21

标签: c# linq generics

我的项目中有一个方法一遍又一遍地重复:

public PAC PAC_GetByCodiPac(string codiPac)

{

var sel = _gam.PAC.Where(pac => pac.CODI_PAC == codiPac);

            if (sel.Count() > 0)
                return sel.First();
            return null;
        }

表PAC表示(患者),所以我对所有表都有这些方法。 我怎样才能为此制作通用方法? 提前谢谢。

5 个答案:

答案 0 :(得分:2)

这是您的通用方法。注意,正如其他人指出的那样,FirstOrDefault比count更好,然后是第一个,所以我在这里使用它。但是也可以编写表达式,使其模仿原始代码的作用。如果您需要其他帮助,请与我们联系。

public static T GetByCodi<T>(IQueryable<T> table, string codi, string fieldName) where T : class
{
    // x
    ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
    Expression currentExpression = parameter;
    Type currentType = typeof(T);
    PropertyInfo property = currentType.GetProperty(fieldName);

    // x.CODI_xxx
    currentExpression = Expression.Property(currentExpression, property);

    // x.CODI_xxx == codi
    currentExpression = Expression.Equal(currentExpression, Expression.Constant(codi));

    // x => x.CODI_xxx == codi
    LambdaExpression lambdaExpression = Expression.Lambda(currentExpression, parameter);

    return table.FirstOrDefault((Func<T, bool>)lambdaExpression.Compile());
}

你这样使用它:

PAC xxx = GetByCodi<PAC>(_gam.PAC, codiPac, "CODI_PAC");

修改1 : 我根据注释更改了代码,以便您可以传递任意ID字段名称。

答案 1 :(得分:1)

我看到你问的是一个非常简单的where查询,即使不需要在单独的方法上使用它。 您还可以简单地增强以下查询链接:

public PAC PAC_GetByCodiPac(string codiPac)
{
   return _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);
}

FirstOrDefault将返回数组中的第一项,否则返回null。

答案 2 :(得分:0)

我不确定你是否要求这个,但是这个方法可以是静态类和方法,所以你可以从任何地方调用它。

答案 3 :(得分:0)

一个简单的解决方案是:

//a generic method
private PAC PAC_GetPAC(Func<PAC, bool> predicate)
{
   return _gam.PAC.Where(predicate).FirstOrDefault();
}

public PAC PAC_GetPACById(long id)
{
   return PAC_GetPAC(p => p.ID == id);
}

public PAC PAC_GetByCodiPac(string codiPac)
{
   return PAC_GetPAC(p => pac.CODI_PAC == codiPac);
}

答案 4 :(得分:0)

如果你想要一个允许你为那个表中的记录指定任何表和任何谓词的泛型方法,那么你实际上不可能比内置的Where<T>(...)更好(并且正如其他人已经指出的那样) )FirstOrDefault<T>(...)扩展方法。

您的代码将如此:

var result = _gam.PAC.Where(pac => pac.CODI_PAC == codiPac).FirstOrDefault();
// OR
var result = _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);

你可以得到的最好的,编写自己的通用方法,就是这样:

public T FirstOrDefault<T>(IQueryable<T> source,
    Expression<Func<T, bool>> predicate)
{
    return source.Where(predicate).FirstOrDefault();
    // OR
    // return source.FirstOrDefault(predicate);
}

这真的是多余的。特别是当您的调用代码使用辅助方法实际上更长时间时:

var result = FirstOrDefault(_gam.PAC, pac => pac.CODI_PAC == codiPac);
// versus
var result = _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);

更糟糕的是,您的代码不再使用流畅的可组合语法。这只会使可读性和维护变得更加困难。

如果您坚持使用IQueryable<T>扩展方法,那么您可以执行以下组合:

var result = _gam.PAC
        .Where(pac => pac.CODI_PAC == codiPac)
        .Where(pac => pac.SomeOtherProperty == someOtherValue)
        .FirstOrDefault();
// OR

var result = (from pac in _gam.PAC
              where pac.CODI_PAC == codiPac
              where pac.SomeOtherProperty == someOtherValue
              select pac).FirstOrDefault();

此处需要注意的一件非常重要的事情是predicate扩展方法中的IQueryable<T>.Where<T>(...)参数属于Expression<Func<T, bool>>类型。这允许IQueryable<T>提供程序在返回结果之前的最后一刻构造本机SQL(或其他本机提供程序查询)。

不使用Expression<Func<T, bool>>意味着您的查询将等同于此:

var result =
    _gam.PAC
        .ToArray()
        .Where(pac => pac.CODI_PAC == codiPac)
        .FirstOrDefault();

这意味着在选择第一个过滤结果并丢弃其余结果之前,查询会将“PAC”表中的每条记录加载到内存中。

最重要的是,通过制作通用帮助程序方法,您重写现有框架代码,并打开性能和维护问题,同时减少代码可读性

我希望这会有所帮助。