NHibernate ICriterion .NET delegate mismatch - c#

I'm using NHibernate in a repository pattern with DI to keep things flexible. I don't want the associated interfaces to expose anything vendor specific like ICriterion. But, I want my query class to accept a delegate that I can put into the .Where clause.
I need something like:
public IEnumerable<MyClass> Execute(Func<MyClass, bool> selector)
{
return session
.QueryOver<MyClass>()
.Where(selector)
.....
}
Is there a way to achieve this or am I going about it in the wrong way?

You are not going the wrong way. I think your code works ; your problem being that you retrieve all your table in memory where filtering is done.
I guess this should enable filtering at DB level for criteria which can be translated to SQL :
public IEnumerable<MyClass> Execute(Expression<Func<MyClass, bool>> selector)
{
return session.Query<MyClass>().Where(selector).ToList();
}
and I bet this should work too :
public IEnumerable<MyClass> Execute(Expression<Func<MyClass, bool>> selector)
{
return session.QueryOver<MyClass>().Where(selector).ToList();
}
Hope this will help

Related

Passing a predicate that was passed in as a parameter

I'm having trouble with passing in a predicate to another function. This predicate would have been passed in as a parameter that is trying to call the second function. Below is a code snippet.
public IEnumerable<ViewModel> BuildModel<TPart, TRecord>(Expression<Func<TRecord, bool>> predicate)
where TPart : ContentPart<TRecord>
where TRecord : ContentPartRecord
{
IEnumerable<ReportPart> items = GetList<ReportPart, ReportRecord>(predicate);
This issue is the predicate parameter, on the call to GetList() it keeps erroring, saying the call has some invalid arguments. The Get list call is:
public IEnumerable<TPart> GetList<TPart, TRecord>(Expression<Func<TRecord, bool>> predicate)
where TPart : ContentPart<TRecord>
where TRecord : ContentPartRecord
I been trying changing the parameter a bunch of different ways trying to get this to work but I haven't had any success. Maybe I'm not understanding why the compiler thinks that 'predicate' is different than what GetList() is expecting.
EDIT: more information
ReportPart : ContentPart<ReportRecord>
ReportRecord : ContentPartRecord
ContentPart and ContentPartRecord are both base classes
Caller to BuildModels
List<ReportViewModel> model = _service.BuildReports<ReportPart, ReportRecord>(x => x.Id == 1).ToList();
BuildModels
public IEnumerable<ReportViewModel> BuildReports<TPart, TRecord>(System.Linq.Expressions.Expression<Func<TRecord, bool>> predicate)
where TPart : ContentPart<TRecord>
where TRecord : ContentPartRecord
{
List<ReportViewModel> model = new List<ReportViewModel>();
IEnumerable<ReportPart> reportParts = GetList<ReportPart, ReportRecord>(predicate);
//do some stuff with reportParts
return model;
}
}
GetList
public IEnumerable<TPart> GetList<TPart, TRecord>(System.Linq.Expressions.Expression<Func<TRecord, bool>> filter)
where TPart : ContentPart<TRecord>
where TRecord : ContentPartRecord
{
return filter == null ?
Services.ContentManager.Query<TPart, TRecord>().List() :
Services.ContentManager.Query<TPart, TRecord>().Where(filter).List();
}
Without a good, minimal, complete code example, it's impossible to know for sure what the best fix for your problem is, assuming one exists at all.
That said, variance problems generally come in two flavors: 1) what you're doing is truly wrong and the compiler is saving you, and 2) what you're doing is not provably correct, so you have to promise the compiler you know what you're doing.
If you're in the first category, then all is lost. You can't get this to work.
But if you're in the second category, you may be able to get your call to work by wrapping the original predicate in a new one that is compatible with the called method's requirements:
IEnumerable<ReportPart> items =
GetList<ReportPart, ReportRecord>(r => predicate((TRecord)r));
That said, while it's possible that there's some important reason for you writing the code this way, given the little bit of code you've shown so far, it's not really clear why you're trying to take the generic predicate and force it into the specific type.
Depending on what's really going on in the rest of the code, a pair of generic methods like this would work better where you either 1) go fully generic (i.e. don't force the type as ReportRecord in the call to GetList()), or 2) you don't bother with the generic types at all (i.e. leave out TPart and TRecord from the BuildModel() method).
Example of 1):
public IEnumerable<ViewModel> BuildModel<TPart, TRecord>(
Expression<Func<TRecord, bool>> predicate)
where TPart : ContentPart<TRecord>
where TRecord : ContentPartRecord
{
IEnumerable<TPart> items = GetList<TPart, TRecord>(predicate);
}
Example of 2):
public IEnumerable<ViewModel> BuildModel(
Expression<Func<ReportRecord, bool>> predicate)
{
IEnumerable<ReportPart> items = GetList<ReportPart, ReportRecord>(predicate);
}
The mixing and matching, even if you can get it to work correctly, is often a sign that there's a more fundamental problem in the architecture, where generics are either being used where they shouldn't be, or are not being taken advantage as well as they should be.
If the above does not get you back on track, you really should improve the question by providing a minimal, complete code example.

Entity Framework 5 where returning whole dataset before filtering results

I have an abstract class which gets implemented for all of my repositories - within there I have a property which returns a collection:
protected readonly IDbSet<T> _dbSet;
public virtual IEnumerable<T> GetSelection(Func<T, bool> where)
{
return _dbSet.Where(where);
}
Repo:
public class PostcodeDataRepository : EntityRepositoryBase<PostcodeData>, IPostcodeDataRepository
{
// Constructor in here
}
Now this works great:
var postcodeData = postcodeRespoistory.GetSelection(x=>x.Postcode == "SW1A 2TT");
(Yes it's uk postcode data, and yes there are nearly 2m rows in the table)
This works great but my problem is returning all of the data only for the application to then filter it is causing some performance issues (as you would expect!). I used from MiniProfiler and EFProf to confirm that it is effectively doing a select * from PostcodeData which isn't what I want it to do.
Has anyone any ideas how I can resolve this?
You need to use the Expression<Func<TSource, bool>> predicate:
public IEnumerable<T> GetSelection(Expression<Func<T, Boolean>> predicate)
{
return _dbSet.Where(where);
}

ObjectSet's Where() method and memory

I'm using an ObjectSet's methods to do various queries in a table, namely, selecting a few records from it as such:
var results = Repository.Find(c => c.Enabled == 1).ToList();
Here is the Find method of my repository:
public IEnumerable<T> Find(Func<T, bool> predicate)
{
try
{
return _objectSet.Where<T>(predicate);
}
catch
{
throw;
}
}
Now, if there is about 1,000,000 records in the targeted table, I can see the process's memory usage grow quite a lot even if the Find call I'm doing should return a few records at most.
It seems all the records are pulled client side, then filtered. This is obviously not what I want LINQ to do.
Do you see anything obviously wrong with what I'm doing ?
Thanks in advance.
I think you should use Expression<Func<T, bool>> instead of a plain Func<T, bool>:
public IEnumerable<T> Find(Expression<Func<T, bool>> predicate) {
// your code
}
Where is overloaded (see ObjectSet Class), and the Func<T>-overload is defined by IEnumerable<T>, whereas Expression<TDelegate> is used by IQueryable<T>. Because predicate is a Func<T> the compiler invokes the extension method defined for IEnumerable<T>, which in turn fetches all the records and does LINQ to objects.

LINQ-to-entities casting issue

I'm trying to filter a LINQ-to-entities query in a generic way, but I keep getting an error. Here is a piece of code:
private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
{
var deletableEntities = (IQueryable<IDeletable>)entities;
deletableEntities = deletableEntities.Where(entity => !entity.Deleted);
entities = (IQueryable<T>)deletableEntities;
}
return entities;
}
Basically I'm trying to filter out deleted entities (i.e. 'Deleted' field is 'true'), if and only if the entity is IDeletable (i.e. it has the 'Deleted' field). The problem is that I can't cast IQueryable< IDeletable > back to IQueryable< T >.
Any ideas on how to fix this? And before you ask: yes, this method has to be generic.
Thanks in advance!
But you can use Cast<T>() to convert it.
entities = deletableEntities.Cast<T>();
You could also use it to case to IDeletable as well, for example,
private IEnumerable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
{
return entities.ToList()
.Cast<IDeletable>()
.Where( e => !e.Deleted )
.Cast<T>();
}
return entities.ToList();
}
I was able to solve my problem by doing this:
private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
{
var deletableEntities = (IQueryable<IDeletable>)entities;
return deletableEntities.Where(entity => !entity.Deleted).Cast<T>();
}
return entities;
}
Thanks to tvanfosson for the inspiration.
If you can assume that no one will need to call this method with T that does not implement IDeletable, you can restrict T:
private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities) where T : IDeletable
As a bonus, you won't need to cast anything or use reflection to test for IDeletable.

How to OrderBy on a generic IEnumerable (IEnumerable<T>) using LINQ in C#?

In my generic repository I have below method:
public virtual IEnumerable<T> GetAll<T>() where T : class
{
using (var ctx = new DataContext())
{
var table = ctx.GetTable<T>().ToList();
return table;
}
}
T is a Linq to Sql class and I want to be able to OrderBy on a particular property (i.e. int SortOrder). Say if T has property name "SortOrder" then do OrderBy on this property. But I am not sure how I can achieve this. So I need some helps. Thank you! I feel like dynamic languages really shines in doing this kind of jobs!
Quote from ScottGu:
While writing type-safe queries is
great for most scenarios, there are
cases where you want the flexibility
to dynamically construct queries on
the fly
And this is exactly the problem I am facing and I am wondering if this linq dynamic helper can be made into official .NET library.
public IEnumerable<T> GetAll<T,K>(Expression<Func<T,K>> sortExpr)
{
using (var ctx = new DataContext())
{
ctx.ObjectTrackingEnabled = false;
var table = ctx.GetTable<T>().OrderBy(sortExpr).ToList();
return table;
}
}
Usage:
var t = GetAll<Foo, int>(x => x.Bar);
Unfortunately, you have to supply the type of the key. Unless you start messing with the expressions.
To get this to work you would need to define a constraint. For example:
public interface IOrderable
{
int SortOrder { get; }
}
public virtual IEnumerable<T> GetAll<T>() where T : class, IOrderable
{
...
}
Now you can use ts.OrderBy(t => t.SortOrder) inside the method body. All the classes you intend to use with this method must then implement this interface.
However as you pointed out LINQ To SQL classes do not implement this interface. I would recommend that you do not take this approach if using LINQ to SQL. With LINQ to SQL it already is very easy to fetch all objects from a table and there is an easy way to order these objects. If you learn to use the provided interfaces correctly your queries will also be much faster and use less memory because you get the database to do filtering for you instead of filtering on the client.
You can't do it generically, unless you constrain the type parameter T to a type that has the property you want to sort on. For instance, if you have an interface named IHaveTheProperty, then you can do:
public virtual IEnumerable<T> GetAll<T>() where T : class, IHaveTheProperty
{
using (var ctx = new DataContext())
{
ctx.ObjectTrackingEnabled = false;
var table = ctx.GetTable<T>().ToList().AsReadOnly().OrderBy(t=>t.TheProperty);
return table;
}
}
But without a constraint, you can only use the methods of the System.Object type.
LINQ to SQL classes are created partial. That means that you can create another class part to force one of those classes to implement your interface:
public partial class Address : IHaveTheProperty
{
}
You could use the dynamic Linq library that would let you construct the OrderBy dynamically: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Personally I always have my repositories return IQueryable then the consumer can handle building up additional filtering, ordering or projections as needed.
If I was asked to do this I could do something like this:
public virtual IEnumerable<T> GetAll<T>() where T : class
{
using (var ctx = new DataContext())
{
var table = ctx.GetTable<T>().ToList();
return table;
}
}
And
publuc virtual IEnumerable<T> GetAll<T>(Func<T,bool> orderByClause) where T:class{
return GetAll<T>().OrderBy(orderByClause);
}
or in .net 4 make the parameter optional and accomplish the same in a single method. You will need a predicate builder like logic to help your client code build delegates on the fly. The link by Jeffrey on my comment kinda illustrates all this stuff! And this link from the extended material from C#3.0 in a nutshell is cool too! If you need to crank the code quick they even have this LinqKit
this should do the trick:
myEnumeration.OrderBy( itm => itm.SortOrder )

Categories