Having IQueryable<T> returned from Linq-2-SQL data context is it possible to wrap it with custom IQueryable<T> implementation that on execution stage (for instance, while enumeration) will handle underlying IQueryProvider's NotSupportedException and failback to enumerating whole data context and applying Expression using in-memory IQueryProvider (System.Linq.EnumerableQuery<T>)?
Code example:
IQueryable<User> users = usersRepository.GetAll();
// linq-to-sql can handle it
users.Where(u => u.Id > 10).ToList();
// linq-to-sql IQueryProvider will throw NotSupportedException in run-time,
// because ComplexFilter body was not captured as Expression
users.Where(u => ComplexFilter(u)).ToList();
// will succeed, because EnumerableQuery do not need Expression Tree
users.AsEnumerable().Where(u => ComplexFilter(u)).ToList();
public static bool ComplexFilter(User user)
{
return user.Id + user.Name.Length > 12;
}
I looking for way to wrap IQueryable that will automatically failback to IEnumerable if underlying IQueryProvider throws NotSupportedException:
IQueryable<User> safeUsersProxy = new SafeUsersProxy<User>(users);
// I want it to try underlying linq-to-sql provider first
// and then automatically failback to IEnumerable and return
// the result anyway (do not care about whole data enumerating in such cases)
safeUsersProxy.Where(u => ComplexFilter(u)).ToList();
The point
This will allow convenient way to execute all queries on DB level whenever possible, and only if Linq-to-SQL was unable to convert Expression into the SQL use slow EnumerableQuery fetching the whole dataset.
Now, I always thought that if someone asks you some rope to hang himself you should only ask two questions, "are you sure?" and "how much rope do you need?". So you ask me for some rope, you told us you are sure you want this rope, so who am I to not give you this rope?
This is what you asked... Let's call it a v0.5 . It even has a codename: Armed and Dangerous. I've even given to v0.1 a codename: Running with Scissors. It is disponible at http://pastebin.com/6qLs8TPt . (the v0.2 is Lost in Space, because I've forgot it on another computer :-) ). I've added to the v0.3 Playing with Fire http://pastebin.com/pRbKt1Z2 a simple logger and a Stack Trace augmenter, and to the v0.4 Crossing without Looking http://pastebin.com/yEhc9vjg a little EF compatibility.
I checked it with simple queries and Join(s). It seems to work. Much could be added. For example an "intelligent" projector that analyzes the Select that probably is removed from the query and tries to rebuild it... But this can be a good start. There is a small problem: as I've written in a comment, depending on the presence/not presence of a Select, the LINQ-to-SQL loads the returned objects in its Object Tracker. If in the "original" query there is a Select (so no object tracking), and I remove it to execute it locally, then the full object will be loaded, and the object tracking will be turned "on". Clearly you can context.ObjectTrackingEnabled = false.
Now it should be compatible with simple EF queries. Not compatible with AsNoTracking()/Include().
The NotSupportedException is tracked in an Exception property. Note that this property is modified only in the most "external" IQueryable. So
// Optional :-) You can ignore the logger and live happy...
// Not optional: you can live happy very far from me!
SafeQueryable.Logger = (iqueriable, expression, e) => Console.WriteLine(e);
var q1 = context.Items.AsSafe();
var q2 = q1.Where(x => x.ID.GetHashCode() > 0);
var q3 = q2.Select(x => x.ID);
var ex = ((ISafeQueryable)q3).Exception;
How to use it? There is a single extension method, AsSafe(). You use it on a IQueryable<T> and then you execute the query... And live happy... Playing with Fire! :-)
Examples of use:
// Queries that are NotSupportedException with LINQ-to-SQL
var q1 = context.Items.AsSafe().Where(x => x.ID.GetHashCode() > 0).Take(2).Select(x => x.ID);
var q2 = context.Items.Where(x => x.ID.GetHashCode() > 0).AsSafe().Take(2).Select(x => x.ID);
var q3 = context.Items.Where(x => x.ID.GetHashCode() > 0).Take(2).AsSafe().Select(x => x.ID);
var q4 = context.Items.Where(x => x.ID.GetHashCode() > 0).Take(2).Select(x => x.ID).AsSafe();
//// Queries that are OK with LINQ-to-SQL
//var q1 = context.Items.AsSafe().Where(x => x.ID > 0).Take(2).Select(x => x.ID);
//var q2 = context.Items.Where(x => x.ID > 0).AsSafe().Take(2).Select(x => x.ID);
//var q3 = context.Items.Where(x => x.ID > 0).Take(2).AsSafe().Select(x => x.ID);
//var q4 = context.Items.Where(x => x.ID > 0).Take(2).Select(x => x.ID).AsSafe();
var r1 = q1.ToList();
var r2 = q2.First();
var r3 = q3.Max();
// The Aggregate isn't normally supported by LINQ-to-SQL
var r4 = q4.Aggregate((x, y) => x + y);
var ex1 = ((ISafeQueryable)q1).Exception;
var ex2 = ((ISafeQueryable)q2).Exception;
var ex3 = ((ISafeQueryable)q3).Exception;
var ex4 = ((ISafeQueryable)q4).Exception;
As you can see, you can use the AsSafe() at any step, and it will work. This happens because the query is wrapped by AsSafe() in a SafeQueryable<T> that is both a IQueryProvider and a IQueryable<T> (like the classes of LINQ-to-SQL). In this way every other IQueryable method you call on it will produce other SafeQueryable<T> objects (so it self-reproduce :-) )
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
/// <summary>
/// v0.5 Codename: Armed and Dangerous
///
/// (previous version v0.1 Codename: Running with Scissors: http://pastebin.com/6qLs8TPt)
/// (previous version v0.2 Codename: Lost in Space: lost in space :-) )
/// (previous version v0.3 Codename: Playing with Fire: http://pastebin.com/pRbKt1Z2)
/// (previous version v0.4 Codename: Crossing without Looking: http://pastebin.com/yEhc9vjg)
///
/// Support class with an extension method to make "Safe" an IQueryable
/// or IQueryable<T>. Safe as "I work for another company, thousand
/// of miles from you. I do hope I won't ever buy/need something from
/// your company".
/// The Extension methods wraps a IQueryable in a wrapper that then can
/// be used to execute a query. If the original Provider doesn't suppport
/// some methods, the query will be partially executed by the Provider
/// and partially executed locally.
///
/// Minimal support for EF.
///
/// Note that this **won't** play nice with the Object Tracking!
///
/// Not suitable for programmers under 5 years (of experience)!
/// Dangerous if inhaled or executed.
/// </summary>
public static class SafeQueryable
{
/// <summary>
/// Optional logger to log the queries that are "corrected. Note that
/// there is no "strong guarantee" that the IQueriable (that is also
/// an IQueryProvider) is executing its (as in IQueriable.Expression)
/// Expression, so an explicit Expression parameter is passed. This
/// because the IQueryProvider.Execute method receives an explicit
/// expression parameter. Clearly there is a "weak guarantee" that
/// unless you do "strange things" this won't happen :-)
/// </summary>
public static Action<IQueryable, Expression, NotSupportedException> Logger { get; set; }
/// <summary>
/// Return a "Safe" IQueryable<T>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static IQueryable<T> AsSafe<T>(this IQueryable<T> source)
{
if (source is SafeQueryable<T>)
{
return source;
}
return new SafeQueryable<T>(source);
}
}
/// <summary>
/// Simple interface useful to collect the Exception, or to recognize
/// a SafeQueryable<T>.
/// </summary>
public interface ISafeQueryable
{
NotSupportedException Exception { get; }
}
/// <summary>
/// "Safe" wrapper around a IQueryable<T;>
/// </summary>
/// <typeparam name="T"></typeparam>
public class SafeQueryable<T> : IOrderedQueryable<T>, IQueryProvider, ISafeQueryable
{
protected static readonly FieldInfo StackTraceStringField = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
// The query. Note that it can be "transformed" to a "safe" version
// of itself. When it happens, IsSafe becomes true
public IQueryable<T> Query { get; protected set; }
// IsSafe means that the query has been "corrected" if necessary and
// won't throw a NotSupportedException
protected bool IsSafe { get; set; }
// Logging of the "main" NotSupportedException.
public NotSupportedException Exception { get; protected set; }
public SafeQueryable(IQueryable<T> query)
{
Query = query;
}
/* IQueryable<T> */
public IEnumerator<T> GetEnumerator()
{
if (IsSafe)
{
return Query.GetEnumerator();
}
return new SafeEnumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Type ElementType
{
get { return Query.ElementType; }
}
public Expression Expression
{
get { return Query.Expression; }
}
public IQueryProvider Provider
{
get { return this; }
}
/* IQueryProvider */
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return CreateQueryImpl<TElement>(expression);
}
public IQueryable CreateQuery(Expression expression)
{
Type iqueryableArgument = GetIQueryableTypeArgument(expression.Type);
MethodInfo createQueryImplMethod = typeof(SafeQueryable<T>)
.GetMethod("CreateQueryImpl", BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(iqueryableArgument);
return (IQueryable)createQueryImplMethod.Invoke(this, new[] { expression });
}
public TResult Execute<TResult>(Expression expression)
{
return ExecuteImpl<TResult>(expression);
}
public object Execute(Expression expression)
{
Type iqueryableArgument = GetIQueryableTypeArgument(expression.Type);
MethodInfo executeImplMethod = typeof(SafeQueryable<T>)
.GetMethod("ExecuteImpl", BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(iqueryableArgument);
return executeImplMethod.Invoke(this, new[] { expression });
}
/* Implementation methods */
// Gets the T of IQueryablelt;T>
protected static Type GetIQueryableTypeArgument(Type type)
{
IEnumerable<Type> interfaces = type.IsInterface ?
new[] { type }.Concat(type.GetInterfaces()) :
type.GetInterfaces();
Type argument = (from x in interfaces
where x.IsGenericType
let gt = x.GetGenericTypeDefinition()
where gt == typeof(IQueryable<>)
select x.GetGenericArguments()[0]).FirstOrDefault();
return argument;
}
protected IQueryable<TElement> CreateQueryImpl<TElement>(Expression expression)
{
return new SafeQueryable<TElement>(Query.Provider.CreateQuery<TElement>(expression));
}
protected TResult ExecuteImpl<TResult>(Expression expression)
{
if (IsSafe && Query.Expression == expression)
{
TResult result = Query.Provider.Execute<TResult>(expression);
return result;
}
try
{
// Note that thanks to how everything knits together, if you
// call query1.First(); query1.First(); the second call will
// get to use the query cached by the first one (technically
// the cached query will be only the "query1" part)
// We try executing it directly
TResult result = Query.Provider.Execute<TResult>(expression);
// Success!
if (!IsSafe && CanCache(expression, true))
{
IsSafe = true;
}
return result;
}
catch (NotSupportedException e1)
{
// Clearly there was a NotSupportedException :-)
Tuple<IEnumerator<T>, bool, TResult> result = HandleEnumerationFailure<TResult>(e1, expression, true);
if (result == null)
{
throw;
}
// Success!
return result.Item3;
}
}
// Is used both indirectly by GetEnumerator() and by Execute<>.
// The returned Tuple<,,> has the first two elements that are valid
// when used by the GetEnumerator() and the last that is valid
// when used by Execute<>.
protected Tuple<IEnumerator<T>, bool, TResult> HandleEnumerationFailure<TResult>(NotSupportedException e1, Expression expression, bool singleResult)
{
// We "augment" the exception with the full stack trace
AugmentStackTrace(e1, 3);
if (SafeQueryable.Logger != null)
{
SafeQueryable.Logger(this, expression, e1);
}
// We save this first exception
Exception = e1;
{
var query = Query;
MethodInfo executeSplittedMethod = typeof(SafeQueryable<T>).GetMethod("ExecuteSplitted", BindingFlags.Instance | BindingFlags.NonPublic);
MethodCallExpression call;
Expression innerExpression = expression;
Type iqueryableArgument;
// We want to check that there is a MethodCallExpression with
// at least one argument, and that argument is an Expression
// of type IQueryable<iqueryableArgument>, and we save the
// iqueryableArgument
while ((call = innerExpression as MethodCallExpression) != null &&
call.Arguments.Count > 0 &&
(innerExpression = call.Arguments[0] as Expression) != null &&
(iqueryableArgument = GetIQueryableTypeArgument(innerExpression.Type)) != null)
{
try
{
Tuple<IEnumerator<T>, bool, TResult> result2 = (Tuple<IEnumerator<T>, bool, TResult>)executeSplittedMethod.MakeGenericMethod(iqueryableArgument, typeof(TResult)).Invoke(this, new object[] { expression, call, innerExpression, singleResult });
return result2;
}
catch (TargetInvocationException e2)
{
if (!(e2.InnerException is NotSupportedException))
{
throw;
}
}
}
return null;
}
}
// Is used both indirectly by GetEnumerator() and by Execute<>.
// The returned Tuple<,,> has the first two elements that are valid
// when used by the GetEnumerator() and the last that is valid
// when used by Execute<>.
protected Tuple<IEnumerator<T>, bool, TResult> ExecuteSplitted<TInner, TResult>(Expression expression, MethodCallExpression call, Expression innerExpression, bool singleResult)
{
// The NotSupportedException should happen here
IQueryable<TInner> innerQueryable = Query.Provider.CreateQuery<TInner>(innerExpression);
// We try executing it directly
IEnumerator<TInner> innerEnumerator = innerQueryable.GetEnumerator();
bool moveNextSuccess = innerEnumerator.MoveNext();
IEnumerator<T> enumerator;
TResult singleResultValue;
// Success!
{
// Now we wrap the partially used enumerator in an
// EnumerableFromStartedEnumerator
IEnumerable<TInner> innerEnumerable = new EnumerableFromStartedEnumerator<TInner>(innerEnumerator, moveNextSuccess, innerQueryable);
// Then we apply an AsQueryable, that does some magic
// to make the query appear to be a Queryable
IQueryable<TInner> innerEnumerableAsQueryable = Queryable.AsQueryable(innerEnumerable);
// We rebuild a new expression by changing the "old"
// inner parameter of the MethodCallExpression with the
// queryable we just built
var arguments = call.Arguments.ToArray();
arguments[0] = Expression.Constant(innerEnumerableAsQueryable);
MethodCallExpression call2 = Expression.Call(call.Object, call.Method, arguments);
Expression expressionWithFake = new SimpleExpressionReplacer(call, call2).Visit(expression);
// We "execute" locally the whole query through a second
// "outer" instance of the EnumerableQuery (this class is
// the class that "implements" the "fake-magic" of
// AsQueryable)
IQueryable<T> queryable = new EnumerableQuery<T>(expressionWithFake);
if (singleResult)
{
enumerator = null;
moveNextSuccess = false;
singleResultValue = queryable.Provider.Execute<TResult>(queryable.Expression);
}
else
{
enumerator = queryable.GetEnumerator();
moveNextSuccess = enumerator.MoveNext();
singleResultValue = default(TResult);
}
}
// We could enter here with a new query from Execute<>(),
// with IsSafe == true . It would be useless to try to cache
// that query.
if (!IsSafe && CanCache(expression, singleResult))
{
Stopwatch sw = Stopwatch.StartNew();
// We redo the same things to create a second copy of
// the query that is "complete", not partially
// enumerated. This second copy will be cached in the
// SafeQueryable<T>.
// Note that forcing the Queryable.AsQueryable to not
// "recast" the query to the original IQueryable<T> is
// quite complex :-) We have to
// .AsEnumerable().Select(x => x) .
IEnumerable<TInner> innerEnumerable = innerQueryable.AsEnumerable().Select(x => x);
IQueryable<TInner> innerEnumerableAsQueryable = Queryable.AsQueryable(innerEnumerable);
// Note that we cache the SafeQueryable<>.Expression!
var arguments = call.Arguments.ToArray();
arguments[0] = Expression.Constant(innerEnumerableAsQueryable);
MethodCallExpression call2 = Expression.Call(call.Object, call.Method, arguments);
Expression expressionWithFake = new SimpleExpressionReplacer(call, call2).Visit(Expression);
IQueryable<T> queryable = new EnumerableQuery<T>(expressionWithFake);
// Now the SafeQueryable<T> has a query that *just works*
Query = queryable;
IsSafe = true;
sw.Stop();
Console.WriteLine(sw.ElapsedTicks);
}
return Tuple.Create(enumerator, moveNextSuccess, singleResultValue);
}
protected bool CanCache(Expression expression, bool singleResult)
{
// GetEnumerator() doesn't permit changing the query
if (!singleResult)
{
return true;
}
// The expression is equal to the one in Query.Expression
// (should be very rare!)
if (Query.Expression == expression)
{
return true;
}
MethodCallExpression call;
Expression innerExpression = expression;
Type iqueryableArgument;
// We walk back the expression to see if a smaller part of it is
// the "original" Query.Expression . This happens for example
// when one of the operators that returns a single value
// (.First(), .FirstOrDefault(), .Single(), .SingleOrDefault(),
// .Any(), .All()., .Min(), .Max(), ...) are used.
while ((call = innerExpression as MethodCallExpression) != null &&
call.Arguments.Count > 0 &&
(innerExpression = call.Arguments[0] as Expression) != null &&
(iqueryableArgument = GetIQueryableTypeArgument(innerExpression.Type)) != null)
{
if (Query.Expression == innerExpression)
{
return true;
}
}
return false;
}
// The StackTrace of an Exception "stops" at the catch. This method
// "augments" it to include the full stack trace.
protected static void AugmentStackTrace(Exception e, int skipFrames = 2)
{
// Playing with a private field here. Don't do it at home :-)
// If not present, do nothing.
if (StackTraceStringField == null)
{
return;
}
string stack1 = e.StackTrace;
string stack2 = new StackTrace(skipFrames, true).ToString();
string stack3 = stack1 + stack2;
StackTraceStringField.SetValue(e, stack3);
}
/* Utility classes */
// An IEnumerator<T> that applies the AsSafe() paradigm, knowing that
// normally the exception happens only on the first MoveFirst().
protected class SafeEnumerator : IEnumerator<T>
{
protected readonly SafeQueryable<T> SafeQueryable_;
protected IEnumerator<T> Enumerator { get; set; }
public SafeEnumerator(SafeQueryable<T> safeQueryable)
{
SafeQueryable_ = safeQueryable;
}
public T Current
{
get
{
return Enumerator != null ? Enumerator.Current : default(T);
}
}
public void Dispose()
{
if (Enumerator != null)
{
Enumerator.Dispose();
}
}
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
// We handle exceptions only on first MoveNext()
if (Enumerator != null)
{
return Enumerator.MoveNext();
}
try
{
// We try executing it directly
Enumerator = SafeQueryable_.Query.GetEnumerator();
bool result = Enumerator.MoveNext();
// Success!
SafeQueryable_.IsSafe = true;
return result;
}
catch (NotSupportedException e1)
{
// Clearly there was a NotSupportedException :-)
Tuple<IEnumerator<T>, bool, T> result = SafeQueryable_.HandleEnumerationFailure<T>(e1, SafeQueryable_.Query.Expression, false);
if (result == null)
{
throw;
}
Enumerator = result.Item1;
return result.Item2;
}
}
public void Reset()
{
if (Enumerator != null)
{
Enumerator.Reset();
}
}
}
}
// A simple expression visitor to replace some nodes of an expression
// with some other nodes
public class SimpleExpressionReplacer : ExpressionVisitor
{
public readonly Dictionary<Expression, Expression> Replaces;
public SimpleExpressionReplacer(Dictionary<Expression, Expression> replaces)
{
Replaces = replaces;
}
public SimpleExpressionReplacer(IEnumerable<Expression> from, IEnumerable<Expression> to)
{
Replaces = new Dictionary<Expression, Expression>();
using (var enu1 = from.GetEnumerator())
using (var enu2 = to.GetEnumerator())
{
while (true)
{
bool res1 = enu1.MoveNext();
bool res2 = enu2.MoveNext();
if (!res1 || !res2)
{
if (!res1 && !res2)
{
break;
}
if (!res1)
{
throw new ArgumentException("from shorter");
}
throw new ArgumentException("to shorter");
}
Replaces.Add(enu1.Current, enu2.Current);
}
}
}
public SimpleExpressionReplacer(Expression from, Expression to)
{
Replaces = new Dictionary<Expression, Expression> { { from, to } };
}
public override Expression Visit(Expression node)
{
Expression to;
if (node != null && Replaces.TryGetValue(node, out to))
{
return base.Visit(to);
}
return base.Visit(node);
}
}
// Simple IEnumerable<T> that "uses" an IEnumerator<T> that has
// already received a MoveNext(). "eats" the first MoveNext()
// received, then continues normally. For shortness, both IEnumerable<T>
// and IEnumerator<T> are implemented by the same class. Note that if a
// second call to GetEnumerator() is done, the "real" IEnumerator<T> will
// be returned, not this proxy implementation.
public class EnumerableFromStartedEnumerator<T> : IEnumerable<T>, IEnumerator<T>
{
public readonly IEnumerator<T> Enumerator;
public readonly IEnumerable<T> Enumerable;
// Received by creator. Return value of MoveNext() done by caller
protected bool FirstMoveNextSuccessful { get; set; }
// The Enumerator can be "used" only once, then a new enumerator
// can be requested by Enumerable.GetEnumerator()
// (default = false)
protected bool Used { get; set; }
// The first MoveNext() has been already done (default = false)
protected bool DoneMoveNext { get; set; }
public EnumerableFromStartedEnumerator(IEnumerator<T> enumerator, bool firstMoveNextSuccessful, IEnumerable<T> enumerable)
{
Enumerator = enumerator;
FirstMoveNextSuccessful = firstMoveNextSuccessful;
Enumerable = enumerable;
}
public IEnumerator<T> GetEnumerator()
{
if (Used)
{
return Enumerable.GetEnumerator();
}
Used = true;
return this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public T Current
{
get
{
// There are various school of though on what should
// happens if called before the first MoveNext() or
// after a MoveNext() returns false. We follow the
// "return default(TInner)" school of thought for the
// before first MoveNext() and the "whatever the
// Enumerator wants" for the after a MoveNext() returns
// false
if (!DoneMoveNext)
{
return default(T);
}
return Enumerator.Current;
}
}
public void Dispose()
{
Enumerator.Dispose();
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
if (!DoneMoveNext)
{
DoneMoveNext = true;
return FirstMoveNextSuccessful;
}
return Enumerator.MoveNext();
}
public void Reset()
{
// This will 99% throw :-) Not our problem.
Enumerator.Reset();
// So it is improbable we will arrive here
DoneMoveNext = true;
}
}
Note that there is a bug in the LINQ-to-SQL:
var q5 = context.Items.Where(x => x.ID > context.Nodis.Min(y => y.ID.GetHashCode()));
var r5 = q5.ToList();
This won't throw NotSupportedException but won't execute correctly. I think it could be a problem with many queries that use the context.SomeTable "internally" in the query.
If I'm not mistaken, you need to materialize the collection through .ToList () before using your method. Try to convert the collection into a list and store it in a variable. After that, try to use his method in the collection that was generated in this variable.
I don't think there's an automatic way to do this, other than writing your own ExtressionTree
But analyzing your code I think that what you need is to use an Extension Method to encapsulate your query
Something like this (I already tested the concept using LinqToSql and it supports the Length property):
public static class Extensions
{
public static IQueryable<User> WhereUserMatches(this IQueryable<User> source)
{
return source.Where(x => x.Id + x.Name.Length > 12);
}
}
Usage
var myFileterdUsers = users.WhereUserMatches();
And since you are working with IQueryable then your condition will be sent to the server not in memory
With this approach you can encapsulate your complex queries and actually execute them in the server instead of memory.
A word about hiding the Exception
If the query cannot be handled by the provider I wouldn't recommend you to hide the exception by falling back to use IEnumerable (in memory query) because this could cause undesired behavior in your application
I'd recommend you to be explicit and throw an exception if the underlying query provider cannot convert it to a valid ExpressionTree
Exceptions are good, they are our friends, they let us know if we are doing something wrong
Hiding the exception is the opposite and it's considered a bad practice, hiding the exception will give you the ilusion that your application works even when most likely it won't do exactly what you think it does
Related
I'm trying to create a wrapper around QueryableBase and INhQueryProvider that would receive a collection in the constructor and query it in-memory instead of going to a database. This is so I can mock the behavior of NHibernate's ToFuture() and properly unit test my classes.
The problem is that I'm facing a stack overflow due to infinite recursion and I'm struggling to find the reason.
Here's my implementation:
public class NHibernateQueryableProxy<T> : QueryableBase<T>, IOrderedQueryable<T>
{
public NHibernateQueryableProxy(IQueryable<T> data) : base(new NhQueryProviderProxy<T>(data))
{
}
public NHibernateQueryableProxy(IQueryParser queryParser, IQueryExecutor executor) : base(queryParser, executor)
{
}
public NHibernateQueryableProxy(IQueryProvider provider) : base(provider)
{
}
public NHibernateQueryableProxy(IQueryProvider provider, Expression expression) : base(provider, expression)
{
}
public new IEnumerator<T> GetEnumerator()
{
return Provider.Execute<IEnumerable<T>>(Expression).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
internal class NhQueryProviderProxy<T> : INhQueryProvider
{
private readonly IQueryProvider provider;
public NhQueryProviderProxy(IQueryable<T> data)
{
provider = data.AsQueryable().Provider;
}
public IQueryable CreateQuery(Expression expression)
{
return new NHibernateQueryableProxy<T>(this, expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new NHibernateQueryableProxy<TElement>(this, expression);
}
public object Execute(Expression expression)
{
return provider.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return provider.Execute<TResult>(expression);
}
public object ExecuteFuture(Expression expression)
{
return provider.Execute(expression);
}
public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters)
{
throw new NotImplementedException();
}
}
Edit: I've kind of figured out the problem. One of the arguments to expression is my custom queryable. When this expression is executed by the provider, it causes an infinite call loop between CreateQuery and Execute. Is it possible to change all the references to my custom queryable to the queryable wrapped by this class?
After a while I decided to give it another try and I guess I've managed to mock it. I didn't test it with real case scenarios but I don't think many tweaks will be necessary. Most of this code is either taken from or based on this tutorial. There are some caveats related to IEnumerable when dealing with those queries.
We need to implement QueryableBase since NHibernate asserts the type when using ToFuture.
public class NHibernateQueryableProxy<T> : QueryableBase<T>
{
public NHibernateQueryableProxy(IQueryable<T> data) : base(new NhQueryProviderProxy<T>(data))
{
}
public NHibernateQueryableProxy(IQueryProvider provider, Expression expression) : base(provider, expression)
{
}
}
Now we need to mock a QueryProvider since that's what LINQ queries depend on and it needs to implement INhQueryProvider because ToFuture() also uses it.
public class NhQueryProviderProxy<T> : INhQueryProvider
{
private readonly IQueryable<T> _data;
public NhQueryProviderProxy(IQueryable<T> data)
{
_data = data;
}
// These two CreateQuery methods get called by LINQ extension methods to build up the query
// and by ToFuture to return a queried collection and allow us to apply more filters
public IQueryable CreateQuery(Expression expression)
{
Type elementType = TypeSystem.GetElementType(expression.Type);
return (IQueryable)Activator.CreateInstance(typeof(NHibernateQueryableProxy<>)
.MakeGenericType(elementType), new object[] { this, expression });
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new NHibernateQueryableProxy<TElement>(this, expression);
}
// Those two Execute methods are called by terminal methods like .ToList() and .ToArray()
public object Execute(Expression expression)
{
return ExecuteInMemoryQuery(expression, false);
}
public TResult Execute<TResult>(Expression expression)
{
bool IsEnumerable = typeof(TResult).Name == "IEnumerable`1";
return (TResult)ExecuteInMemoryQuery(expression, IsEnumerable);
}
public object ExecuteFuture(Expression expression)
{
// Here we need to return a NhQueryProviderProxy so we can add more queries
// to the queryable and use another ToFuture if desired
return CreateQuery(expression);
}
private object ExecuteInMemoryQuery(Expression expression, bool isEnumerable)
{
var newExpr = new ExpressionTreeModifier<T>(_data).Visit(expression);
if (isEnumerable)
{
return _data.Provider.CreateQuery(newExpr);
}
return _data.Provider.Execute(newExpr);
}
public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters)
{
throw new NotImplementedException();
}
}
The expression tree visitor will change the type of the query for us:
internal class ExpressionTreeModifier<T> : ExpressionVisitor
{
private IQueryable<T> _queryableData;
internal ExpressionTreeModifier(IQueryable<T> queryableData)
{
_queryableData = queryableData;
}
protected override Expression VisitConstant(ConstantExpression c)
{
// Here the magic happens: the expression types are all NHibernateQueryableProxy,
// so we replace them by the correct ones
if (c.Type == typeof(NHibernateQueryableProxy<T>))
return Expression.Constant(_queryableData);
else
return c;
}
}
And we also need a helper (taken from the tutorial) to get the type being queried:
internal static class TypeSystem
{
internal static Type GetElementType(Type seqType)
{
Type ienum = FindIEnumerable(seqType);
if (ienum == null) return seqType;
return ienum.GetGenericArguments()[0];
}
private static Type FindIEnumerable(Type seqType)
{
if (seqType == null || seqType == typeof(string))
return null;
if (seqType.IsArray)
return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
if (seqType.IsGenericType)
{
foreach (Type arg in seqType.GetGenericArguments())
{
Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
if (ienum.IsAssignableFrom(seqType))
{
return ienum;
}
}
}
Type[] ifaces = seqType.GetInterfaces();
if (ifaces != null && ifaces.Length > 0)
{
foreach (Type iface in ifaces)
{
Type ienum = FindIEnumerable(iface);
if (ienum != null) return ienum;
}
}
if (seqType.BaseType != null && seqType.BaseType != typeof(object))
{
return FindIEnumerable(seqType.BaseType);
}
return null;
}
}
To test the above code, I ran the following snippet:
var arr = new NHibernateQueryableProxy<int>(Enumerable.Range(1, 10000).AsQueryable());
var fluentQuery = arr.Where(x => x > 1 && x < 4321443)
.Take(1000)
.Skip(3)
.Union(new[] { 4235, 24543, 52 })
.GroupBy(x => x.ToString().Length)
.ToFuture()
.ToList();
var linqQuery = (from n in arr
where n > 40 && n < 50
select n.ToString())
.ToFuture()
.ToList();
As I said, no complex scenarios were tested but I guess only a few tweaks will be necessary for real-world usages.
I am trying to create a sorting method that passes a class property to sort by. I am using ASP .NET and MVC so the class i am referring to is actually a model. The issue is that I keep getting an error that states:
"Unable to create a constant value of type 'System.Func`2'. Only primitive types or enumeration types are supported in this context."}
Here is my method:
public IQueryable<AP_Tasks> GetOrderedList(IQueryable<AP_Tasks> tasks, Func<AP_Tasks, IComparable> prop, string previousSort, string currentSort, int currentCount)
{
if (previousSort == null)
{
tasks = tasks.OrderBy(x => x.TaskDate);
return tasks;
}
if (previousSort == currentSort)
{
if (currentCount % 2 == 0)
tasks = tasks.OrderByDescending(x => prop);
else
{
tasks = tasks.OrderBy(x => x.TaskDate);
}
}
return tasks;
}
Here is my call:
case "TaskID":
query = GetOrderedList(query, x => x.TaskID, previousOrder, sortOrder, currentCount);
break;
case "TaskDate":
query = query.OrderByDescending(t => t.TaskDate);
break;
TaskID is my new call. If the case was TaskDate it would work but since there are many cases I would like to have a method do it for me to reduce redundancy.
UPDATED CODE
public IQueryable<AP_Tasks> GetOrderedList(IQueryable<AP_Tasks> tasks, Func<AP_Tasks, IComparable> prop, string previousSort, string currentSort, int currentCount)
{
if (previousSort == null)
{
tasks = tasks.OrderBy(x => x.TaskDate);
return tasks;
}
if (previousSort == currentSort)
{
if (currentCount % 2 == 0)
tasks = tasks.OrderByDescending(prop);
else
{
tasks = tasks.OrderBy(prop);
}
}
return tasks;
}
You can use the following two methods to order a property based on a string property name instead of a lambda:
public static IQueryable<T> OrderByProperty<T>(this IQueryable<T> query,
string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var selector = Expression.Lambda(Expression.Property(parameter, propertyName),
parameter);
return Queryable.OrderBy(query, (dynamic)selector);
}
public static IQueryable<T> OrderByDescendingProperty<T>(this IQueryable<T> query,
string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var selector = Expression.Lambda(Expression.Property(parameter, propertyName),
parameter);
return Queryable.OrderByDescending(query, (dynamic)selector);
}
This allows you to simply pass in whatever string you had been switching on to the method and order on that without needing to cover every single case individually at compile time.
I have the following code:
IEnumerable<KeyValuePair<T, double>> items =
sequence.Select(item => new KeyValuePair<T, double>(item, weight(item)));
if (items.Any(pair => pair.Value<0))
throw new ArgumentException("Item weights cannot be less than zero.");
double sum = items.Sum(pair => pair.Value);
foreach (KeyValuePair<T, double> pair in items) {...}
Where weight is a Func<T, double>.
The problem is I want weight to be executed as few times as possible. This means it should be executed at most once for each item. I could achieve this by saving it to an array. However, if any weight returns a negative value, I don't want to continue execution.
Is there any way to accomplish this easily within the LINQ framework?
Sure, that's totally doable:
public static Func<A, double> ThrowIfNegative<A, double>(this Func<A, double> f)
{
return a=>
{
double r = f(a);
// if r is NaN then this will throw.
if ( !(r >= 0.0) )
throw new Exception();
return r;
};
}
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
var d = new Dictionary<A, R>();
return a=>
{
R r;
if (!d.TryGetValue(a, out r))
{
r = f(a);
d.Add(a, r);
}
return r;
};
}
And now...
Func<T, double> weight = whatever;
weight = weight.ThrowIfNegative().Memoize();
and you're done.
One way is to move the exception into the weight function, or at least simulate doing so, by doing something like:
Func<T, double> weightWithCheck = i =>
{
double result = weight(i);
if (result < 0)
{
throw new ArgumentException("Item weights cannot be less than zero.");
}
return result;
};
IEnumerable<KeyValuePair<T, double>> items =
sequence.Select(item => new KeyValuePair<T, double>(item, weightWithCheck(item)));
double sum = items.Sum(pair => pair.Value);
By this point, if there is an exception to be had, you should have it. You do have to enumerate items before you can be assured of getting the exception, though, but once you get it, you will not call weight again.
Both answers are good (where to throw the exception, and memoizing the function).
But your real problem is that your LINQ expression is evaluated every time you use it, unless you force it to evaluate and store as a List (or similar). Just change this:
sequence.Select(item => new KeyValuePair<T, double>(item, weight(item)));
To this:
sequence.Select(item => new KeyValuePair<T, double>(item, weight(item))).ToList();
You could possibly do it with a foreach loop. Here is a way to do it in one statement:
IEnumerable<KeyValuePair<T, double>> items = sequence
.Select(item => new KeyValuePair<T, double>(item, weight(item)))
.Select(kvp =>
{
if (kvp.Value < 0)
throw new ArgumentException("Item weights cannot be less than zero.");
else
return kvp;
}
);
No, there is nothing already IN the LINQ framework to do this, but you could surely write up your own methods and invoke them from the linq query (As has already been shown by many).
Personally, I would either ToList the first query or use Eric's suggestion.
Instead of a functional memoization suggested by other answers, you can also employ a memoization for the whole data sequence:
var items = sequence
.Select(item => new KeyValuePair<T, double>(item, weight(item)))
.Memoize();
(Note a call to Memoize() method at the end of the expression above)
A nice property of data memoization is that it represents a drop-in replacement for ToList() or ToArray() approaches.
The fully featured implementation is pretty involved though:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
static class MemoizationExtensions
{
/// <summary>
/// Memoize all elements of a sequence, e.g. ensure that every element of a sequence is retrieved only once.
/// </summary>
/// <remarks>
/// The resulting sequence is not thread safe.
/// </remarks>
/// <typeparam name="T">The type of the elements of source.</typeparam>
/// <param name="source">The source sequence.</param>
/// <returns>The sequence that fully replicates the source with all elements being memoized.</returns>
public static IEnumerable<T> Memoize<T>(this IEnumerable<T> source) => Memoize(source, false);
/// <summary>
/// Memoize all elements of a sequence, e.g. ensure that every element of a sequence is retrieved only once.
/// </summary>
/// <typeparam name="T">The type of the elements of source.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="isThreadSafe">Indicates whether resulting sequence is thread safe.</param>
/// <returns>The sequence that fully replicates the source with all elements being memoized.</returns>
public static IEnumerable<T> Memoize<T>(this IEnumerable<T> source, bool isThreadSafe)
{
switch (source)
{
case null:
return null;
case CachedEnumerable<T> existingCachedEnumerable:
if (!isThreadSafe || existingCachedEnumerable is ThreadSafeCachedEnumerable<T>)
{
// The source is already memoized with compatible parameters.
return existingCachedEnumerable;
}
break;
case IList<T> _:
case IReadOnlyList<T> _:
case string _:
// Given source types are intrinsically memoized by their nature.
return source;
}
if (isThreadSafe)
return new ThreadSafeCachedEnumerable<T>(source);
else
return new CachedEnumerable<T>(source);
}
class CachedEnumerable<T> : IEnumerable<T>, IReadOnlyList<T>
{
public CachedEnumerable(IEnumerable<T> source)
{
_Source = source;
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
IEnumerable<T> _Source;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
IEnumerator<T> _SourceEnumerator;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
protected readonly IList<T> Cache = new List<T>();
public virtual int Count
{
get
{
while (_TryCacheElementNoLock()) ;
return Cache.Count;
}
}
bool _TryCacheElementNoLock()
{
if (_SourceEnumerator == null && _Source != null)
{
_SourceEnumerator = _Source.GetEnumerator();
_Source = null;
}
if (_SourceEnumerator == null)
{
// Source enumerator already reached the end.
return false;
}
else if (_SourceEnumerator.MoveNext())
{
Cache.Add(_SourceEnumerator.Current);
return true;
}
else
{
// Source enumerator has reached the end, so it is no longer needed.
_SourceEnumerator.Dispose();
_SourceEnumerator = null;
return false;
}
}
public virtual T this[int index]
{
get
{
_EnsureItemIsCachedNoLock(index);
return Cache[index];
}
}
public IEnumerator<T> GetEnumerator() => new CachedEnumerator<T>(this);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
internal virtual bool EnsureItemIsCached(int index) => _EnsureItemIsCachedNoLock(index);
bool _EnsureItemIsCachedNoLock(int index)
{
while (Cache.Count <= index)
{
if (!_TryCacheElementNoLock())
return false;
}
return true;
}
internal virtual T GetCacheItem(int index) => Cache[index];
}
sealed class ThreadSafeCachedEnumerable<T> : CachedEnumerable<T>
{
public ThreadSafeCachedEnumerable(IEnumerable<T> source) :
base(source)
{
}
public override int Count
{
get
{
lock (Cache)
return base.Count;
}
}
public override T this[int index]
{
get
{
lock (Cache)
return base[index];
}
}
internal override bool EnsureItemIsCached(int index)
{
lock (Cache)
return base.EnsureItemIsCached(index);
}
internal override T GetCacheItem(int index)
{
lock (Cache)
return base.GetCacheItem(index);
}
}
sealed class CachedEnumerator<T> : IEnumerator<T>
{
CachedEnumerable<T> _CachedEnumerable;
const int InitialIndex = -1;
const int EofIndex = -2;
int _Index = InitialIndex;
public CachedEnumerator(CachedEnumerable<T> cachedEnumerable)
{
_CachedEnumerable = cachedEnumerable;
}
public T Current
{
get
{
var cachedEnumerable = _CachedEnumerable;
if (cachedEnumerable == null)
throw new InvalidOperationException();
var index = _Index;
if (index < 0)
throw new InvalidOperationException();
return cachedEnumerable.GetCacheItem(index);
}
}
object IEnumerator.Current => Current;
public void Dispose()
{
_CachedEnumerable = null;
}
public bool MoveNext()
{
var cachedEnumerable = _CachedEnumerable;
if (cachedEnumerable == null)
{
// Disposed.
return false;
}
if (_Index == EofIndex)
return false;
_Index++;
if (!cachedEnumerable.EnsureItemIsCached(_Index))
{
_Index = EofIndex;
return false;
}
else
{
return true;
}
}
public void Reset()
{
_Index = InitialIndex;
}
}
}
More info and a readily available NuGet package: https://github.com/gapotchenko/Gapotchenko.FX/tree/master/Source/Gapotchenko.FX.Linq#memoize
I have a function called "CreateCriteriaExpression" that takes a json string and creates a linq expression from it.
This method is called by another called "GetByCriteria", which calls the "CreateCriteriaExpression" method and then executes that expression against an entity framework context.
For all of my entity framework objects, the "GetByCriteria" method is identical except for it's types. So I am trying to convert it to use generics instead of hard coded types.
When the "GetByCriteria" method gets to the point that it has to call the "CreateCriteriaExpression" method, I am having it use a factory class to determine the appropriate class/method to use. Then in the "linq expression" class, the linq expression for a particular type is created and returned.
The problem I am having is that the linq expression has to be created for a specific type, but the return value is of the generic type and it won't automatically convert between the two, even though the one is a parent of the other (covariance).
Is there any way I can make this work?
Some example code:
The "GetByCriteria" method:
/// <summary>
/// Gets a <see cref="System.Collections.Generic.List"/> of <see cref="TEntity"/>
/// objects that match the passed JSON string.
/// </summary>
/// <param name="myCriteria">A list of JSON strings containing a key/value pair of "parameterNames" and "parameterValues".</param>
/// <param name="myMatchMethod">Defines which matching method to use when finding matches on the <paramref name="myCriteria"/>.</param>
/// <returns>
/// A <see cref="System.Collections.Generic.List"/> of <see cref="TEntity"/>
/// objects.
/// </returns>
/// <seealso cref="TEntity"/>
///
/// <seealso cref="Common.MultipleCriteriaMatchMethod"/>
/// <remarks>
/// This method takes a <see cref="System.Collections.Generic.List"/> of JSON strings, and a <see cref="Common.MultipleCriteriaMatchMethod"/> and returns a
/// <see cref="System.Collections.Generic.List"/> of all matching
/// <see cref="TEntity"/> objects from the back-end database. The <paramref name="myMatchMethod"/> is used to determine how to match when multiple <paramref name="myCriteria"/> are passed. You can require that any results must match on ALL the passed JSON criteria, or on ANY of the passed criteria. This is essentially an "AND" versus and "OR" comparison.
/// </remarks>
[ContractVerification(true)]
public static List<TEntity> GetByCriteria<TContext, TEntity>(List<string> myCriteria, Common.MultipleCriteriaMatchMethod myMatchMethod)
where TContext : System.Data.Objects.ObjectContext, new()
where TEntity : System.Data.Objects.DataClasses.EntityObject
{
// Setup Contracts
Contract.Requires(myCriteria != null);
TContext db = new TContext();
// Intialize return variable
List<TEntity> result = null;
// Initialize working variables
// Set the predicates to True by default (for "AND" matches)
var predicate = PredicateBuilder.True<TEntity>();
var customPropertiesPredicate = PredicateBuilder.True<TEntity>();
// Set the predicates to Falase by default (for "OR" matches)
if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAny)
{
predicate = PredicateBuilder.False<TEntity>();
customPropertiesPredicate = PredicateBuilder.False<TEntity>();
}
// Loop over each Criteria object in the passed list of criteria
foreach (string x in myCriteria)
{
// Set the Criteria to local scope (sometimes there are scope problems with LINQ)
string item = x;
if (item != null)
{
JsonLinqParser parser = JsonLinqParserFactory.GetParser(typeof(TEntity));
// If the designated MultipleCriteriaMatchMethod is "MatchOnAll" then use "AND" statements
if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAll)
{
predicate = predicate.Expand().And<TEntity>(parser.CreateCriteriaExpression<TEntity>(item).Expand());
customPropertiesPredicate = customPropertiesPredicate.Expand().And<TEntity>(parser.CreateCriteriaExpressionForCustomProperties<TEntity>(item).Expand());
}
// If the designated MultipleCriteriaMatchMethod is "MatchOnAny" then use "OR" statements
else if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAny)
{
predicate = predicate.Expand().Or<TEntity>(parser.CreateCriteriaExpression<TEntity>(item).Expand());
customPropertiesPredicate = customPropertiesPredicate.Expand().Or<TEntity>(parser.CreateCriteriaExpressionForCustomProperties<TEntity>(item).Expand());
}
}
}
// Set a temporary var to hold the results
List<TEntity> qry = null;
// Set some Contract Assumptions to waive Static Contract warnings on build
Contract.Assume(predicate != null);
Contract.Assume(customPropertiesPredicate != null);
// Run the query against the backend database
qry = db.CreateObjectSet<TEntity>().AsExpandable<TEntity>().Where<TEntity>(predicate).ToList<TEntity>();
//qry = db.CreateObjectSet<TEntity>().Where(predicate).ToList<TEntity>();
// Run the query for custom properties against the resultset obtained from the database
qry = qry.Where<TEntity>(customPropertiesPredicate.Compile()).ToList<TEntity>();
// Verify that there are results
if (qry != null && qry.Count != 0)
{
result = qry;
}
// Return the results
return result;
}
The JsonLinqParser class (does not build):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LinqKit;
using Newtonsoft.Json.Linq;
namespace DAL
{
internal class JsonLinqParser_Paser : JsonLinqParser
{
internal override System.Linq.Expressions.Expression<Func<TEntity, bool>> CreateCriteriaExpression<TEntity>(string myCriteria)
{
var predicate = PredicateBuilder.True<BestAvailableFIP>();
JObject o = JObject.Parse(myCriteria);
// bmp
decimal _bmp;
if (o["bmp"] != null && decimal.TryParse((string)o["bmp"], out _bmp))
{
predicate = predicate.And<BestAvailableFIP>(x => x.bmp == _bmp);
}
// COUNTY
if (!string.IsNullOrWhiteSpace((string)o["COUNTY"]))
{
string _myStringValue = (string)o["COUNTY"];
predicate = predicate.And<BestAvailableFIP>(x => x.COUNTY.Contains(_myStringValue));
}
// emp
decimal _emp;
if (o["emp"] != null && decimal.TryParse((string)o["emp"], out _emp))
{
predicate = predicate.And<BestAvailableFIP>(x => x.emp == _emp);
}
// FIPSCO_STR
if (!string.IsNullOrWhiteSpace((string)o["FIPSCO_STR"]))
{
string _myStringValue = (string)o["FIPSCO_STR"];
predicate = predicate.And<BestAvailableFIP>(x => x.FIPSCO_STR.Contains(_myStringValue));
}
// FIPSCODE
double _FIPSCODE;
if (o["FIPSCODE"] != null && double.TryParse((string)o["FIPSCODE"], out _FIPSCODE))
{
predicate = predicate.And<BestAvailableFIP>(x => x.FIPSCODE == _FIPSCODE);
}
// FROMDESC
if (!string.IsNullOrWhiteSpace((string)o["FROMDESC"]))
{
string _myStringValue = (string)o["FROMDESC"];
predicate = predicate.And<BestAvailableFIP>(x => x.FROMDESC.Contains(_myStringValue));
}
// LANEMI
decimal _LANEMI;
if (o["LANEMI"] != null && decimal.TryParse((string)o["LANEMI"], out _LANEMI))
{
predicate = predicate.And<BestAvailableFIP>(x => x.LANEMI == _LANEMI);
}
// MPO_ABBV
if (!string.IsNullOrWhiteSpace((string)o["MPO_ABBV"]))
{
string _myStringValue = (string)o["MPO_ABBV"];
predicate = predicate.And<BestAvailableFIP>(x => x.MPO_ABBV.Contains(_myStringValue));
}
// owner
if (!string.IsNullOrWhiteSpace((string)o["owner"]))
{
string _myStringValue = (string)o["owner"];
predicate = predicate.And<BestAvailableFIP>(x => x.owner.Contains(_myStringValue));
}
// PASER
decimal _PASER;
if (o["PASER"] != null && decimal.TryParse((string)o["PASER"], out _PASER))
{
predicate = predicate.And<BestAvailableFIP>(x => x.PASER == _PASER);
}
// PASER_GROUP
if (!string.IsNullOrWhiteSpace((string)o["PASER_GROUP"]))
{
string _myStringValue = (string)o["PASER_GROUP"];
predicate = predicate.And<BestAvailableFIP>(x => x.PASER_GROUP.Contains(_myStringValue));
}
// pr
decimal _pr;
if (o["pr"] != null && decimal.TryParse((string)o["pr"], out _pr))
{
predicate = predicate.And<BestAvailableFIP>(x => x.pr == _pr);
}
// RDNAME
if (!string.IsNullOrWhiteSpace((string)o["RDNAME"]))
{
string _myStringValue = (string)o["RDNAME"];
predicate = predicate.And<BestAvailableFIP>(x => x.RDNAME.Contains(_myStringValue));
}
// SPDR_ABBV
if (!string.IsNullOrWhiteSpace((string)o["SPDR_ABBV"]))
{
string _myStringValue = (string)o["SPDR_ABBV"];
predicate = predicate.And<BestAvailableFIP>(x => x.SPDR_ABBV.Contains(_myStringValue));
}
// TODESC
if (!string.IsNullOrWhiteSpace((string)o["TODESC"]))
{
string _myStringValue = (string)o["TODESC"];
predicate = predicate.And<BestAvailableFIP>(x => x.TODESC.Contains(_myStringValue));
}
// TYPE
if (!string.IsNullOrWhiteSpace((string)o["TYPE"]))
{
string _myStringValue = (string)o["TYPE"];
predicate = predicate.And<BestAvailableFIP>(x => x.TYPE.Contains(_myStringValue));
}
return predicate;
}
internal override System.Linq.Expressions.Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties<TEntity>(string myCriteria)
{
var predicate = PredicateBuilder.True<TEntity>();
return predicate;
}
}
}
The JsonLinqParser base class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace DAL
{
abstract class JsonLinqParser
{
abstract internal Expression<Func<TEntity, bool>> CreateCriteriaExpression<TEntity>(string myCriteria)
where TEntity : System.Data.Objects.DataClasses.EntityObject;
abstract internal Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties<TEntity>(string myCriteria)
where TEntity : System.Data.Objects.DataClasses.EntityObject;
}
}
The factory class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DAL
{
internal static class JsonLinqParserFactory
{
internal static JsonLinqParser GetParser(Type type)
{
switch (type.Name)
{
case "BestAvailableFIP":
return new JsonLinqParser_Paser();
default:
//if we reach this point then we failed to find a matching type. Throw
//an exception.
throw new Exception("Failed to find a matching JsonLinqParser in JsonLinqParserFactory.GetParser() - Unknown Type: " + type.Name);
}
}
}
}
The problem is that JsonLinqParser_Paser's signatures are generic, type-agnostic, and your implementation is its specialization for concrete BestAvailableFIP type. This is not a covariance issue, it's just type incompatibility (at compiler level).
The solution is to make JsonLinqParser to be generic type (not having generic methods) - or even an interface, then make JsonLinqParser_Paser to implement JsonLinqParser<BestAvailableFIP>. We'll then have everything matching.
The IJsonLinqParser interface:
interface IJsonLinqParser<TEntity>
where TEntity : System.Data.Objects.DataClasses.EntityObject
{
Expression<Func<TEntity, bool>> CreateCriteriaExpression(string myCriteria);
Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties(string myCriteria)
}
The implementation - signatures for JsonLinqParser_Paser:
internal class JsonLinqParser_Paser : IJsonLinqParser<BestAvailableFIP>
{
public Expression<Func<BestAvailableFIP, bool>> CreateCriteriaExpression(string myCriteria)
{
// implementation as yours
}
public Expression<Func<BestAvailableFIP, bool>> CreateCriteriaExpressionForCustomProperties(string myCriteria)
{
// implementation as yours
}
}
The factory needs to return IJsonLinqParser<TEntity>, what is not a problem as we know TEntity there:
internal static class JsonLinqParserFactory
{
internal static IJsonLinqParser<TEntity> GetParser<TEntity>()
where TEntity : System.Data.Objects.DataClasses.EntityObject
{
switch (typeof(TEntity).Name)
{
case "BestAvailableFIP":
return (IJsonLinqParser<TEntity>) new JsonLinqParser_Paser();
default:
//if we reach this point then we failed to find a matching type. Throw
//an exception.
throw new Exception("Failed to find a matching JsonLinqParser in JsonLinqParserFactory.GetParser() - Unknown Type: " + typeof(TEntity).Name);
}
}
}
And finally in GetByCriteria you can have:
IJsonLinqParser<TEntity> parser = JsonLinqParserFactory.GetParser<TEntity>();
No need for <TEntity> in parser method calls now, as parser is already TEntity-specific.
Hope this helps.
By the way, your factory infrastructure can be easily replaced by good IoC tool.
For example given a Factory with a method
public static T Save<T>(T item) where T : Base, new()
{
/* item.Id == Guid.Empty therefore item is new */
if (item.Id == Guid.Empty && repository.GetAll<T>(t => t.Name == item.Name))
{
throw new Exception("Name is not unique");
}
}
how do I create a property of Base (say MustNotAlreadyExist) so that I can change the method above to
public static T Save<T>(T item) where T : Base, new()
{
/* item.Id == Guid.Empty therefore item is new */
if (item.Id == Guid.Empty && repository.GetAll<T>(t.MustNotAlreadyExist))
{
throw new Exception("Name is not unique");
}
}
public class Base
{
...
public virtual Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name); /* <- this clearly doesn't work */
}
}
and then how can I override MustNotAlreadyExist in Account : Base
public class Account : Base
{
...
public override Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name && b.AccountCode == accountCode); /* <- this doesn't work */
}
...
}
Try this:
public class Account : Base
{
...
public override Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name && b.AccountCode == accountCode).Any();
}
...
}
The Any() method will return true if any record matches the predicate. It could be argued that it is outside the responsibility of the repository to check for presence of a record before saving.
UPDATE:
There is a great article on CodeProject that describes a generic Repository for Entity Framework:
http://www.codeproject.com/KB/database/ImplRepositoryPatternEF.aspx
This could be applied to a non-Entity Framework data context. Here is an excerpt that provides a very flexible method for checking for an existing value by accepting the name of a field, a value, and a key value. You can apply this to any Entity type and use it to check for the presence of an entity before attempting a save.
/// <summary>
/// Check if value of specific field is already exist
/// </summary>
/// <typeparam name="E"></typeparam>
/// <param name="fieldName">name of the Field</param>
/// <param name="fieldValue">Field value</param>
/// <param name="key">Primary key value</param>
/// <returns>True or False</returns>
public bool TrySameValueExist(string fieldName, object fieldValue, string key)
{
// First we define the parameter that we are going to use the clause.
var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
MemberExpression leftExprFieldCheck =
MemberExpression.Property(xParam, fieldName);
Expression rightExprFieldCheck = Expression.Constant(fieldValue);
BinaryExpression binaryExprFieldCheck =
MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
MemberExpression leftExprKeyCheck =
MemberExpression.Property(xParam, this._KeyProperty);
Expression rightExprKeyCheck = Expression.Constant(key);
BinaryExpression binaryExprKeyCheck =
MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
BinaryExpression finalBinaryExpr =
Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);
//Create Lambda Expression for the selection
Expression<Func<E, bool>> lambdaExpr =
Expression.Lambda<Func<E, bool>>(finalBinaryExpr,
new ParameterExpression[] { xParam });
//Searching ....
return ((IRepository<E, C>)this).TryEntity(new Specification<E>(lambdaExpr));
}
/// <summary>
/// Check if Entities exist with Condition
/// </summary>
/// <param name="selectExpression">Selection Condition</param>
/// <returns>True or False</returns>
public bool TryEntity(ISpecification<E> selectSpec)
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Any<E>
(selectSpec.EvalPredicate);
}
I am not shure if you problem is solvable since you need to access both the repository and the new item to be checked. The new item to be checked is not available in a seperate method.
However you can outsource the call to GetAll so that your code becomes something similar to
(not tested)
public static T Save<T>(T item) where T : Base, new()
{
if (item.Id == Guid.Empty && (Check(repository, item)))
{
throw new Exception("Name is not unique");
}
}
public class Base
{
...
public Func<Enumerable<T>, T, bool> Check { get; set;}
public Base()
{
Check = (col, newItem) => (null != col.FirstOrDefault<T>(
item => item.Name == newItem.Name));
}
}
OK, here is the answer, this is a combination of the code posted by Dave Swersky and a little but of common sense.
public interface IUniqueable<T>
{
Expression<Func<T, bool>> Unique { get; }
}
public class Base, IUniqueable<Base>
{
...
public Expression<Func<Base, bool>> Unique
{
get
{
var xParam = Expression.Parameter(typeof(Base), typeof(Base).Name);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "Name");
Expression rightExprFieldCheck = Expression.Constant(this.Name);
BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
return Expression.Lambda<Func<Base, bool>>(binaryExprFieldCheck, new ParameterExpression[] { xParam });
}
}
...
}
public class Account : Base, IUniqueable<Account>
{
...
public new Expression<Func<Account, bool>> Unique
{
get
{
var xParam = Expression.Parameter(typeof(Account), typeof(Account).Name);
MemberExpression leftExprNameCheck = MemberExpression.Property(xParam, "Name");
Expression rightExprNameCheck = Expression.Constant(this.Name);
BinaryExpression binaryExprNameCheck = MemberExpression.Equal(leftExprNameCheck, rightExprNameCheck);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "AccountCode");
Expression rightExprFieldCheck = Expression.Constant(this.AccountCode);
BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
BinaryExpression binaryExprAllCheck = Expression.OrElse(binaryExprNameCheck, binaryExprFieldCheck);
return Expression.Lambda<Func<Account, bool>>(binaryExprAllCheck, new ParameterExpression[] { xParam });
}
}
...
}
public static class Manager
{
...
public static T Save<T>(T item) where T : Base, new()
{
if (!item.IsValid)
{
throw new ValidationException("Unable to save item, item is not valid", item.GetRuleViolations());
}
if (item.Id == Guid.Empty && repository.GetAll<T>().Any(((IUniqueable<T>)item).Unique))
{
throw new Exception("Item is not unique");
}
return repository.Save<T>(item);
}
...
}
Essentially by implementing the IUniqueable interface for a specific type I can return a different Expression for each type. All good :-)