Implement the Select for a custom LINQ provider - linq

I understand pretty the way implementing Linq to something however there is a little problem that I encounter here is the situation, I'm implementing a Linq to Tree object the tree it self is simple Tree implementation so I started by creating the Queryable object
public class TreeContext<T> : IOrderedQueryable where T :IComparable<T>
{
private Tree<T> _root;
private Expression _expression;
private IQueryProvider _provider;
public TreeContext(Tree<T> root)
{
_root = root;
Provider = new TreeProvider<T>(_root);
}
public TreeContext(IQueryProvider provider, Expression expression)
{
Provider = provider;
Expression = expression;
}
#region Model
public IEnumerable<TResult> Select<TResult>(Func<T,TResult> predicate)
{
var param = predicate.Method.GetParameters()[0];
return null;
}
public IEnumerable<T> Where(Func<T, bool> predicate)
{
Provider.Execute<IEnumerable>(Expression);
return null;
}
#endregion
public Expression Expression
{
private set { _expression = value; }
get { return _expression; }
}
public Type ElementType => typeof(T);
public IQueryProvider Provider
{
private set { _provider = value; }
get { return _provider; }
}
public IEnumerator<T> GetEnumerator()
=> GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=>Provider.Execute<IEnumerable>(Expression).GetEnumerator();
}
Then I implemented the QueryProvider and here resides the problem, exactly at the select method implementation
public class TreeProvider<T> : IQueryProvider where T:IComparable<T>
{
private Tree<T> _root;
public TreeProvider(Tree<T> root)
=> _root = root;
public IQueryable CreateQuery(Expression expression)
=> new TreeContext<T>(this, expression);
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
=>throw new NotImplementedException();
public object Execute(Expression expression)
=> new TreeQueryContext<T>(_root).Execute(expression);
public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}
}
The select implementation is not provided for this custom provider so that I can write a request like
from el in context select el
I added a method select
IEnumerable Select<T,TResult>(Func<T,TResult> selector)
The problem is that I didn't find a way the transform this selector to an expression so that I pass it to the execute method of the QueryProvider
Maybe I didn't understand well how to develop a custom LINQ to custom provider and I didn't find any good and complete tutorial on that in C#

If you want to create LINQ Provider - you have to deal always with Expression Tree to create and parse query.
Select and Where in your TreeContext are not needed at all. Standard LINQ Extensions has these methods and will automatically prepare Expression Tree for you. Focus on TreeProvider implementation.
Since you are new in this area, I would suggest to base your LINQ Provider on Relinq. It will simplify your life from start and will show possible ways for implementation. It has base classes for Provider and Query implementation, but it is not required to derive from them.
Just try
var queryParser = new QueryParser(new ExpressionTreeParser(
ExpressionTreeParser.CreateDefaultNodeTypeProvider(),
ExpressionTreeParser.CreateDefaultProcessor(
ExpressionTransformerRegistry.CreateDefault(),
null)));
var queryModel = parser.GetParsedQuery(expression);
And Relinq will give you QueryModel, which is much easier to parse.

Related

Do I need to impliment UnitOfWork for EF Core and if so is there a standard pattern somewhere?

I have read in some places that EF already implements it's own UnitOfWork and transactions.
I am looking at the following solutions:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
I don't really like this because I don't want to have to manually add in every type of repo I have as that goes against the reason for the GenericRepository which I've already put in a lot of work to making generic already.
Also looking at the UnitOfWork Attribute solution described here but backing away because of the reasons discussed by the author himself:
Entity Framework Core 1.0 unit of work with Asp.Net Core middleware or Mvc filter
But let me try to lay out my question in the discussion below.
I have a Generic Repo and a Generic Service. They are registered in my Startup like this:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
My Generic Repo looks like this (leaving out Interfaces for brevity):
public enum FilteredSource
{
All,
GetAllIncluding,
}
public class GenericRepository<T> : IGenericRepository<T>
where T: BaseEntity
{
protected readonly ApplicationDbContext _context;
protected DbSet<T> _dbSet;
public GenericRepository(ApplicationDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
// no eager loading
private IQueryable<T> All => _dbSet.Cast<T>();
// eager loading
private IQueryable<T> GetAllIncluding(
params Expression<Func<T, object>>[] includeProperties) =>
includeProperties.Aggregate(All, (currentEntity, includeProperty) => currentEntity.Include(includeProperty));
// eager loading
public async Task<T> GetSingleIncludingAsync(
long id, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> entities = GetAllIncluding(includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return await entities.SingleOrDefaultAsync(e => e.Id == id);
}
// no eager loading
public async Task<T> GetSingleIncludingAsync(long id)
{
return await _dbSet.SingleOrDefaultAsync(e => e.Id == id);
}
/// <summary>
/// Takes in a lambda selector and let's you filter results from GetAllIncluding or All.
/// </summary>
/// <param name="selector">labmda expression to filter results by.</param>
/// <param name="getFilteredSource">All or GetAllIncluding as the method to get results from.</param>
/// <param name="includeProperties">array of eager load lamda expressions.</param>
/// <returns></returns>
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All.Where(selector);
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties).Where(selector);
break;
}
return await results.AsQueryable().ToListAsync();
}
public async Task<IEnumerable<T>> GetUnFiltered(
FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All;
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties);
break;
}
return await results.AsQueryable().ToListAsync();
}
public async Task<T> InsertAsync(T entity)
{
if (entity == null)
{
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Insert");
}
await _dbSet.AddAsync(entity);
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
T entityToUpdate = await
_dbSet.AsNoTracking().SingleOrDefaultAsync(e => e.Id == entity.Id);
if (entityToUpdate == null)
{
//return null;
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Update");
}
_dbSet.Update(entity);
return entity;
}
public async Task<T> DeleteAsync(T entity)
{
_dbSet.Remove(entity);
return await Task.FromResult(entity);
}
public Task SaveAsync() => _context.SaveChangesAsync();
}
Service Layer looks like this:
public class GenericService<T> : IGenericService<T>
where T : BaseEntity
{
private IGenericRepository<T> _genericRepo;
public GenericService(IGenericRepository<T> genericRepo)
{
_genericRepo = genericRepo;
}
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
return await _genericRepo.GetFiltered(selector, filteredSource,
includeProperties);
}
public async Task<IEnumerable<T>> GetUnFiltered(
FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
return await _genericRepo.GetUnFiltered(filteredSource,
includeProperties);
}
// no eager loading
public async Task<T> GetSingleIncludingAsync(long id)
{
return await _genericRepo.GetSingleIncludingAsync(id);
}
// eager loading
public async Task<T> GetSingleIncludingAsync(long id, params Expression<Func<T, object>>[] includeProperties)
{
T entity = await _genericRepo.GetSingleIncludingAsync(id, includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return entity;
}
public async Task<T> InsertAsync(T entity)
{
var result = await _genericRepo.InsertAsync(entity);
await _genericRepo.SaveAsync();
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
var result = await _genericRepo.UpdateAsync(entity);
if (result != null)
{
await _genericRepo.SaveAsync();
}
return result;
}
public async Task<T> DeleteAsync(T entity)
{
throw new NotImplementedException();
}
}
An example of a MVC Core Web API controller that uses the service looks like this:
[Route("api/[controller]")]
public class EmployeesController : Controller
{
private IGenericService<Employee> _genericService;
public EmployeesController(IGenericService<Employee> genericService)
{
_genericService = genericService;
}
// GET: api/employees
[HttpGet]
public async Task<IEnumerable<Employee>> GetEmployeesAsync(
string firstName = null, string lastName = null)
{
return await _genericService.GetFiltered(
e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
&& (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName)),
FilteredSource.GetAllIncluding,
new Expression<Func<Employee, object>>[] { a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit }
);
}
// GET api/employees/5
[HttpGet("{id}", Name = "GetEmployeeById")]
public async Task<IActionResult> GetEmployeeByIdAsync(long id)
{
var employee = await _genericService.GetSingleIncludingAsync(id,
a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit);
if (employee == null)
{
return NotFound();
}
else
{
return new ObjectResult(employee);
}
}
// PUT api/employees/id
[HttpPut("{id}")]
public async Task<IActionResult> PutEmployeeAsync([FromBody] Employee emp)
{
var employee = await _genericService.UpdateAsync(emp);
if (employee == null)
{
return NotFound();
}
return new ObjectResult(employee);
}
}
So here are my questions:
The way I understand it, UnitOfWork is used in case you bring in two repositories into a service or controller. If you manipulate data in repo 1 and it passes, and then manipulate data in repo 2 and it fails (or visa versa) you want to roll everything back.
So far I am not using two repos. But if this situation occurs, it will be because I am bringing in two GenericServices to a controller, which will in turn bring in two GenericRepos.
Now lets talk about scope.
I have to bring in my GenericRepo as Scoped. Not Singleton. Because if I query an object on one request, and then try to update that object on the next request, I will get an error that the object cannot be updated because it is already being tracked by the Singleton Repo from the previous request.
So I bring it in Scoped as shown in the StartUp segment:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
I also bring in the service as scoped. Pretty much guessing I should bring it in as scoped also.
Now, if I bring in a GenericService of Type Employee -> which gets a GenericRepository of Type Employee into a controller and I also bring in GenericService of Type Case -> which gets a GenericRepository of Type Case, are these two different GenericRepos? Or are they the same Repo?
Will they be treated as the same transaction and all pass or fail together?
Or do I need to manually implement UnitOfWork?
Another factor I think that goes into this is whether the following baked into Core DI line is Singleton or Scoped:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyConn")));
You are right to use Scoped but see below: it's your ApplicationDbContext that must be scoped, the services and repos can be transient.
Since IGenericRepo<Employee> is a different type to IGenericRepo<Case> yes you will get two different GenericRepo<T>'s (and the same logic applies to the services).
But about the UnitOfWork pattern in the article you link, I don't get your comment that "I don't really like this because I don't want to have to manually add in every type of repo ..."
You could change the UoW code in the article to a GenericUnitOfWork<T1,T2> ?
Or you could take the view that for each controller which needs to write to 2 repos, you write a UoW class specifically for it. Note that you can strip out the lazy getter code that the article uses in its UoW class, because your repos are already created for you anyway by the services container, so the UoW reduces to only a few lines of mostly-boiler code anyway.
public class UnitOfWork<T1,T2> : IDisposable
{
ApplicationDbContext context;
GenericRepo<T1> Repo1 {get; private set;}
GenericRepo<T2> Repo2 {get; private set;}
public UnitOfWork(ApplicationDbContext context)
{
this.context=context;
Repo1 =new GenericRepo<T1>(context)
Repo2 =new GenericRepo<T2>(context)
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
But here's the important thing: the UoW pattern as presented in the article depends crucially on both repos using the same DbContext (otherwise it would be a distributed transaction and requires more code)
So it's important that your ApplicationDbContext is scoped:
services.AddScoped(typeof(ApplicationDbContext), typeof(ApplicationDbContext));
However, you do have to be sure that every controller in your application can happily accept this constraint that it should only need one underlying ApplicationDbContext. For must cases, it should be fine.
Finally, you might as well be explicit that the real dependency is the DbContext:
public class EmployeesController : Controller
{
UnitofWork<T1,T2> uow;
public EmployeesController(ApplicationDbContext dbContext)
{
this.uow= new UnitofWork<Employee,Case>(dbContext)
}
//...

Lambda expression check if null helper

I want to be able to write
MyObject.IsNull(p => p.MyObjectProperty)
I think it is achievable with expression.
I thied to implement it this way:
public static void IsNull<T>(this T root, Expression<Func<T, object>> expression)
{
if (CheckIfNull<T>(expression))
{
throw new ArgumentNullException(GetName(expression));
}
}
private static string GetName<T>(Expression<Func<T, object>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
public static bool CheckIfNull<T>(Expression<Func<T, object>> expression)
{
Expression<Func<T, object>> obj = o => expression.Compile().Invoke(o);
return obj == null;
}
But it is seams to be not working.
How can I fix that?
You have a bug:
The comparison obj == null should be obj(root) == null - of course you have to pass root as an argument to CheckIfNull.
The former comparison will always evaluate to false, since you are effectively comparing o => expression.Compile().Invoke(o) with null - they are never equal. You rather want to compare the result of the call to Invoke with null.
All my suggestions combined:
public static bool CheckIfNull<T>(this T root, Expression<Func<T, object>> expression)
{
return expression.Compile()(root) == null;
}
public static void IsNull<T>(this T root, Expression<Func<T, object>> expression)
{
if (root.CheckIfNull<T>(expression))
{
throw new ArgumentNullException(GetName(expression));
}
}
private static string GetName<T>(Expression<Func<T, object>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
Further comments:
I'm not sure ArgumentNullException is the right exception for this situation. But without knowledge about your scenario, it is hard to suggest something better. Actually:
it seems weird to write an extension method that merely throws an exception if a member is null, especially for a method named IsNull, which is why
I would rename IsNull to ThrowIfNull and rename CheckIfNull to IsNull

JavaFX Table Cell Formatting

TableColumn<Event,Date> releaseTime = new TableColumn<>("Release Time");
releaseTime.setCellValueFactory(
new PropertyValueFactory<Event,Date>("releaseTime")
);
How can I change the format of releaseTime? At the moment it calls a simple toString on the Date object.
If you want to preserve the sorting capabilities of your TableColumn, none of the solutions above is valid: if you convert your Date to a String and show it that way in your TableView; the table will sort it as such (so incorrectly).
The solution I found was subclassing the Date class in order to override the toString() method. There is a caveat here though: the TableView uses java.sql.Date instead of java.util.Date; so you need to subclass the former.
import java.text.SimpleDateFormat;
public class CustomDate extends java.sql.Date {
public CustomDate(long date) {
super(date);
}
#Override
public String toString() {
return new SimpleDateFormat("dd/MM/yyyy").format(this);
}
}
The table will call that method in order to print the date.
Of course, you need to change too your Date class in the TableColumn declaration to the new subclass:
#FXML
TableColumn<MyObject, CustomDate> myDateColumn;
Same thing when you attach your object attribute to the column of your table:
myDateColumn.setCellValueFactory(new PropertyValueFactory< MyObject, CustomDate>("myDateAttr"));
And finally, for the shake of clarity this is how you declare the getter in your object class:
public CustomDate getMyDateAttr() {
return new CustomDate(myDateAttr.getTime()); //myDateAttr is a java.util.Date
}
It took me a while to figure out this due to the fact that it uses java.sql.Date behind the scenes; so hopefully this will save other people some time!
Update for Java FX8:
(I'm not sure it is the good place for that answer, but I get the problem in JavaFX8 and some things have changed, like java.time package)
Some differences with the previous answers:
I keep the date type on the column, so I need to use both cellValueFactory and cellFactory.
I Make a generic reusable method to generate the cellFactory for all date columns.
I use java 8 date for java.time package! But the method could be easily reimplemented for java.util.date.
#FXML
private TableColumn<MyBeanUi, ZonedDateTime> dateColumn;
#FXML
public void initialize () {
// The normal binding to column
dateColumn.setCellValueFactory(cellData -> cellData.getValue().getCreationDate());
//.. All the table initialisation and then
DateTimeFormatter format = DateTimeFormatter .ofLocalizedDate(FormatStyle.SHORT);
dateColumn.setCellFactory (getDateCell(format));
}
public static <ROW,T extends Temporal> Callback<TableColumn<ROW, T>, TableCell<ROW, T>> getDateCell (DateTimeFormatter format) {
return column -> {
return new TableCell<ROW, T> () {
#Override
protected void updateItem (T item, boolean empty) {
super.updateItem (item, empty);
if (item == null || empty) {
setText (null);
}
else {
setText (format.format (item));
}
}
};
};
}
The advantages are that:
The column is typed with a "java8 Date" to avoid the sort problem evoqued by #Jordan
The method "getDateCell" is generic and can be used as an util function for all Java8 Time types (Local Zoned etc.)
I'd recommend using Java generics to create re-usable column formatter that takes any java.text.Format. This cuts down on the amount of boilerplate code...
private class ColumnFormatter<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
private Format format;
public ColumnFormatter(Format format) {
super();
this.format = format;
}
#Override
public TableCell<S, T> call(TableColumn<S, T> arg0) {
return new TableCell<S, T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
setGraphic(new Label(format.format(item)));
}
}
};
}
}
Examples of usage
birthday.setCellFactory(new ColumnFormatter<Person, Date>(new SimpleDateFormat("dd MMM YYYY")));
amplitude.setCellFactory(new ColumnFormatter<Levels, Double>(new DecimalFormat("0.0dB")));
I needed to do this recently -
dateAddedColumn.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<Film, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Film, String> film) {
SimpleStringProperty property = new SimpleStringProperty();
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
property.setValue(dateFormat.format(film.getValue().getCreatedDate()));
return property;
}
});
However - it is a lot easier in Java 8 using Lamba Expressions:
dateAddedColumn.setCellValueFactory(
film -> {
SimpleStringProperty property = new SimpleStringProperty();
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
property.setValue(dateFormat.format(film.getValue().getCreatedDate()));
return property;
});
Hurry up with that Java 8 release oracle!
You can accomplish that through Cell Factories. See
https://stackoverflow.com/a/10149050/682495
https://stackoverflow.com/a/10700642/682495
Although the 2nd link is about ListCell, the same logic is totally applicable to TableCells too.
P.S. Still if you need some sample code, kindly will attach here.
An universal solution could be as simple as that:
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
public interface AbstractConvertCellFactory<E, T> extends Callback<TableColumn<E, T>, TableCell<E, T>> {
#Override
default TableCell<E, T> call(TableColumn<E, T> param) {
return new TableCell<E, T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(convert(item));
}
}
};
}
String convert(T value);
}
And its sample usage:
TableColumn<Person, Timestamp> dateCol = new TableColumn<>("employment date");
dateCol.setCellValueFactory(new PropertyValueFactory<>("emploumentDateTime"));
dateCol.setCellFactory((AbstractConvertCellFactory<Person, Timestamp>) value -> new SimpleDateFormat("dd-MM-yyyy").format(value));
This is what i did and i worked perfectly.
tbColDataMovt.setCellFactory((TableColumn<Auditoria, Timestamp> column) -> {
return new TableCell<Auditoria, Timestamp>() {
#Override
protected void updateItem(Timestamp item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
}
}
};
});
You can easily pipe Properties of different type and put a formatter or converter in between.
//from my model
ObjectProperty<Date> valutaProperty;
//from my view
TableColumn<Posting, String> valutaColumn;
valutaColumn.setCellValueFactory(
cellData -> {
SimpleStringProperty property = new SimpleStringProperty();
property.bindBidirectional(cellData.getValue().valutaProperty, new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN));
return property;
});
The StringConverter classes are another mechanism.
TextFieldTableCell has a constructor as follows: public TextFieldTableCell(StringConverter<T> converter).
... and StringConverters consist of subclasses such as LocalDateStringConverter. A default implementation would then be:
new TextFieldTableCell( new LocalDateStringConverter() );
... this is not bad, but the parameter-less LocalDateStringConverter uses dates of the format 'dd/mm/yyyy' both for parsing (fromString() method) and toString(). But there are other constructors where you can pass a FormatStyle or DateTimeFormatter.
From my experiments, however, StringConverters are slightly problematic in that it is difficult to catch the DateTimeParseException thrown by fromString() with an invalid date.
This can be remedied by creating your own StringConverter class, e.g.:
class ValidatingLocalDateStringConverter extends LocalDateStringConverter {
boolean valid;
#Override
LocalDate fromString(String value) {
valid = true;
if (value.isBlank()) return null;
try {
// NB wants ISO
return LocalDate.parse( value );
} catch ( DateTimeParseException e) {
valid = false;
}
return null;
}
#Override
String toString( LocalDate date ){
// NB returns ISO or the String "null" with null date value (!)
String s = date.toString();
return s.equals( 'null' )? '' : s;
}
}
Using this StringConverter solution will mean that dates are sorted according to chronological order, regardless of the String representation.

EF4 CTP5 - LINQ Dynamic Query Library throws InvalidCastException

With the upgrade to EF4 CTP5, the previously working (with CTP4) LINQ Dynamic Query Library throws the following exception
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery' to type 'System.Linq.IQueryable`1[KIT.TAM.Core.Entities.TravelAgent]'.
on the return statement below:
namespace System.Linq.Dynamic
{
public static class DynamicQueryable
{
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
{
return (IQueryable<T>)Where((IQueryable)source, predicate, values);
}
}
}
Is there an updated version of the library that works with EF4 CTP5?
Thanks folks.
Solved this one. In DynamicLibrary.cs:
I replaced
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
{
return (IQueryable<T>)Where((IQueryable)source, predicate, values);
}
with
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values);
return source.Provider.CreateQuery<T>(
Expression.Call(
typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(lambda)));
}
This is the basically the same code in
public static IQueryable Where(this IQueryable source, string predicate, params object[] values)
but changed source.Provider.CreateQuery() to source.Provider.CreateQuery<T>.
You will also have to do this for the static method OrderBy<T>.

Why does hard coding a function expression speed up query by four minutes?

I am using Dane Morgridge's repository code as a parent to my repository class. In the parent class--EFRepository--there is a method that calls an ObjectSet's Where clause passing in a Func method. Upon calling this code and then assigning it to my grid, the process takes 4 minutes. However, if I hard code the call to the ObjectSet's Where it only takes three seconds. Any ideas why? It seems like the compiler is messing it up somehow.
private void button1_Click(object sender, RoutedEventArgs e)
{
IQueryable<PRODDATA> testP = test.Repository.Find(w => w.PCUST == 49 && w.PDTTK == 20101030);
DateTime firstDate = System.DateTime.Now;
//This is where it takes the most time when passing in the expression above. When the espression is hardcoded (see below) it speeds it up considerably.
radGridView1.ItemsSource = testP;
DateTime secondDate = System.DateTime.Now;
}
public class EFRepository<T> : IRepository<T> where T : PRODDATA
{
public IUnitOfWork UnitOfWork { get; set; }
private IObjectSet<T> _objectset;
private IObjectSet<T> ObjectSet
{
get
{
if (_objectset == null)
{
_objectset = UnitOfWork.Context.CreateObjectSet<T>();
}
return _objectset;
}
}
public virtual IQueryable<T> All()
{
return ObjectSet.AsQueryable();
}
public IQueryable<T> Find(Func<T, bool> expression)
{
//Hardcoding this only takes 2 seconds.
//return ObjectSet.Where(w => w.PCUST == 49 && w.PDTTK == 20101030).AsQueryable();
//passing expression takes 4 minutes.
return ObjectSet.Where(expression).AsQueryable();
}
public void Add(T entity)
{
ObjectSet.AddObject(entity);
}
public void Delete(T entity)
{
ObjectSet.DeleteObject(entity);
}
public void Save()
{
UnitOfWork.Save();
}
}
Because Find takes a Func<T,bool> instead of an Expression<Func<T,bool>>. Presumably, this query is being sent to a DB engine, since it is operating on an IQueryable<T>. But if the expression is passed as a delegate and not a true expression, the LINQ-to-whatever-DB layer is not able to inspect the expression and turn it into SQL. This is resulting in the entire data set being sent from the db server to the C# code, where the "where" expression is then applied in the CLR instead of in the DB.
Change the signature of Find from
public IQueryable<T> Find(Func<T, bool> expression)
to
public IQueryable<T> Find(Expression<Func<T, bool>> expression)
Then see how it performs.

Resources