Filter with terniary operator - c#

I have a method
AddOrUpdateList(this List<T> list, T scope, Func<T, bool>, Func<T, bool> when = null){
T item = default;
if (when == null)
list.SingleOrDefault(t => t.Equals(scope)); //Check Single
else
item = list.SingleOrDefault(when);
.
.
.
}
When I'm going to use it I have two possible starts:
I receive an element which element.Title is something, so I use the Title as filter
I receive an element which has no title, so I need to get the last one from a List
I was thinking to use terniary operator and lambda to create a filter that contemplates both cases.
What I came up with is:
var filter = (element.Title != "")
? (Func<T, bool>)(e => e.Title == element.Title)
: (Func<T, bool>)(e = list.Select(ele => ele).Last());
But that gives me: Error CS0030 Cannot convert type 'T' to 'System.Func<T, bool>'
Any idea or help you can give me??
Im pretty new to C# and Linq so I don't understand to well how some things should work
Thanks in advance.

Your current filter is
var filter = (element.Title != "")
? (Func<T, bool>)(e => e.Title == element.Title)
: (Func<T, bool>)(e = list.Select(ele => ele).Last());
But e = list.Select(ele => ele).Last() is not a Func<T, bool>. In fact, it is not a function at all. As #canton7 said, you need to use the arrow notation (=>)to create a lambda. Moreover, even if that line was a lambda, it would be of type Func<T, T>,
since it simply returns the last item in a list of items of type T; it does not return a bool at all!
Is this more what you want?
var filter = (element.Title != "")
? (Func<T, bool>)(e => e.Title == element.Title)
: (Func<T, bool>)(e => e.Title == list.Last().Title);

What type of element? So, AddOrUpdateList(Func<T, bool>) uses T as type of some generic code. When you have concrete element, you should know what type of code. And set a concrete class name.
If you show more code, I will try to help you.
May be I сlimbing out of business, but it seems strange. Acording to method name, you want to Add element if it is not exist or update it if it already in list.
So, here is an example of typicaly such method implementation.
public void AddOrUpdate<T, K>(this List<T> list, T element, Func<T, K> keySelector, Action<T, T> mapFunction) : T new()
{
var elementKey = keySelector(element);
var elementForUpdate = list.FirstOrDefault(x => keySelector(x) == elementKey);
var exists = elementForUpdate != null;
if (!exists)
{
var newElement = new T();
list.Add(newElement);
elementForUpdate = list.Last();
}
mapFunction(elementForUpdate, element);
}
So, then in other part of code you can use it like:
...
var itemToAdd = new ClassWithTitle { Title = "Title 1"};
list.AddOrUpdate(itemToAdd , x => x.Title, (destination, origin) =>
{
//Logic to map fields
destination.Title = origin.Title;
destination.SomeOtherField = origin.SomeOtherField;
})
...
Please, pay attention.
One more option, if you work with list (not with database), you can use method IndexOf() then this method will very simple.
public void AddOrUpdate<T, K>(this List<T> list, T element, Func<T, K> keySelector)
{
var elementKey = keySelector(element);
var elementFounded = list.FirstOrDefault(x => keySelector(x) == elementKey);
var exists = elementForUpdate != null;
if (!exists)
{
//IMPORTANT: Here you add element that sended to method. You should take it into consideration.
//If you want to add a clone of this object, implement interface IClonable and use this method like list.Add(element.Clone());
list.Add(element);
return;
}
var indexOfFoundedElement = list.IndexOf(elementFounded);
//IMPORTANT: Here you replace element that sended to method. You should take it into consideration.
list[indexOfFoundedElement] = element;
}

Related

Call OrderBy() with a field name as a string [duplicate]

This question already has answers here:
Generate EF orderby expression by string [duplicate]
(6 answers)
Closed 7 years ago.
I am using .NET 4.51, EF 6
I make a number of calls to my repository layer where I need to do some basic ordering on a single field in either ascending or descending order such as:
The result of GetAllList() is a List<T>. Now unfortunately the Id field I have to sort by is not always called Id nor is the Text field. They can be other things such as MyId, SomeTextField and so on.
So I was wondering if there was a way I could do the OrderBy() and OrderByDescending() clauses by supplying a string for the field name something like:
_Repository.GetAllList().OrderBy(r => r."SomeTextField")
In this way I could move all this code to a common method.
Any pointers greatly appreciated.
This will work:
public static class LinqExtensions
{
private static PropertyInfo GetPropertyInfo(Type objType, string name)
{
var properties = objType.GetProperties();
var matchedProperty = properties.FirstOrDefault (p => p.Name == name);
if (matchedProperty == null)
throw new ArgumentException("name");
return matchedProperty;
}
private static LambdaExpression GetOrderExpression(Type objType, PropertyInfo pi)
{
var paramExpr = Expression.Parameter(objType);
var propAccess = Expression.PropertyOrField(paramExpr, pi.Name);
var expr = Expression.Lambda(propAccess, paramExpr);
return expr;
}
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> query, string name)
{
var propInfo = GetPropertyInfo(typeof(T), name);
var expr = GetOrderExpression(typeof(T), propInfo);
var method = typeof(Enumerable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
return (IEnumerable<T>) genericMethod.Invoke(null, new object[] { query, expr.Compile() });
}
public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string name)
{
var propInfo = GetPropertyInfo(typeof(T), name);
var expr = GetOrderExpression(typeof(T), propInfo);
var method = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
return (IQueryable<T>) genericMethod.Invoke(null, new object[] { query, expr });
}
}
Testing:
var r = new List<temp> {
new temp { a = 5 },
new temp { a = 1 },
new temp { a = 15 }
}.OrderBy("a");
Gives the correct result (1, 5, 15) - and will provide lazy execution for your use with EF
You will need to implement the overloads if needed.
Does it have to be a string? Why not just make a method that takes a Func key selector as a parameter.
public List<T> GetAllListOrdered<T,TProp>(SimpleOrderingDirectionEnum direction, Func<T,TProp> keySelector)
{
return direction == SimpleOrderingDirectionEnum.Ascending ? _Repository.GetAllList().OrderBy(keySelector).ToList() : _Repository.GetAllList().OrderByDescending(keySelector).ToList();
}
Then call it like
Func<ObjectToSortType, ObjectPropertyToSortBy> keySelector = r => r.Id;
GetAllListOrdered(SimpleOrderingDirectionEnum.Ascending, keySelector);
If the Rob's answer is not enough for you. Try Linq Dynamic. http://dynamiclinq.azurewebsites.net/
using System.Linq.Dynamic; //Import the Dynamic LINQ library
//The standard way, which requires compile-time knowledge
//of the data model
var result = myQuery
.Where(x => x.Field1 == "SomeValue")
.Select(x => new { x.Field1, x.Field2 });
//The Dynamic LINQ way, which lets you do the same thing
//without knowing the data model before hand
var result = myQuery
.Where("Field1=\"SomeValue\"")
.Select("new (Field1, Field2)");
Thanks to all. Rob, your solution was pretty close to what I ended up with.
Based on your insights I did some more searching and came across Marc Gravel's answer here Dynamic LINQ OrderBy on IEnumerable<T> (second post).
It added dynamic's as an additional bonus.

Convert generic parameter of type T to List<TP> when T is IEnumerable<TP>

My application has a method that caches a value. I need a second method that check if the generic parameter of type T implements IEnumerable and not implements IList. If the answer is yes, the method should do a .ToList and cast it back to T (See comment in code example).
Can you tell me how I do the .ToList() to return a List casted to back to T? (Is it actually possible?)
Something like this:
public T ToListIfIEnumerable<T>(T value)
{
var needToCovert = TypeImplementsGenericIEnumerableButNotGenericIList(value);
if (!needToCovert)
{
return value;
}
// return value.ToList() <-- How to do that???
}
private bool TypeImplementsGenericIEnumerableButNotGenericIList<T>(T value)
{
var type = value.GetType();
var interfaces = type.GetInterfaces();
var gi = typeof(IEnumerable<>);
var gl = typeof(IList<>);
// It would be better if the next lines did't compare strings!
// Suggestions welcome...
var implementsIEnumerable = interfaces.Any(
i => i.IsGenericType && i.Name == gi.Name && i.Namespace == gi.Namespace);
var implementsIList = interfaces.Any(
i => i.IsGenericType && i.Name == gl.Name && i.Namespace == gl.Namespace);
return implementsIEnumerable && !implementsIList;
}
Backgrond info:
The object that uses the method does something like Lazy. Caching an IEnumerable makes not much sense in examples like: Enumerable.Range(1, 3).Select(i => Guid.NewGuid()) It does gives new Guid's every time you call it. That is why I would like to do a ToList().
Dynamic typing and overloading could help here, if you don't mind using dynamic:
object ConvertToListIfNecessary(dynamic input)
{
return MaybeToList(input);
}
private IList<T> MaybeToList<T>(IEnumerable<T> input)
{
return input.ToList();
}
private IList<T> MaybeToList<T>(IList<T> input)
{
return input;
}
private object MaybeToList(object input)
{
// Take whatever action you want if the input *doesn't* implement
// IEnumerable<T>
}
Basically this gets the smarts behind dynamic to do the grungy reflection work for you. It may not be quite as quick as something that's hand-rolled, but it's likely to be much simpler to get right.

Creating a composite condition using anonymous filter method

I am trying to edit a search tool using linq,
What I like a filter in where clause is (ItemNumber == X AND ( StatementStatus == SatusA Or StatementStatus == StatusB ) )
But right now, it is like:
What I like a filter in where clause is (ItemNumber == X AND StatementStatus == SatusA Or StatementStatus == StatusB )
as AND has higher operational priority over OR the result is not what I want. :)
Could you please help?
using (var ctx = new MyContext()) {
Func<Statement, bool> filter = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filter = new Func<Statement, bool>(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
if (request.StatusA)
filter = filter == null ? new Func<Statement, bool>(s => s.StatementStatus == StatementStatusType.StatusA) :
filter.And(s => s.StatementStatus == StatementStatusType.StatusA);
if (request.StatusB)
filter = filter == null ? new Func<Statement, bool>(s => s.StatementStatus == StatementStatusType.StatusB) :
filter.Or(s => s.StatementStatus == StatementStatusType.StatusB);
var results = ctx.Statements
.Include("StatementDetails")
.Include("StatementDetails.Entry")
.Where(filter)
.Take(100)
.Select(s => new StatementSearchResultDTO{ ....
}
}
That's happens not because AND have higher priority than OR. What happens in reality:
var firstFilter = ...; // itemNumber
var secondFilter = ...; // statusA
var firstAndSecondFilter = firstFilter.And(secondFilter); // itemNumber && statusA
var thirdFilter = ...; // statusB
var endFilter = firstAndSecondFilter.Or(thirdFilter) // (itemNumber && statusA) || statusB.
The problem - wrong control flow. You must to do something like that:
var filterByA = ...;
var filterByB = ...;
var filterByAorB = filterByA.Or(filterByB);
var filterByNumber = ...;
var endFiler = filterByNumber.And(filterByAorB);
And your code is bad, not just because it works wrong, but because it's hard to write code in such style. Reasons:
This code doesn't follow DRY principle. You have two same lambdas that checks for StatusA (look in your ternary operator) and two same lambdas that checks for StatusB
You have too long ternary operator with null checks. That's bad because you don't see general picture, your eyes focused on syntax problems. You may write and extension method AndNullable for funcs. Like this:
static Func<T1, TOut> AndNullable<T1, TOut>(this Func<T1, TOut> firstFunc, Func<T1, TOut> secondFunc) {
if (firstFunc != null) {
if (secondFunc != null)
return firstFunc.And(secondFunc);
else
return firstFunc;
}
else {
if (secondFunc != null)
return secondFunc;
else
return null;
}
}
And that same for Or. Now your code can be wroted like this:
Func<Statement, bool> filter = null;
if (request.StatusA)
filter = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filter = filter.OrNullable(s => s.StatementStatus == StatementStatusType.StatusB);
if (!string.IsNullOrEmpty(request.ItemNumber))
filter = filter.AndNullable(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
Reads more better.
Your filter is global filter. Writing of global filter is simpler for few filter conditions and number of lines is small, but it's more complicated to understand your filter. Rewrite it in this way:
Func<Statement, bool> filterByStatusA = null;
Func<Statement, bool> filterByStatusB = null;
if (request.StatusA)
filterByStatusA = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filterByStatusB = s => s.StatementStatus == StatementStatusType.StatusB;
Func<Statement, bool> filterByStatuses = filterByStatusA.OrNullable(filterByStatusB);
Func<Statement, bool> filterByItemNumber = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filterByItemNumber = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
Func<Statement, bool> endFilter = filterByItemNumber.And(filterByStatuses);
Okay, we have outthinked how we can write filters by combining them as Func<..> but we still have problems.
What problems we will got, if result filter is null? Answer: ArgumentNullException due to documentation. We must to think about this case.
What another problems we can got with using of simple Func<...>? Well, you must to know difference between IEnumerable<T> and IQueryable<T> interfaces. In simple words, all operations on IEnumerable causes simple iteratation over all elements (well, it's lazy, IEnumerable really slower than IQueryable). So, for example, combining of Where(filter), Take(100), ToList() on collection that have 10000 elements that are bad for this filter and 400 elements that are good will cause iterating over 10100 elements. If you wrote similar code for IQueryable the request of filtering will send on database server and this server will iterate only ~400 (or 1000, but not 10100), if you have configured indexes on database. So what happens in your code.
var results = ctx.Statements // you are getting DbSet<Statement> that implements interface IQueryable<Statement> (and IQueryable<T> implements IEnumerable<T>)
.Include("StatementDetails") // still IQueryable<Statement>
.Include("StatementDetails.Entry") // still IQueryable<Statement>
.Where(filter) // Cuz your filter is Func<..> and there are no extension methods on IQueryable that accepts Func<...> as parameter, your IQueryable<Statement> casted automatically to IEnumerable<Statement>. Full collection will be loaded in your memory and only then filtered. That's bad
.Take(100) // IEnumerable<Statement>
.Select(s => new StatementSearchResultDTO { .... // IEnumerable<Statement> -> IEnumerable<StatementSearchResultDTO>
}
Okay. Now you understand the problem. So, simple right code for you can be writed in this way:
using (var ctx = new MyContext()) {
results = ctx.Statements
.Include("StatementDetails")
.Include("StatementDetails.Entry")
.AsQueryable();
if (!string.IsNullOrEmpty(request.ItemNumber))
results = results.Where(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
if (request.StatusA) {
if (request.StatusB)
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusA ||
s.StatementStatus == StatementStatusType.StatusA);
else
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusA);
}
else {
if (request.StatusB) {
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusB);
}
else {
// do nothing
}
}
results = .Take(100)
.Select(s => new StatementSearchResultDTO{ ....
};
// .. now you can you results.
}
Yeah, totally ugly, but now your database solves how to find Statements that satisfy the filter. Therefore, this request is quickly as possible. Now we must understand what magic happens in code I written upper. Let's compare two examples of code:
results = results.Where(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
And this:
Func<Statement, bool> filter = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
results = results.Where(filter);
What the difference? Why first is more faster? Answer: when compiler sees first code, it examines that type of results is IQueryable<T> and IEnumerable<T> so that condition inside of brackets can have type Func<Statement, bool> (compiled function) or Expression<Func<Statement, bool>> (data, that can be compiled in function). And compiler chooses Expression (why - really dunno, just chooses). After request of first object query compiled not in C# statement, but in SQL statement and sends to server. Your SQL server can optimize request, because of indexes existing.
Well, the more better way - to write your own expressions. There are different ways to write your own expression, but there is a way to write it with not ugly syntax. The problem that you can't just invoke one expression from another - that doesn't supported by Entity Framework and can be not supported by another ORM's. So, we can use PredicateBuilder by Pete Montgomery: link. And then write two simple extensions on expressions suitable for us.
public static Expression<Func<T, bool>> OrNullable<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
if (first != null && second != null)
return first.Compose(second, Expression.OrElse);
if (first != null)
return second;
if (second != null)
}
And that same for And. And now we can write our filter:
{
Expression<Func<Statement, bool>> filterByStatusA = null;
Expression<Func<Statement, bool>> filterByStatusB = null;
if (request.StatusA)
filterByStatusA = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filterByStatusB = s => s.StatementStatus == StatementStatusType.StatusB;
Expression<Func<Statement, bool>> filterByStatuses = filterByStatusA.OrNullable(filterByStatusB);
Expression<Func<Statement, bool>> filterByItemNumber = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filterByItemNumber = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
Expression<Func<Statement, bool>> endFilter = filterByItemNumber.And(filterByStatuses);
requests = ...;
if (endFilter != null)
requests = requests.Where(endFilter);
}
You can got a problem, because class ExpressionVisitor in PredicateBuilder in .NET < 4.0 is sealed. You can get write your own ExpressionVisitor or just copy it from this article.
OK, here is the way I have solved it:
filter.And(s => (request.StatusA && s.StatementStatus == StatementStatusType.StatusA) ||
(request.StatusB && s.StatementStatus == StatementStatusType.StautsB) ||
!(request.StatusA || request.StatusB)); //None selected = All selected
Any comments?

LINQ to Entities does not recognize the method 'Boolean Contains[Int32]

I have the following extension methods in which I am using to do a Contains on LINQ-To-Entities:
public static class Extensions
{
public static IQueryable<TEntity> WhereIn<TEntity, TValue>
(
this ObjectQuery<TEntity> query,
Expression<Func<TEntity, TValue>> selector,
IEnumerable<TValue> collection
)
{
if (selector == null) throw new ArgumentNullException("selector");
if (collection == null) throw new ArgumentNullException("collection");
if (!collection.Any())
return query.Where(t => false);
ParameterExpression p = selector.Parameters.Single();
IEnumerable<Expression> equals = collection.Select(value =>
(Expression)Expression.Equal(selector.Body,
Expression.Constant(value, typeof(TValue))));
Expression body = equals.Aggregate((accumulate, equal) =>
Expression.Or(accumulate, equal));
return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, p));
}
//Optional - to allow static collection:
public static IQueryable<TEntity> WhereIn<TEntity, TValue>
(
this ObjectQuery<TEntity> query,
Expression<Func<TEntity, TValue>> selector,
params TValue[] collection
)
{
return WhereIn(query, selector, (IEnumerable<TValue>)collection);
}
}
When I call the extenion method to check if a list of ids is in a particular table, it works and I get back the List of ids, like this:
List<int> Ids = _context.Persons
.WhereIn(x => x.PersonId, PersonIds)
.Select(x => x.HeaderId).ToList();
When I execute the next statement, it complains that LINQ-To-Entities does not recogonize Contains(int32), but I thought I am not going against the entity anymore, but a collection of ints.
predicate = predicate.And(x=> Ids.Contains(x.HeaderId));
If I have a comma separated string such as "1,2,3", then the following works:
predicate = predicate.And(x=>x.Ids.Contains(x.HeaderId));
I am trying to take the List returned and create comma separated list of strings, the problem here is that now when I do predicate = predicate.And(x=>sb.Contains(x.HeaderId.ToString());, it complains that it does not like ToString().
I also tried doing:
predicate = predicate.And(x=>Extensions.WhereIn(Ids, x.id));, but it can't resolve WhereIn. It says I must add `<>`, but I am not sure what to add here and how implement it.
Where is nothing wrong with your WhereIn, and you are correct: when you use Ids, you are not going against the entity anymore, but a collection of ints.
Problem is when you're using .And on predicate: LINQ-To-Entities tries to convert everything inside those brackets into Entities methods, and there is no corresponding Contains method.
Solution:
Instead of
predicate = predicate.And(x=> Ids.Contains(x.HeaderId));
use
predicate = predicate.And(Contains<XClassName, int>(x.HeaderId));
where Contains defined as follows:
private static Expression<Func<TElement, bool>> Contains<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, List<TValue> values)
{
if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
if (null == values) { throw new ArgumentNullException("values"); }
if (!values.Any())
return e => false;
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
return Expression.Lambda<Func<TElement, bool>>(#equals.Aggregate(Expression.Or), valueSelector.Parameters.Single());
}
and XClassName is the name of the class of your x
You cant use array like that, you need to previsit this lambda in order to expand it to primitives. Alternatively you can change underlying provider so it knows how to generate IN statement , as it doesnt by default.
Didnt find post where one guys actually implement it, will updated once I did.
Basically when you use your extension method it is like
x=>arr.Contains(x)
So if you try to execute such lambda agains your entityset etc it will throw you exception saying that parameters can only be primitives.
The reason is that underlying provider doesnt know how to convert .Contains method for array as function parameter into sql query. And in order to solve that you have two options
teach it how to use T[] as parameter and use Contains with this parameter
update your extension method in order to generate new lamda which will use 'allowed' building blocks, ie expressions using primitive types like int, string, guid etc.
Check this article
http://msdn.microsoft.com/en-us/library/bb882521(v=vs.90).aspx
Replace your:
List<int> Ids = _context.Persons
.WhereIn(x => x.PersonId, PersonIds)
.Select(x => x.HeaderId).ToList();
with
var Ids = _context.Persons
.WhereIn(x => x.PersonId, PersonIds)
.Select(x => x.HeaderId).ToList();
and then try.

Make a Search Method generic using LINQ

I have a method in my project that 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);
}

Categories

Resources