Projections over sub collections - EF4 - linq

Lets assume the following model with EF4:
class Order
{
....
public int Id {get;private set;}
//ICollection is the root of all evil here
public ICollection<OrderDetail> Details {get;private set;}
}
I can then project over this structure with Linq:
var IdAndCount = context
.Orders
.Select ( o => new {
Id = o.Id,
Count = o.Details.Where(d => d.Foo > 0).Count()});
So far so good, this will be fully translated to sql.
Now to the problem, what if I want to extract the where clause predicate in this query:
Func<OrderDetail,bool> detailPredicate = d => d.Foo > 0;
var IdAndCount = context
.Orders
.Select ( o => new {
Id = o.Id,
Count = o.Details
.Where(detailPredicate)
.Count()});
This compiles, but fails at runtime because there is no way for EF4 to translate the predicate to SQL since it is a Func and not an Expression.
Changing the predicate to an Expression<Func<OrderDetail,bool>> will not work since ".Where" on the OrderDetails links to the IEnumerable "Where" since the details are ICollection.
So, is it possible to extract parts of a bigger Linq query if the properties beeing traversed are IEnumerable or similair?

Try
Expression<Func<OrderDetail,bool>> detailPredicate = d => d.Foo > 0;

Related

LINQ to Subsonic Left Outer Join

I have the following query:
var q = from x in content_item.All()
join y in vendor.All() on x.Vendor_ID equals y.Vendor_ID into tmp
from v in tmp.DefaultIfEmpty()
select new { Z=x.Content_Item_Name,W=((v!=null)?v.Vendor_Name:"")};
when I type:
var items = q.ToList();
I got the following exception:
Expression of type 'System.Collections.Generic.IEnumerable`1[Vamp.Models.content_item]' cannot be used for parameter of type 'System.Linq.IQueryable`1[Vamp.Models.content_item]' of method 'System.Linq.IQueryable`1[<>f__AnonymousType0`2[Vamp.Models.content_item,System.Collections.Generic.IEnumerable`1[Vamp.Models.vendor]]] GroupJoin[content_item,vendor,Nullable`1,<>f__AnonymousType0`2](System.Linq.IQueryable`1[Vamp.Models.content_item], System.Collections.Generic.IEnumerable`1[Vamp.Models.vendor], System.Linq.Expressions.Expression`1[System.Func`2[Vamp.Models.content_item,System.Nullable`1[System.UInt32]]], System.Linq.Expressions.Expression`1[System.Func`2[Vamp.Models.vendor,System.Nullable`1[System.UInt32]]], System.Linq.Expressions.Expression`1[System.Func`3[Vamp.Models.content_item,System.Collections.Generic.IEnumerable`1[Vamp.Models.vendor],<>f__AnonymousType0`2[Vamp.Models.content_item,System.Collections.Generic.IEnumerable`1[Vamp.Models.vendor]]]])'
Any idea?
Note: content_item.All() is IQueryable and vendor.All() is IQueryable
Sorry I missed this question back when you asked it...
The left outer join syntax in SubSonic 3 is slightly different. I have a workaround posted as an answer to this question: Subsonic 3.0 Left Join
Hi you need to do something like this, create a getter setter as followed:
public class ReturnProperty
{
public string Z{ get; set; }
public string W{ get; set; }
}
And Change your query like this:
var q = from x in content_item.All()
join y in vendor.All() on x.Vendor_ID equals y.Vendor_ID into tmp
from v in tmp.DefaultIfEmpty()
select new ReturnProperty { Z=x.Content_Item_Name,W=((v!=null)?v.Vendor_Name:"")};
var items = q.ToList();
Hope this helps..

LINQ/Projection - How to sort TableA by non FK column id/name field per TableB?

How can I use LINQ/Projection to sort a list of A objects that contain an id field that references table/object B (B contains id and name).
I want to sort list of A objects that contain B by B.name?
Model (pseudo)
public class A
{
public int AId {get; set;}
public Nullable<int> BId {get; set;}
}
public class B
{
public int BId {get;set;}
public string Name {get;set;}
}
Code in some controller passing in a list of A's that contain B's but sort them by B.Name?
var list = db.As.OrderBy(x => x.BId->References.Name); // Way wrong but using something similar
return(list.ToList()
Basically, looking for the equivalent of this (using projection join or OrderBy from above):
var q1 =
from a in db.As
join b in db.Bs on a.BId equals b.BId
orderby b.Name // <- Need this to sort by B's name
select c;
Am I right that you are looking for the equivalent to the LINQ query you have already written above, only that it is based on extension methods instead?
In this case the following should work:
var list = db.As.Where(a => a.BId.HasValue)
.Join(db.Bs, a => a.BId.Value, b => b.BId, (a, b) => new { a, b.Name })
.OrderBy(r => r.Name)
.Select(r => r.a);
I've also added a check to make sure A.BId is not null before getting its value.
Just curious: why can't you use your LINQ query (with the only difference of selecting a instead of c)?

IQueryable to IQueryable<T>

is it possibile convert an IQueryable object to IQueryable where T is a mapped entity? (T will be a POCO class).
Thanks in advance.
Just Cast<T>() it. Assuming it is a queryable of the same type. Otherwise you could use the OfType<T>() filtering method to filter out items of a certain type.
IQueryable query = ...;
IQueryable<MyType> x = query.Cast<MyType>(); // assuming the queryable is of `MyType` objects
IQueryable<MyDerivedType> y = query.OfType<MyDerivedType>(); // filter out objects derived from `MyType` (`MyDerivedType`)
However in your case, you say that you are using Dynamic LINQ and doing a dynamic projection. Consider this completely made up query:
var query = dc.SomeTable
.Where("SomeProperty = \"foo\"")
.Select("new (SomeProperty, AnotherProperty)");
It results in a query of type IQueryable. You cannot cast this to a query of a specific type IQueryable<T> after all, what is T? What the Dynamic LINQ library does is creates a type that derives from DynamicCass. You could cast to IQueryable<DynamicClass> (query.Cast<DynamicClass>()) but you will not have access to the properties so it's moot.
Really the only nice option you have is to use dynamic to access these properties in this case.
foreach (dynamic x in query)
{
string someProperty = x.SomeProperty;
int anotherProperty = x.AnotherProperty;
// etc...
}
If you want to convert this to a query of your POCO objects, you'll have to do the conversion as a separate step but using LINQ to Objects.
IEnumerable<SomePoco> query =
dc.SomeTable
.Where("SomeProperty = \"foo\"")
.Select("new (SomeProperty, AnotherProperty)")
.Cast<DynamicObject>().AsEnumerable().Cast<dynamic>()
.Select(x => new SomePoco
{
SomeProperty = x.SomeProperty,
AnotherProperty = x.AnotherProperty,
});
If you must have an IQueryable<T>, then you should not use dynamic projections in the first place.
IQueryable<SomePoco> query =
dc.SomeTable
.Where("SomeProperty = \"foo\"")
.Select(x => new SomePoco
{
SomeProperty = x.SomeProperty,
AnotherProperty = x.AnotherProperty,
});
Seeing as how the cast is not working for LINQ to Entities, then I suppose the only option you have to get a strongly type collection of your POCO objects is to break this out into a loop.
var query = dc.SomeTable
.Where("SomeProperty = \"foo\"")
.Select("new (SomeProperty, AnotherProperty)");
var result = new List<SomePoco>();
foreach (dynamic x in query)
{
result.Add(new SomePoco
{
SomeProperty = x.SomeProperty,
AnotherProperty = x.AnotherProperty,
});
}

IN and NOT IN with Linq to Entities (EF4.0)

This has been ruining my life for a few days now, time to ask...
I am using Entity Framework 4.0 for my app.
A Location (such as a house or office) has one or more facilities (like a bathroom, bedroom, snooker table etc..)
I want to display a checkbox list on the location page, with a checkbox list of facilities, with the ones checked that the location currently has.
My View Model for the facilities goes like this...
public class FacilityViewItem
{
public int Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
So when im passing the Location View Model to the UI, i want to pass a List<T> of facilities where T is of type FacilityViewItem.
To get the facilities that the location already has is simple - i make a query using Location.Facilities which returns an EntityCollection where T is of type Facility. This is because Facilities is a navigation property....
var facs = from f in location.Facilities
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name,
Checked = true
};
So here is where my problem lies - i want the rest of the facilities, the ones that the Location does not have.
I have tried using Except() and Any() and Contains() but i get the same error.
Examples of queries that do not work...
var restOfFacilities = from f in ctx.Facilities
where !hasFacilities.Contains(f)
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name
};
var restOfFacilities = ctx.Facilities.Except(facilitiesThatLocationHas);
var notFacs = from e in ctx.Facilities
where !hasFacilities.Any(m => m.FacilityId == e.FacilityId)
select new FacilityViewItem()
{
Id = e.FacilityId,
Name = e.Name
};
And the error i get with every implementation...
System.NotSupportedException was unhandled
Message=Unable to create a constant value of type 'Chapter2ConsoleApp.Facility'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
What am i overlooking here?
ironically enough i solved it in a matter of hours after i posted the question on here, after days of suffering.
The error is basically saying 'i dont know how to calculate what items are not included by comparing strongly typed objects. Give me a list of Ints or some simple types, and i can take care of it'.
So, first you need to get a list of the primary keys, then use that in the contains clause...
//get the primary key ids...
var hasFacilityIds = from f in hasFacilities
select f.FacilityId;
//now use them in the contains clause...
var restOfFacilities = from f in ctx.Facilities
where !hasFacilityIds.Contains(f.FacilityId)
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name
};
The first query seems fine, but you need to compare the Ids:
var restOfFacilities = from f in ctx.Facilities
where !facs.Select(fac => fac.Id).Contains(f.Id)
select f;
I wanna see what's hasFacilities, anyway, as L2E shows, "Only primitive types ('such as Int32, String, and Guid') are supported in this context", so I suppose you must retrieve first the data and put into a collection of FacilityViewItem.
var restOfFacilities = ctx
.Facilities
.Where(f => !hasFacilities.Contains(f))
.Select(f => new { f.FacilityId, f.Name })
.ToList()
.Select(f => new FacilityViewItem {
Id = f.FacilityId,
Name = f.Name
});
var notFacs = ctx
.Facilities
.Where(e => !hasFacilities.Any(m => m.FacilityId == e.FacilityId))
.Select(e => new { e.FacilityId, e.Name })
.ToList()
.Select(e => new FacilityViewItem {
Id = e.FacilityId,
Name = e.Name
});
hope it helps

Linq to NHibernate - select count problem

Given the classes A and B where
class A
{
string Name;
Ilist<B> BList;
}
class B
{
string Name;
}
With FluentNH mapping, relationship is many-to-many which is HasManyToMany(x => x.B) for A. B has no reference to A. NH version is 2.1.2.4000.
What should be the linq query to select the collection where each row contains B.Name and count of A's containing that B? Result must be the List of anonymous type who has 2 fields: Name and Count. Result also should include all B's, hence it should be outer join.
My intend is to get the result with minimum round-trips to database, possibly in one go.
If you want to do it in Linq in one hit in code, you could do this...
var result = Session.Linq<A>()
.SelectMany(a => a.BList, (a, b) => new { b.Name, A = a.Id })
.ToList()
.GroupBy(x => x.Name)
.Select(x => new { Name = x.Key, Count = x.Count() })
.ToList();
NHibernate.Linq (2.1.2.4000) can't handle a GroupBy after a SelectMany it seems, so the first ToList pulls all the data into memory. This is inefficient -- a SQL count would be better.
Alternatively, you could add a lazy loaded collection to your B class that goes back to A. If you're using a many-to-many table in the middle, that should be easy.
public class B
{
public virtual string Name { get; set; }
public virtual IList<A> AList { get; private set; }
}
Your query simply becomes...
var result = Session.Linq<B>()
.Where(b => b.AList.Count > 0)
.Select(b => new { b.Name, b.AList.Count }
.ToList();
Which produces very efficient SQL from Linq (using a count) and gives the same result.

Resources