I am getting the following error using
System.Exception: Invalid Cast. The update expression must be of type MemberInitExpression. at Z.EntityFramework.Plus.BatchUpdate.ResolveUpdateFromQueryDictValues[T](Expression`1 updateFactory)
And this is what I am doing:
public void Update(Expression<Func<CustomerConsentEntity, bool>> expression, CustomerConsentEntity entity)
{
DbContext.CustomerConsents.Where(expression).Update(x => entity);
}
public void Update(Expression<Func<CustomerConsentEntity, bool>> expression, CustomerConsentEntity entity)
{
DbContext.CustomerConsents.Where(a => a.TrackingId == Guid.Parse("4D8214D5-3D43-4A3E-9572-BCDCEA6F3BF9")).Update(x => entity);
}
public void Update(Expression<Func<CustomerConsentEntity, bool>> expression, CustomerConsentEntity entity)
{
Expression<Func<CustomerConsentEntity, CustomerConsentEntity>> updateEntity = x => entity;
DbContext.CustomerConsents.Where(expression).Update(updateEntity);
}
I am using the Z.EntityFramework.Plus and have done this a couple of times, but this time it seems to be failing for some strange reason. I have tried a couple of different ways while keeping the signature simple but they alll fail.
This works but it's not ideal:
public void Update(Expression<Func<CustomerConsentEntity, bool>> expression, Expression<Func<CustomerConsentEntity, CustomerConsentEntity>> updateExpression)
{
DbContext.CustomerConsents.Where(expression).Update(updateExpression);
}
The way I found to get this working properly is to build Member init expression and pass it as a parameter to the Update function instead of Expression<Func>.
PS
The entire syntax for building expression trees made this method parametrization too complex for maintenance so at some point I decided to discard it and just build multiple methods with precompiled expressions.
Related
I've created my database schema using Entity Framework's Code First, and one of the models has a composite key (which is reflected perfectly in the db).
However, when I try to look for records using a list of composite keys I get an exception that says:
"Unable to create a constant value of type 'Anonymous type'. Only primitive types or enumeration types are supported in this context."
This is the code I've used last that failed (variations of this had also produced the same error):
var ids = models.Select(m => new { m.Id, m.InstanceId })
.ToArray();
var records = _dataService.TableWithCompositeKey
.Where(t => ids.Contains(new { t.Id, t.InstanceId }))
.ToArray();
The exception is thrown when evaluating records. So how can I accomplish such a task ?
EDIT : Rename variable, and add comments on multiple records
I did assume you wanted single records on after the other due to the nature of this problem. Contains cant do what you want in EF.
Context.Set<poco>.Find() supports multiple field keys
see Ef Source public virtual TEntity Find(params object[] keyValues)
var record = _dataService.TableWithCompositeKey
.Find( Id, InstanceId )
.ToList();
if you can NOT get teh records with a regular where clause and remove the unwanted entries not in the key list then :
a) Get individually
foreach...
find(a,b)
b) Build the where clause dynamically
using something like Predicate builder see below, to build the where clause
with
foreach
Thischeck = ( a = X and b = Y ) // eg create Expression for (Tpoco t)=>value.Equals(t.PropertyName)
Whereclause.Or(thisCheck)
observation, If you have small number of entries to collect, then individual gets is of consideration.
If you have many records to get, then size of the Where clause generated is of consideration. That may end up with a full table scan of the DB.
You also need to be careful that the Linq you generate doesnt cause all records to be loaded into memory before the wanted records are found.
No nice solution to this issue.
/// See http://www.albahari.com/expressions for information and examples.
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T> () { return f => true; }
public static Expression<Func<T, bool>> False<T> () { return f => false; }
public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
}
I have the following method automatically generated from the scaffold template with repository:-
public Group Find(int id)
{
return context.Groups.Find(id);
}
But since the Groups object has two navigation properties which I need , so I wanted to include the .Include, so I replace the .find with .where :-
public Group Find(int id)
{
return context.Groups.Where(c=>c.GroupID==id)
.Include(a => a.UserGroups)
.Include(a2 => a2.SecurityRoles)
.SingleOrDefault();
}
But my question is how can I apply the .Include with the .find() instead of using .Where()?
I was just thinking about what find actually does. #lazyberezovsky is right include and find cant be used in conjunction with each other. I think this is quite deliberate and here's why:
The Find method on DbSet uses the primary key value to attempt to find
an entity tracked by the context. If the entity is not found in the
context then a query will be sent to the database to find the entity
there. Null is returned if the entity is not found in the context or
in the database.
Find is different from using a query in two significant ways:
A round-trip to the database will only be made if the entity with the given key is not found in the context.
Find will return entities that are in the Added state. That is, Find will return entities that have been added to the context but have
not yet been saved to the database.
(from http://msdn.microsoft.com/en-us/data/jj573936.aspx)
Because find is an optimised method it can avoid needing a trip to the server. This is great if you have the entity already tracked, as EF can return it faster.
However if its not just this entity which we are after (eg we want to include some extra data) there is no way of knowing if this data has already been loaded from the server. While EF could probably make this optimisation in conjunction with a join it would be prone to errors as it is making assumptions about the database state.
I imagine that include and find not being able to be used together is a very deliberate decision to ensure data integrity and unnecessary complexity. It is far cleaner and simpler
when you are wanting to do a join to always go to the database to perform that join.
You can't. Find method defined on DbSet<T> type and it returns entity. You can't call Include on entity, so the only possible option is calling Find after Include. You need DbSet<T> type for that, but Include("UserGroups") will return DbQuery<T>, and Include(g => g.UserGroups) will also return DbQuery<T>:
public static IQueryable<T> Include<T>(this IQueryable<T> source, string path)
where T: class
{
RuntimeFailureMethods.Requires(source != null, null, "source != null");
DbQuery<T> query = source as DbQuery<T>;
if (query != null)
return query.Include(path); // your case
// ...
}
DbQuery<T> is not a child of DbSet<T> thus method Find is not available. Also keep in mind, that Find first looks for entity in local objects. How would it include some referenced entities, if they don't loaded yet?
You can try to do this:
public static class DbContextExtention
{
public static TEntity FirstOfDefaultIdEquals<TEntity, TKey>(
this IQueryable<TEntity> source, TKey otherKeyValue)
where TEntity : class
{
var parameter = Expression.Parameter(typeof(TEntity), "x");
var property = Expression.Property(parameter, "ID");
var equal = Expression.Equal(property, Expression.Constant(otherKeyValue));
var lambda = Expression.Lambda<Func<TEntity, bool>>(equal, parameter);
return source.FirstOrDefault(lambda);
}
public static TEntity FirstOfDefaultIdEquals<TEntity>(
this ObservableCollection<TEntity> source, TEntity enity)
where TEntity : class
{
var value = (int)enity.GetType().GetProperty("ID").GetValue(enity, null);
var parameter = Expression.Parameter(typeof(TEntity), "x");
var property = Expression.Property(parameter, "ID");
var equal = Expression.Equal(property, Expression.Constant(value));
var lambda = Expression.Lambda<Func<TEntity, bool>>(equal, parameter);
var queryableList = new List<TEntity>(source).AsQueryable();
return queryableList.FirstOrDefault(lambda);
}
}
GetById:
public virtual TEntity GetByIdInclude(TId id, params Expression<Func<TEntity, object>>[] includes)
{
var entry = Include(includes).FirstOfDefaultIdEquals(id);
return entry;
}
Method include EntityFramework Core (look here(EF6 and EF Core)):
protected IQueryable<TEntity> Include(params Expression<Func<TEntity, object>>[] includes)
{
IIncludableQueryable<TEntity, object> query = null;
if (includes.Length > 0)
{
query = DbSet.Include(includes[0]);
}
for (int queryIndex = 1; queryIndex < includes.Length; ++queryIndex)
{
query = query.Include(includes[queryIndex]);
}
return query == null ? DbSet : (IQueryable<TEntity>)query;
}
My current project uses a generic repository interface, thus:
public interface IDataSource : IDisposable
{
void Add<T>(T newItem) where T : EntityBase;
T Get<T>(Guid id) where T : EntityBase;
T Get<T>(Expression<Func<T, bool>> predicate) where T : EntityBase;
IQueryable<T> GetAll<T>(Expression<Func<T, bool>> predicate = null) where T : EntityBase;
int Count<T>(Expression<Func<T, bool>> predicate = null) where T : EntityBase;
bool Any<T>(Expression<Func<T, bool>> predicate = null) where T : EntityBase;
void Update<T>(T updated) where T : EntityBase;
void Delete<T>(Guid id) where T : EntityBase;
void Delete<T>(T item) where T : EntityBase;
void Commit();
}
As an example, the Get method looks like this:
public T Get<T>(Expression<Func<T, bool>> predicate) where T : EntityBase
{
return db.Set<T>().Single(predicate);
}
where db is an instance my data context, which extends Entity Framework's DbContext. The whole thing implements IDisposable so that I can use it in a scope block for unit-of-work pattern, waiting to the end before committing changes, or disposing the entire thing if something goes wrong before that.
This interface is used by a logic layer to handle more complex queries, to keep business logic entirely separated from data access. So, a query to that layer might go like this:
public List<Product> ItemsBoughtByCustomer(Guid customerID)
{
using (var db = DataAccess.GetContext())
{
List<Purchase> purchaseHistory = db.GetAll<Purchase>(p => p.CustomerID == customerID);
List<int> IDs = purchaseHistory.Select(p => p.ProductID);
return db.GetAll<Product>(p => IDs.Contains(p.ID));
}
}
(Yes, I realise that can be condensed; it is in the app, but for an example, this is clearer.)
My problem is that sometimes I return a set of objects, and then later I might want to get to some of the things it references. For example, when I get a Product to display, the Display might want to do this:
#foreach (Comment comment in Product.Comments)
{
<div class="comment">
<span>#Html.UserDisplay(comment.Author)</span>
<span>#comment.Body</span>
</div>
}
(ignore the quality of the HTML; again, it's a quick example)
The problem is that this throws errors when Entity Framework's lazy loading leaves these properties null when returning entities from my queries. Now, I'm aware of the Include() method, but if my repository is generic then it's difficult to apply those. I could turn it off entirely, but then EF will start retrieving enormous linked collections of things when I don't need them - the structure of my model and the links that things have out to the audit logs mean a lot of links for EF to follow.
Is there a way that I can lazy-load in a slightly smarter manner? Is there a method like .Single() and .Where() that I can call on the DbSet that will bring child objects as well, so that I can specifically ask for child objects to be included for a certain query?
add an optional parameter for the include path then invoke Include(str) on the DbSet. Example with your Get method:
public T Get<T>(Expression<Func<T, bool>> predicate, string includePath = null) where T : EntityBase
{
var query = db.Set<T>();
if( !string.IsNullorWhitespace( includePath ) )
{
query = query.Include( includePath );
}
return query.Single(predicate);
}
So I have classes that looks like this.
public class User {
public virtual IList<Member> Members {get;set;}
}
public class Member {
public virtual AnotherTable Another {get;set;}
}
public class AnotherTable {
public string Name {get;set;}
}
When I perform the query directly against the DataContext the Include works, but when I do an AsQueryable() on the IList of members the include doesn't work.
Is there a way to have Include/Eager functionality on lazy loaded properties, such as the Members property above, or do I always have to go through the DataContext to get that feature?
User.Members.AsQueryable().Include(a => a.Another).ToList() // <-- nada, no way Jose
_db.Members.Include(m => m.Another).ToList() // <-- all good in the neighborhood
I ask cause it can be a huge difference of 1 sql query vs. 100 queries for something result equivalent.
Thanks in advance.
AsQueryable doesn't make it linq-to-entities query. It is still Linq-to-object query on top of List. List doesn't know how to handle Include - only DbQuery knows it so you must get DbQuery:
var entry = context.Entry(user);
entry.Collection(u => u.Member).Query().Include(m => m.Another).Load();
You'll have to go through the DbContext in order for Include() to work. You could abstract it into a Repository, but you'll still need to pass your Include() expression to your underlying context.
private IQueryable<T> GetQuery<T>(params Expression<Func<T, object>>[] includeProperties) where T : class
{
IQueryable<T> query = _db.Set<T>();
if (includeProperties != null)
{
foreach (Expression<Func<T, object>> expression in includeProperties)
{
query = query.Include(expression);
}
}
return query;
}
I also faced same problem.
I solved this just adding the reference System.Data.Entity & use following namespace:
using System.Data.Entity;
You can try with it.
When using the following code (simplified), I get the error
"Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types."
in the line with the return statement:
public MyRepository<Post>
{
public Expression<Func<Post, object>> DefaultOrder;
public MyRepository()
{
DefaultOrder = p => p.PublishedOn;
}
public IQueryable<Post> All()
{
var entities = new MyDbContext().Set<Post>();
return entities.OrderByDescending(DefaultOrder);
}
}
I used the same code with db4o/db4o.Linq instead of Entity Framework and there was no problem.
So here my questions:
Why is this error happening?
Is there an alternative solution, which lets me define my DefaultOrder in the same (a similar) manner as I do now?
EDIT:
Found a solution, that works for me, replaced the default order expression with an order method:
public MyRepository<T>
{
public Func<IQueryable<T>, IOrderedQueryable<T>> DefaultOrderMethod;
public MyRepository()
{
DefaultOrderMethod = o => o.OrderBy(x => x.PublishedOn);
}
public IQueryable<T> All()
{
var entities = new MyDbContext().Set<T>();
return DefaultOrderMethod(entities);
}
}
The second type of the expression passed into the OrderBy extension method is inferred from the type that is returned by the expression. It's expecting a Expression>. So if you're going to store the sort expression you need to explicitly give it the TOrderBy type.
public MyRepository<Post>
{
public Expression<Func<Post, DateTime>> DefaultOrder;
public MyRepository()
{
DefaultOrder = p => p.PublishedOn;
}
public IQueryable<Post> All()
{
var entities = new MyDbContext().Set<Post>();
return entities.OrderByDescending(DefaultOrder);
}
}
.NET does not support boxing/unboxing a generic parameter type unless you're using .NET 4.0 and use interfaces via covariance and contravariance which would not work for your example.
Again this is just how the generic system works in .NET. The only reason something like this works...
Query.OrderBy(x => x.PublishedOn)
... is because the TOrderBy type can be inferred from the expression return type (DateTime).