I like the fact that AddOrUpdate let's you specify a filter to check to avoid adding duplicates. But I would like similar functionality without the update.
Right now I do something like this:
var checkProfile = from p in db.Profile
where p => p.LastName == newProfile.lastName
&& p => p.FirstName == newProfile.firstName
&& p => p.Middle== newProfile.middle
select p;
if (checkProfile.FirstOrDefault() == null)
{
db.Profile.Add(newProfile);
db.SaveChanges();
}
I know I can do something like this:
db.Profile.AddOrUpdate(p => new {p.LastName, p.FirstName, p.Middle}, newProfile);
db.SaveChanges();
But I would rather skip modifying the data in this case.
The first example does what I want but with more code. Is there a simpler/cleaner way to do what I want in the first example?
Update:
I like Ognyan Dimitrov's suggestion. I'm trying to implement it. My models inherit from BaseEntity. Can I put a generic version of that there?
My model is defined:
public class Address :BaseEntity
{
My BaseEntity:
public class BaseEntity
{
public virtual T AddIfNotExists<T>(T entity, Expression<Func<T, bool>> predicate = null)
{
var exists = predicate != null ? DbSet.Any(predicate) : DbSet.Any();
return !exists ? DbSet.Add(entity) : null;
}
}
I'm getting errors for Any(...) and Add(...). The error for Add(...) is 'An object reference is required for the non-static field, method, or property 'System.Data.Entity.DbSet.Add(object)' '
Should I be using this.Add(object) ?
Update 2:
I've created this code:
public static class DbSetExtensions
{
public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
{
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return !exists ? dbSet.Add(entity) : null;
}
}
Now I'm trying to call it like this, but it's not correct. Forgive my lack of understanding.
_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
pi => new {pi.ProfileId, pi.ProfileIdentifierTypeId, pi.ProfileIdentifierValue});
Update - Solution:
I can call the DbSetextensions like this:
_db.ProfileIdentifier.AddIfNotExists(newIdentifier,
pi => pi.ProfileId == profileId &&
pi.ProfileIdentifierTypeId == (int)type &&
pi.ProfileIdentifierValue == value);
Thanks a lot for working with me Ognyan!!!
Have you tried to check if the entity exists and if not - add it? Like this :
UPDATE
using System.Linq.Expressions;
public class ContextWithExtensionExample
{
public void DoSomeContextWork(DbContext context)
{
var uni = new Unicorn();
context.Set<Unicorn>().AddIfNotExists(uni, x => x.Name == "James");
}
}
public static class DbSetExtensions
{
public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
{
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return !exists ? dbSet.Add(entity) : null;
}
}
You can use this method directly and remember to call DbContext.SaveChanges() after the call.
All the other answers are incorrect.
"Read before write" can violate data integrity without being put inside a transaction control.
In SQL Server, you can use merge statement. However merge statement is not available in EF.
The solution is OK, when you have to add just one item, but it's very expensive in terms of performance in case you have to add multiple items.
I think there is a better solution:
public static class DbSetExtensions
{
public static EntityEntry<TEnt> AddIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, TEnt entity, Func<TEnt, TKey> predicate) where TEnt : class
{
var exists = dbSet.Any(c => predicate(entity).Equals(predicate(c)));
return exists
? null
: dbSet.Add(entity);
}
public static void AddRangeIfNotExists<TEnt, TKey>(this DbSet<TEnt> dbSet, IEnumerable<TEnt> entities, Func<TEnt, TKey> predicate) where TEnt : class
{
var entitiesExist = from ent in dbSet
where entities.Any(add => predicate(ent).Equals(predicate(add)))
select ent;
dbSet.AddRange(entities.Except(entitiesExist));
}
}
So later it can be used like this:
using (var context = new MyDbContext())
{
var user1 = new User { Name = "Peter", Age = 32 };
context.Users.AddIfNotExists(user1, u => u.Name);
var user2 = new User { Name = "Joe", Age = 25 };
context.Users.AddIfNotExists(user2, u => u.Age);
// Adds user1 if there is no user with name "Peter"
// Adds user2 if there is no user with age 25
context.SaveChanges();
}
I used something like, read these two posts to make my code.
I hope to help those in need of a similar signature to AddOrUpdate.
Entity Framework Add if not exist without update
Making AddOrUpdate change only some properties
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace System.Data.Entity.Migrations
{
//
// Summary:
// Metodos de extensão para System.Data.Entity.IDbSet
public static class DbSetMigrationsGustavoExtensions
{
/// <summary>
/// Adiciona uma entidade se ela não existe ainda
/// Assinatura semelhante ao AddOrUpdate
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="set">Set onde serão adicionadas as entidades</param>
/// <param name="identifierExpression">Campos usados na comparação</param>
/// <param name="entities">Entidades para adicionar</param>
public static void AddIfNotExists<TEntity>(this IDbSet<TEntity> set, Expression<Func<TEntity, object>> identifierExpression, params TEntity[] entities) where TEntity : class
{
var identifyingProperties = GetProperties<TEntity>(identifierExpression).ToList();
var parameter = Expression.Parameter(typeof(TEntity));
foreach (var entity in entities)
{
var matches = identifyingProperties.Select(pi => Expression.Equal(Expression.Property(parameter, pi.Name), Expression.Constant(pi.GetValue(entity, null))));
var matchExpression = matches.Aggregate<BinaryExpression, Expression>(null, (agg, v) => (agg == null) ? v : Expression.AndAlso(agg, v));
var predicate = Expression.Lambda<Func<TEntity, bool>>(matchExpression, new[] { parameter });
if (!set.Any(predicate))
{
set.Add(entity);
}
}
}
private static IEnumerable<PropertyInfo> GetProperties<T>(Expression<Func<T, object>> exp) where T : class
{
Debug.Assert(exp != null);
Debug.Assert(exp.Body != null);
Debug.Assert(exp.Parameters.Count == 1);
var type = typeof(T);
var properties = new List<PropertyInfo>();
if (exp.Body.NodeType == ExpressionType.MemberAccess)
{
var memExp = exp.Body as MemberExpression;
if (memExp != null && memExp.Member != null)
properties.Add(type.GetProperty(memExp.Member.Name));
}
else if (exp.Body.NodeType == ExpressionType.Convert)
{
var unaryExp = exp.Body as UnaryExpression;
if (unaryExp != null)
{
var propExp = unaryExp.Operand as MemberExpression;
if (propExp != null && propExp.Member != null)
properties.Add(type.GetProperty(propExp.Member.Name));
}
}
else if (exp.Body.NodeType == ExpressionType.New)
{
var newExp = exp.Body as NewExpression;
if (newExp != null)
properties.AddRange(newExp.Members.Select(x => type.GetProperty(x.Name)));
}
return properties.OfType<PropertyInfo>();
}
/// <summary>
/// Faz um set.Any(predicate)
/// Se não existe nada no set então adiciona
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="set">Set onde será adicionada a entidade</param>
/// <param name="predicate">Condição (exemplo: dbUser => dbUser.Nome == "Gustavo")</param>
/// <param name="entity">Entidade para adicionar</param>
/// <returns></returns>
public static T AddIfNotExists<T>(this IDbSet<T> set, Expression<Func<T, bool>> predicate, T entity) where T : class, new()
{
return !set.Any(predicate) ? set.Add(entity) : null;
}
}
}
Quoted from MSDN EF article.
Insert or update pattern
A common pattern for some applications is to either Add an entity as
new (resulting in a database insert) or Attach an entity as existing
and mark it as modified (resulting in a database update) depending on
the value of the primary key. For example, when using database
generated integer primary keys it is common to treat an entity with a
zero key as new and an entity with a non-zero key as existing. This
pattern can be achieved by setting the entity state based on a check
of the primary key value.
Note that when you change the state to Modified all the properties of
the entity will be marked as modified and all the property values will
be sent to the database when SaveChanges is called.
context.Entry(profile).State = profile.Id == 0 ? EntityState.Added : EntityState.Modified;
context.SaveChanges();
What worked for me is this:
public static void AddIfNotExists<T>(this DbSet<T> dbSet, Func<T, object> predicate, params T [] entities) where T : class, new()
{
foreach (var entity in entities)
{
var newValues = predicate.Invoke(entity);
Expression<Func<T, bool>> compare = arg => predicate(arg).Equals(newValues);
var compiled = compare.Compile();
var existing = dbSet.FirstOrDefault(compiled);
if (existing == null)
{
dbSet.Add(entity);
}
}
}
The only thing that comes to mind is to use IEqualityComparer<T>, but this doesn't really stop the work, merely abstracts it away and creates cleaner code.
Related
I'm trying to do a generic method to update Entity Framework collections, one to many. I did this method, but i'm having a problem, when i try to verify, if the element in the new collection already exists in the old one. If exists, i have to update it, rather then remove and add it again.
The code is this:
public TEntity UpdateCollection<TEntity, TChildren>(myappContext dbContext, TEntity parentObject, Expression<Func<TEntity,
ICollection<TChildren>>> propertyExpression, ICollection<TChildren> objectChilren) where TEntity : class where TChildren : class
{
var parentEntityObject = dbContext.Entry<TEntity>(parentObject);
List<TChildren> originalChildrenData = parentEntityObject.Collection(propertyExpression).CurrentValue.ToList();
// Updating or removing existing items
foreach (var originalItem in originalChildrenData)
{
// Where the problem is: If entry was just modified, i have to update.
var newItem = objectChilren.FirstOrDefault(x => x == originalItem);
if (newItem != null)
{
dbContext.Entry<TChildren>(originalItem).CurrentValues.SetValues(newItem);
dbContext.Entry<TChildren>(originalItem).State = System.Data.EntityState.Modified;
}
else
{
dbContext.Entry<TChildren>(originalItem).State = System.Data.EntityState.Deleted;
}
}
// Adding new items
foreach(var newItem in objectChilren.Except(originalChildrenData)){
parentEntityObject.Collection(propertyExpression).CurrentValue.Add(newItem);
}
parentEntityObject.State = System.Data.EntityState.Modified;
return parentEntityObject.Entity;
}
Instead of try to check with:
var newItem = objectChilren.FirstOrDefault(x => x == originalItem);
if (newItem != null)
I also tryed with:
var newItem = this.Set<TChildren>().Local.FirstOrDefault(x => x == originalItem);
But also doesn't work, always returns null. I have to get the corresponding entry and only update it.
If it's not possible, there is another generic way to update collections "one to many"?
I did it, comparing the key property from my collection, as Magnus suggested, like this:
public TEntity UpdateCollection<TEntity, TChildren>(myappContext dbContext, TEntity parentObject, Expression<Func<TEntity,
ICollection<TChildren>>> propertyExpression, ICollection<TChildren> objectChilren) where TEntity : class where TChildren : class
{
var parentEntityObject = dbContext.Entry<TEntity>(parentObject);
List<TChildren> originalChildrenData = parentEntityObject.Collection(propertyExpression).CurrentValue.ToList();
// Get key name
var entityKeyName = GetKeyName(dbContext, originalChildrenData.Union(objectChilren).First());
// Updating or removing existing items
foreach (var originalItem in originalChildrenData)
{
var originalValueKey = originalItem.GetType().GetProperty(entityKeyName).GetValue(originalItem, null);
var itemCompareExpression = GetCompareExpression<TChildren>(entityKeyName, originalValueKey);
// If entry was just modified, i have to update.
var newItem = objectChilren.FirstOrDefault(itemCompareExpression.Compile());
if (newItem != null)
{
dbContext.Entry<TChildren>(originalItem).CurrentValues.SetValues(newItem);
dbContext.Entry<TChildren>(originalItem).State = System.Data.EntityState.Modified;
// Remove item, because only 'new items' will be added after this loop
objectChilren.Remove(newItem);
}
else
{
dbContext.Entry<TChildren>(originalItem).State = System.Data.EntityState.Deleted;
}
}
// Adding new items
foreach(var newItem in objectChilren)
{
parentEntityObject.Collection(propertyExpression).CurrentValue.Add(newItem);
}
parentEntityObject.State = System.Data.EntityState.Modified;
return parentEntityObject.Entity;
}
Methods called:
public string GetKeyName<TEntity>(myappContext dbContext, TEntity entity) where TEntity : class
{
ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
ObjectSet<TEntity> set = objectContext.CreateObjectSet<TEntity>();
return set.EntitySet.ElementType.KeyMembers.FirstOrDefault().Name;
}
public Expression<Func<TEntity, bool>> GetCompareExpression<TEntity>(string keyName, object value) where TEntity : class
{
var parameter = Expression.Parameter(typeof(TEntity), "x");
var property = Expression.Property(parameter, keyName);
var method = property.Type.GetMethod("Equals", new[] { property.Type });
var convertedValue = Convert.ChangeType(value, property.Type);
var expression = Expression.Call(property, method, Expression.Constant(convertedValue));
return Expression.Lambda<Func<TEntity, bool>>(expression, parameter);
}
As all my entities only have one key, i just used "First" on "GetKeyName", but it returns all keys, if necessary.
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
I'm trying to add a LINQ or DbContext extension method to get an element (FirstOrDefault) but if one does not already exist then create a new instance with data (FirstOrCreate) instead of returning null.
is this possible?
i.e.:
public static class LINQExtension
{
public static TSource FirstOrCreate<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
if (source.First(predicate) != null)
{
return source.First(predicate);
}
else
{
return // ???
}
}
}
and a usage could be:
using (var db = new MsBoxContext())
{
var status = db.EntitiesStatus.FirstOrCreate(s => s.Name == "Enabled");
//Here we should get the object if we find one
//and if it doesn't exist create and return a new instance
db.Entities.Add(new Entity()
{
Name = "New Entity",
Status = status
});
}
I hope that you understand my approach.
public static class LINQExtension
{
public static TSource FirstOrCreate<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate,
Func<T> defaultValue)
{
return source.FirstOrDefault(predicate) ?? defaultValue();
}
}
usage
var status = db.EntitiesStatus.FirstOrCreate(s => s.Name == "Enabled",
() => new EntityStatus {Name = "Enabled"});
However you must note that this will not work quite like FirstOrDefault().
If you did the following
var listOfStuff = new List<string>() { "Enabled" };
var statuses = from s in listOfStuff
select db.EntitiesStatus.FirstOrCreate(s => s.Name == "Enabled",
() => new EntityStatus {Name = "Enabled"});
You would get O(n) hits to the database.
However I suspect if you did...
var listOfStuff = new List<string>() { "Enabled" };
var statuses = from s in listOfStuff
select db.EntitiesStatus.FirstOrDefault(s => s.Name == "Enabled")
?? new EntityStatus {Name = "Enabled"};
It is plausible it could work...
conclussion:
instead of implement an extension method the best solution is using ?? operator in that way:
var status = db.EntitiesStatus.FirstOrDefault(s => s.Name == "Enabled") ?? new EntityStatus(){Name = "Enabled"};
I am a self taught programmer and I am really bad at typing so i was looking for the exact same thing. I ended up writing my own. It took a few steps and revisions before it would work with more than 1 property. Of course there are some limitations and I haven't fully tested it but so far it seems to work for my purposes of keeping the records distinct in the DB and shortening the code (typing time).
public static class DataExtensions
{
public static TEntity InsertIfNotExists<TEntity>(this ObjectSet<TEntity> objectSet, Expression<Func<TEntity, bool>> predicate) where TEntity : class, new()
{
TEntity entity;
#region Check DB
entity = objectSet.FirstOrDefault(predicate);
if (entity != null)
return entity;
#endregion
//NOT in the Database... Check Local cotext so we do not enter duplicates
#region Check Local Context
entity = objectSet.Local().AsQueryable().FirstOrDefault(predicate);
if (entity != null)
return entity;
#endregion
///********* Does NOT exist create entity *********\\\
entity = new TEntity();
// Parse Expression Tree and set properties
//Hit a recurrsive function to get all the properties and values
var body = (BinaryExpression)((LambdaExpression)predicate).Body;
var dict = body.GetDictionary();
//Set Values on the new entity
foreach (var item in dict)
{
entity.GetType().GetProperty(item.Key).SetValue(entity, item.Value);
}
return entity;
}
public static Dictionary<string, object> GetDictionary(this BinaryExpression exp)
{
//Recurssive function that creates a dictionary of the properties and values from the lambda expression
var result = new Dictionary<string, object>();
if (exp.NodeType == ExpressionType.AndAlso)
{
result.Merge(GetDictionary((BinaryExpression)exp.Left));
result.Merge(GetDictionary((BinaryExpression)exp.Right));
}
else
{
result[((MemberExpression)exp.Left).Member.Name] = exp.Right.GetExpressionVaule();
}
return result;
}
public static object GetExpressionVaule(this Expression exp)
{
if (exp.NodeType == ExpressionType.Constant)
return ((ConstantExpression)exp).Value;
if (exp.Type.IsValueType)
exp = Expression.Convert(exp, typeof(object));
//Taken From http://stackoverflow.com/questions/238413/lambda-expression-tree-parsing
var accessorExpression = Expression.Lambda<Func<object>>(exp);
Func<object> accessor = accessorExpression.Compile();
return accessor();
}
public static IEnumerable<T> Local<T>(this ObjectSet<T> objectSet) where T : class
{
//Taken From http://blogs.msdn.com/b/dsimmons/archive/2009/02/21/local-queries.aspx?Redirected=true
return from stateEntry in objectSet.Context.ObjectStateManager.GetObjectStateEntries(
EntityState.Added |
EntityState.Modified |
EntityState.Unchanged)
where stateEntry.Entity != null && stateEntry.EntitySet == objectSet.EntitySet
select stateEntry.Entity as T;
}
public static void Merge<TKey, TValue>(this Dictionary<TKey, TValue> me, Dictionary<TKey, TValue> merge)
{
//Taken From http://stackoverflow.com/questions/4015204/c-sharp-merging-2-dictionaries
foreach (var item in merge)
{
me[item.Key] = item.Value;
}
}
}
Usage is as simple as:
var status = db.EntitiesStatus.InsertIfNotExists(s => s.Name == "Enabled");
The extension will check the database first, if is not found it will check the local context (so you do not add it twice), if it is still not found it creates the entity, parses the expression tree to get the properties and values from the lambda expression, sets those values on a new entity, adds the entity to the context and returns the new entity.
A few things to be aware of...
This does not handle all possible uses (assumes all the expressions in the lambda are ==)
The project I did this in is using an ObjectContext as apposed to a DBContext (I have not switched yet so I don't know if this would work with DBContext. I assume it would not be difficult to change)
I am self-taught so there maybe many ways to optimize this. If you have any input please let me know.
What about this extension that also adds the new created entity to the DbSet.
public static class DbSetExtensions
{
public static TEntity FirstOrCreate<TEntity>(
this DbSet<TEntity> dbSet,
Expression<Func<TEntity, bool>> predicate,
Func<TEntity> defaultValue)
where TEntity : class
{
var result = predicate != null
? dbSet.FirstOrDefault(predicate)
: dbSet.FirstOrDefault();
if (result == null)
{
result = defaultValue?.Invoke();
if (result != null)
dbSet.Add(result);
}
return result;
}
public static TEntity FirstOrCreate<TEntity>(
this DbSet<TEntity> dbSet,
Func<TEntity> defaultValue)
where TEntity : class
{
return dbSet.FirstOrCreate(null, defaultValue);
}
}
The usage with predicate:
var adminUser = DbContext.Users.FirstOrCreate(u => u.Name == "Admin", () => new User { Name = "Admin" });
or without predicate:
var adminUser = DbContext.Users.FirstOrCreate(() => new User { Name = "Admin" });
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 :-)