How to extend LINQ select method in my own way - linq

The following statement works fine if the source is not null:
Filters.Selection
.Select(o => new GetInputItem() { ItemID = o.ItemId })
It bombs if "Filters.Selection" is null (obviously). Is there any possible way to write my own extension method which returns null if the source is null or else execute the "Select" func, if the source is not null.
Say, something like the following:
var s = Filters.Selection
.MyOwnSelect(o => new GetInputItem() { ItemID = o.ItemId })
"s" would be null if "Filters.Selection" is null, or else, "s" would contain the evaluated "func" using LINQ Select.
This is only to learn more about LINQ extensions/customizations.
thanks.

You could do this:
public static IEnumerable<U> SelectOrNull<T,U>(this IEnumerable<T> seq, Func<T,U> map)
{
if (seq == null)
return Enumerable.Empty<U>(); // Or return null, though this will play nicely with other operations
return seq.Select(map);
}

Yes have a look at the Enumerable and Queryable classes in the framework, they implement the standard query operators.
You would need to implement a similar class with the same Select extension methods matching the same signatures, then if the source is null exit early, you should return an empty sequence.

Assuming you're talking about LINQ to Objects, absolutely:
public static class NullSafeLinq
{
public static IEnumerable<TResult> NullSafeSelect<TSource, TResult>
(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
// We don't intend to be safe against null projections...
if (selector == null)
{
throw new ArgumentNullException("selector");
}
return source == null ? null : source.Select(selector);
}
}
You may also want to read my Edulinq blog post series to learn more about how LINQ to Objects works.

Related

How can I make LINQ Lambda expressions fail gracefully like XPath?

More a general question, but how can I write LINQ Lambda expressions such that they will return a default string or simply an empty string if the LINQ expression fails or returns nothing. In XSLT XPath if a match fails then one just got nothing, and the application did not crash whereas in LINQ one seems to get exceptions.
I use First() and have tried FirstOrDefault().
So example queries may be:
Customers.First(c=>c.id==CustId).Tasks.ToList();
or
Customers.Where(c=>c.id==CustId).ToList();
or
Model.myCustomers.Where(c=>c.id==CustId);
etc.
Whatever the query, if it returns no records or null, then is there a general approach to ensure the query fails gracefully?
Thanks.
There isn't anything elegant built into C# for propagating nulls when you access properties. You could create your own extension methods:
public static class Extensions
{
public static TValue SafeGet<TObject, TValue>(
this TObject obj,
Func<TObject, TValue> propertyAccessor)
{
return obj == null ? default(TValue) : propertyAccessor(obj);
}
public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> collection)
{
return collection ?? Enumerable.Empty<T>();
}
}
Used like this:
Customers.FirstOrDefault(c => c.id==CustId).SafeGet(c => c.Tasks).OrEmpty().ToList();
Customers.First(c=>c.id==CustId) will crash if there is no matching record.
There are few ways you can try to find it, if you use FirstOrDefault that'll return NULL if no match is found and you can check for NULL.
Or, you can use the .Any syntax which checks if you have any record and returns boolean.
The only query I would expect to throw an exception would be the first one (assuming that Customers is a valid collection and not null itself):
Customers.First(c=>c.id==CustId).Tasks.ToList();
This will throw an exception if there is no customer with an id of CustId (you have some casing issues with your property and variable names).
If you don't wish to throw an exception on no match, then use FirstOrDefault as you mention, and do a null check, e.g:
var customer = Customers.FirstOrDefault(c => c.id == CustId);
if (customer == null)
{
// deal with no match
return;
}
var taskList = customer.Tasks.ToList();

Use LINQ to select Single from nested collections

I have two classes - MyBaseClass and BaseClassContainer - that are declared like such:
public class MyBaseClass
{
private Guid id;
public Guid ID
{
if (id == Guid.Empty)
{
id = Guid.NewGuid();
}
return id;
}
//...Other Properties omitted for brevity
}
and
public class BaseClassContainer : INotifyPropertyChanged
{
private ObservableCollection<MyBaseClass> baseClasses;
public ObservableCollection<MyBaseClass> BaseClasses
{
//...Omitted for brevity...
}
}
Then in my code I have an ObservableCollection of type BaseClassContainer (BaseClassContainerCollection). What I'm trying to figure out is how can I use LINQ to select a single BaseClassContainer from the ObservableCollection where one of its MyBaseClass.ID matches a specific Guid. The reason I'm using the Single() method is because I know they're all going to be unique.
I've tried the following but it doesn't work:
var result = BaseClassContainerCollection.Single(container => container.BaseClasses.Single(baseClass => baseClass.ID == specificGuid));
I get an error saying: Cannot implicitly convert type 'MyBaseClass' to 'bool'. What am I missing?
Lets break apart your query:
BaseClassContainerCollection.Single(yourPredicate);
Single, as it is used here, basically says "filter BaseClassContainerCollection on this predicate" (a "filter" function that evaluates to true or false for whether or not to include it in the results). Instead of a function that returns true/false, you're saying you want it to evaluate to a MyBaseClass, which doesn't make sense. Your inner call to Single makes sense, because x => x.Id == guid is a function that returns true/false and filters to only those elements that meet the criteria (then states that you know there will only be one of them in the results or else throw an exception).
What you want to do is Select the single MyBaseClass result from the inner query, then call Single on the result (without a predicate) since you know the result should only have one item returned. I believe you're looking for:
BaseClassContainerCollection.Select(container => container.BaseClasses.Single(baseClass => baseClass.ID == specificGuid)).Single();

Linq Parsing Error when trying to create seperation of concerns

I am in the middle of a refactoring cycle where I converted some extension methods that used to look like this:
public static IQueryable<Family> FilterOnRoute(this IQueryable<Family> families, WicRoute route)
{
return families.Where(fam => fam.PODs
.Any(pod => pod.Route.RouteID == route.RouteID));
}
to a more fluent implementation like this:
public class SimplifiedFamilyLinqBuilder
{
private IQueryable<Family> _families;
public SimplifiedFamilyLinqBuilder Load(IQueryable<Family> families)
{
_families = families;
return this;
}
public SimplifiedFamilyLinqBuilder OnRoute(WicRoute route)
{
_families = _families.Where(fam => fam.PODs
.Any(pod => pod.Route.RouteID == route.RouteID));
return this;
}
public IQueryable<Family> AsQueryable()
{
return _families;
}
}
which I can call like this: (note this is using Linq-to-Nhibernate)
var families =
new SimplifiedFamilyLinqBuilder()
.Load(session.Query<Family>())
.OnRoute(new WicRoute() {RouteID = 1})
.AsQueryable()
.ToList();
this produces the following SQL which is fine with me at the moment: (of note is that the above Linq is being translated to a SQL Query)
select ... from "Family" family0_
where exists (select pods1_.PODID from "POD" pods1_
inner join Route wicroute2_ on pods1_.RouteID=wicroute2_.RouteID
where family0_.FamilyID=pods1_.FamilyID
and wicroute2_.RouteID=#p0);
#p0 = 1
my next effort in refactoring is to move the query part that deals with the child to another class like this:
public class SimplifiedPODLinqBuilder
{
private IQueryable<POD> _pods;
public SimplifiedPODLinqBuilder Load(IQueryable<POD> pods)
{
_pods = pods;
return this;
}
public SimplifiedPODLinqBuilder OnRoute(WicRoute route)
{
_pods = _pods.Where(pod => pod.Route.RouteID == route.RouteID);
return this;
}
public IQueryable<POD> AsQueryable()
{
return _pods;
}
}
with SimplifiedFamilyLinqBuilder changing to this:
public SimplifiedFamilyLinqBuilder OnRoute(WicRoute route)
{
_families = _families.Where(fam =>
_podLinqBuilder.Load(fam.PODs.AsQueryable())
.OnRoute(route)
.AsQueryable()
.Any()
);
return this;
}
only I now get this error:
Remotion.Linq.Parsing.ParserException : Cannot parse expression 'value(Wic.DataTests.LinqBuilders.SimplifiedPODLinqBuilder)' as it has an unsupported type. Only query sources (that is, expressions that implement IEnumerable) and query operators can be parsed.
I started to implement IQueryable on SimplifiedPODLinqBuilder(as that seemed more logical than implementing IEnumberable) and thought I would be clever by doing this:
public class SimplifiedPODLinqBuilder : IQueryable
{
private IQueryable<POD> _pods;
...
public IEnumerator GetEnumerator()
{
return _pods.GetEnumerator();
}
public Expression Expression
{
get { return _pods.Expression; }
}
public Type ElementType
{
get { return _pods.ElementType; }
}
public IQueryProvider Provider
{
get { return _pods.Provider; }
}
}
only to get this exception (apparently Load is not being called and _pods is null):
System.NullReferenceException : Object reference not set to an instance of an object.
is there a way for me to refactor this code out that will parse properly into an expression that will go to SQL?
The part fam => _podLinqBuilder.Load(fam.PODs.AsQueryable() is never going to work, because the linq provider will try to parse this into SQL and for that it needs mapped members of Family after the =>, or maybe a mapped user-defined function but I don't know if Linq-to-Nhibernate supports that (I never really worked with it, because I still doubt if it is production-ready).
So, what can you do?
To be honest, I like the extension methods much better. You switched to a stateful approach, which doesn't mix well with the stateless paradigm of linq. So you may consider to retrace your steps.
Another option: the expression in .Any(pod => pod.Route.RouteID == route.RouteID)); could be paremeterized (.Any(podExpression), with
OnRoute(WicRoute route, Expression<Func<POD,bool>> podExpression)
(pseudocode).
Hope this makes any sense.
You need to separate methods you intend to call from expressions you intend to translate.
This is great, you want each of those methods to run. They return an instance that implements IQueryable<Family> and operate on that instance.
var families = new SimplifiedFamilyLinqBuilder()
.Load(session.Query<Family>())
.OnRoute(new WicRoute() {RouteID = 1})
.AsQueryable()
.ToList();
This is no good. you don't want Queryable.Where to get called, you want it to be an expression tree which can be translated to SQL. But PodLinqBuilder.Load is a node in that expression tree which can't be translated to SQL!
families = _families
.Where(fam => _podLinqBuilder.Load(fam.PODs.AsQueryable())
.OnRoute(route)
.AsQueryable()
.Any();
You can't call .Load inside the Where expression (it won't translate to sql).
You can't call .Load outside the Where expression (you don't have the fam parameter).
In the name of "separation of concerns", you are mixing query construction methods with query definition expressions. LINQ, by its Integrated nature, encourages you to attempt this thing which will not work.
Consider making expression construction methods instead of query construction methods.
public static Expression<Func<Pod, bool>> GetOnRouteExpr(WicRoute route)
{
int routeId = route.RouteID;
Expression<Func<Pod, bool>> result = pod => pod.Route.RouteID == route.RouteID;
return result;
}
called by:
Expression<Func<Pod, bool>> onRoute = GetOnRouteExpr(route);
families = _families.Where(fam => fam.PODs.Any(onRoute));
With this approach, the question is now - how do I fluidly hang my ornaments from the expression tree?

IEqualityComparer exception

I am using Entity Framework 4.0 and trying to use the "Contains" function of one the object sets in my context object. to do so i coded a Comparer class:
public class RatingInfoComparer : IEqualityComparer<RatingInfo>
{
public bool Equals(RatingInfo x, RatingInfo y)
{
var a = new {x.PlugInID,x.RatingInfoUserIP};
var b = new {y.PlugInID,y.RatingInfoUserIP};
if(a.PlugInID == b.PlugInID && a.RatingInfoUserIP.Equals(b.RatingInfoUserIP))
return true;
else
return false;
}
public int GetHashCode(RatingInfo obj)
{
var a = new { obj.PlugInID, obj.RatingInfoUserIP };
if (Object.ReferenceEquals(obj, null))
return 0;
return a.GetHashCode();
}
}
when i try to use the comparer with this code:
public void SaveRatingInfo2(int plugInId, string userInfo)
{
RatingInfo ri = new RatingInfo()
{
PlugInID = plugInId,
RatingInfoUser = userInfo,
RatingInfoUserIP = "192.168.1.100"
};
//This is where i get the execption
if (!context.RatingInfoes.Contains<RatingInfo>(ri, new RatingInfoComparer()))
{
//my Entity Framework context object
context.RatingInfoes.AddObject(ri);
context.SaveChanges();
}
}
i get an execption:
"LINQ to Entities does not recognize the method 'Boolean Contains[RatingInfo](System.Linq.IQueryable1[OlafCMSLibrary.Models.RatingInfo], OlafCMSLibrary.Models.RatingInfo,
System.Collections.Generic.IEqualityComparer1[OlafCMSLibrary.Models.RatingInfo])' method, and his method cannot be translated into a store expression."
Since i am not proficient with linQ and Entity Framework i might be making a mistake with my use of the "var" either in the "GetHashCode" function or in general.
If my mistake is clear to you do tell me :) it does not stop my project! but it is essential for me to understand why a simple comparer doesnt work.
Thanks
Aaron
LINQ to Entities works by converting an expression tree into queries against an object model through the IQueryable interface. This means than you can only put things into the expression tree which LINQ to Entities understands.
It doesn't understand the Contains method you are using, so it throws the exception you see. Here is a list of methods which it understands.
Under the Set Methods section header, it lists Contains using an item as supported, but it lists Contains with an IEqualityComparer as not supported. This is presumably because it would have to be able to work out how to convert your IEqualityComparer into a query against the object model, which would be difficult. You might be able to do what you want using multiple Where clauses, see which ones are supported further up the document.

linq and object initialisation

If I have something like:
var query = from children in _data.Children
where children.ChildId == childId
select new CustomModel.MyChild
{
ChildId = children.ChildId,
Name = children.ChildName
};
return query.FirstOrDefault();
Where I want the resultant object to be my custom model.
Can I handle the custom model instantiation in a different method, which could be reused if I had multiple linq queries that all generated a custom child model?
For example,
var query = from children in _data.Children
where children.ChildId == childId
select CreateMyCustomChild([param ??]);
return query.FirstOrDefault();
This may well be impossible, I don't know, but what would the method signature be like if it is possible?
I'm only thinking reuse for when multiple linq queries contain duplicate object initialisation code.
Thanks
It really depends on what version of LINQ you're using. If you're using LINQ to SQL, I don't believe you can call arbitrary methods in the query. The query translator wouldn't know what to do with the method call
If you're using LINQ to Objects, you're absolutely fine to do it, like this:
var query = from children in _data.Children
where children.ChildId == childId
select CreateMyCustomChild(children)
return query.FirstOrDefault();
// Elsewhere
public CustomModel.MyChild CreateMyCustomChild(OtherChild child)
{
return new CustomModel.MyChild
{
ChildId = child.ChildId,
Name = child.ChildName
};
}
(Side note: I'd call the range variable in the query "child" rather than "children" as at any one time it only represents a single child.)
If you wanted you could write "select 1" or in your case "CreateMyCustomChild(children)" since "children" is containing all your info. In your case you aren't adding a lot of info to "children", so why not "select children"?
In other words, just try it out. The return type of your value will determine over which type your LINQ enumerates.
Suppose you had a method that did the transform for you.
public static class Conversions
{
public static CustomModel.MyChild ToCustomModel(this DataModel.MyChild source)
{
return new CustomModel.MyChild()
{
ChildId = source.ChildId,
Name = source.ChildName
}
}
}
You can use such a method to do the conversion of a single item.
DataModel.MyChild myResult = getResult();
CustomModel.MyChild myConvertedResult = myResult.ToCustomModel()
Such a method can also be used in a Enumerable.Select method call.
IEnumerable<DataModel.MyChild> myQueriedResults = getResult();
IEnumerable<CustomModel.MyChild> myConvertedResults =
myQueryiedResults.Select(c => c.ToCustomModel());
While you can do with expressions, I don't think it is worth the hassle. Instead I suggest you define an extension method like:
IQueryable<CustomModel.MyChild> ToModel(this IQueryable<Child> childs)
{
return childs.Select( c=>
select new CustomModel.MyChild
{
ChildId = children.ChildId,
Name = children.ChildName
}
);
}
You can then call:
return _data.Children
.Where(c=>c.ChildId == childId)
.ToModel()
.FirstOrDefault();

Resources