开发者

Make a Search Method generic using LINQ

开发者 https://www.devze.com 2023-03-03 06:08 出处:网络
I have a method in my project th开发者_运维技巧at repeats over and over: public PAC PAC_GetByCodiPac(string codiPac)

I have a method in my project th开发者_运维技巧at repeats over and over:

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;
        }

The table PAC means (patient), so I have these methods for all the tables I have. How can I make a generic method for this? Thanks in advance.


Here is your generic method. Note, that as others pointed out FirstOrDefault is better than count and then first, so I'm using it here. But it's also possible to write the expression so that it mimics what your original code does. Please let me know if you need additional help with this.

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());
}

You use it like this:

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

Edit 1: I changed the code according to the comment so that you can pass arbitrary ID field name in.


I see that what you asked is a very straight forward where query even doesn't require to have have it on a separate method. Also you can simply enhance your query link the following:

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

FirstOrDefault will return the first item on the array, if not it will return null.


If you want a generic method that lets you specify any table and any predicate for records from that table then you can't really get any better than the built-in Where<T>(...) and (as others have already pointed out) the FirstOrDefault<T>(...) extension methods.

Your code would then look like so:

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

The best you could get then, writing your own generic method, would be this:

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

And that is really just redundant. Especially when your calling code would be actually longer using the helper method:

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

And even worse, your code is no longer using a fluent, composable syntax. This just makes readability and maintenance more difficult.

If you stick with using the IQueryable<T> extension methods then you can do composition like this:

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();

One very important thing to note here is that the predicate parameter in the IQueryable<T>.Where<T>(...) extension method is of type Expression<Func<T, bool>>. This allows the IQueryable<T> provider to construct the native SQL (or other native provider query) at the very last moment before returning a result.

Not using Expression<Func<T, bool>> means that your query would be the equivalent of this:

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

And that would mean the query will load every record from the "PAC" table into memory before selecting the first filtered result and throwing out the rest of the results.

The bottom-line is that by making a generic helper method you are rewriting existing framework code and you open yourself to performance and maintenance issues while also reducing code readability.

I hope this helps.


I'm not sure if you are asking for this, but this method could be in a static class and method and so you'd be able to call it from everywhere.


An easy solution will be:

//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);
}
0

精彩评论

暂无评论...
验证码 换一张
取 消