I want to generate expression tree like
StatusRAG.where(x=> (overallRAG.Contains(x.OverallRAG) == overallRAGinclusive) || (costRAG.Contains(x.CostRAG) == costRAGinclusive))
Here is my data:
var overallRAG = new List<string>(){"Red", "Green"}
var costRAG = new List<string>(){"Red", "Amber"}
bool overallRAGinclusive = true
bool costRAGinclusive = false
class StatusRAG
{
public OverallRAG {get; set;}
public CostRAG {get; set;}
}
Linq expression wrong. I think you need not equal of costrag.
StatusRAG.where (x=> overallRAG.Contains(x.OverallRAG) || !costRAG.Contains(x.CostRAG))
Related
I am having to generically build a comparative predicate for an Entity Framework Linq query. I'm using reflection and am able to build a single level Lambda expression without any trouble. However where I am starting to get stuck is I have an Entity that has a relationship
public class Parent {
public virtual Child child { get; set; }
.... Other Stuff...
}
public class Child {
public int property { get; set; }
public virtual Parent parent { get; set; }
.... Other Stuff.....
}
How can I can I pass in "Child.property" into Reflection to be able to create a lambda expression comparing and come up with a lambda expression similar to item => item.Child.property == value?
I think you are looking for this:
ParameterExpression parameter = Expression.Parameter(typeof(Parent), "item");
Expression child = Expression.PropertyOrField(parameter, "child");
Expression childProperty = Expression.PropertyOrField(child, "property");
int value = 1;
Expression comparison = Expression.Equal(childProperty, Expression.Constant(value));
Expression<Func<Parent, bool>> lambda = Expression.Lambda<Func<Parent, bool>>(comparison, parameter);
var sample = new[] { new Parent() { child = new Child() { property = 1 } } };
var result = sample.Where(lambda.Compile());
I assume you are wanting a generic solution supporting nested properties:
public Expression buildLambda(Type startingType, string propertyPath, object value) {
var parameter=Expression.Parameter(startingType,"item");
var valueExpression = Expression.Constant(value);
var propertyExpression=propertyPath.Split('.').Aggregate(parameter,(Expression parent,string path)=>Expression.Property(parent,path));
return Expression.Lambda(Expression.Equal(propertyExpression,valueExpression),parameter);
}
MVC3, Entity Framework 4.1 Code first.
Working with 2 tables
Model:
public class UniversityMaster
{
[Key]
public string UniversityId { get; set; }
public string UniversityName { get; set; }
}
public class ProgramMaster
{
[Key]
public string ProgramId { get; set; }
public string ProgramName { get; set; }
public string UniversityId { get; set; }
public virtual UniversityMaster University { get; set; } // navigation property
}
Dynamic expression for sorting (just to avoid a switch case statement):
public virtual IQueryable< ProgramMaster > GetQueryableSort(string sortField="", string sortDirection="")
{
IQueryable<ProgramMaster> query = _dbSet;
ParameterExpression pe = Expression.Parameter(typeof(ProgramMaster), string.Empty);
MemberExpression property = Expression.PropertyOrField(pe, sortField);
//get a exception here if the sort field is of navigation property (University.UniversityName)
LambdaExpression lambda = Expression.Lambda(property, pe);
if (sortDirection == "ASC")
orderbydir = "OrderBy";
else
orderbydir = "OrderByDescending";
MethodCallExpression call = Expression.Call(typeof(Queryable),
orderbydir, new Type[] { typeof(TEntity), property.Type }, query.Expression, Expression.Quote(lambda));
var returnquery = (IOrderedQueryable<ProgramMaster>)query.Provider.CreateQuery< ProgramMaster >(call);
return returnquery;
}
The page is displaying a grid with two columns Program Name and University Name using webgrid. The sorting work fine for Program Name column, however fails if sorted by University Name as this property is in UniversityMaster and the Expression.PropertyOrField searches this property in ProgramMaster. Here is the exception:
University.UniversityName' is not a member of type 'App.Core.Model.ProgramMaster
My question is how I make this work for navigation properties of my model class.
Hope I was able explain the scenario. Any help is appreciated.
Well that's because the MemberExpression is trying to call a member named Univerty.UniversityName on the parameter. What you want to do is call a member named Univerity on the parameter, then call UniversityName on that. Effectively, you need to iteratively resolve the property names.
public virtual IQueryable< ProgramMaster > GetQueryableSort(string sortField = "", string sortDirection = "")
{
IQueryable<ProgramMaster> query = _dbSet;
var propertyNames = sortField.Split(".");
ParameterExpression pe = Expression.Parameter(typeof(ProgramMaster), string.Empty);
Expression property = pe;
foreach(var prop in propertyName)
{
property = Expression.PropertyOrField(property, prop);
}
LambdaExpression lambda = Expression.Lambda(property, pe);
if (sortDirection == "ASC")
orderbydir = "OrderBy";
else
orderbydir = "OrderByDescending";
MethodCallExpression call = Expression.Call(
typeof(Queryable),
orderbydir,
new Type[] { typeof(TEntity), property.Type },
query.Expression,
Expression.Quote(lambda));
var returnquery = (IOrderedQueryable<ProgramMaster>)query.Provider.CreateQuery<ProgramMaster>(call);
return returnquery;
}
Microsoft has a DynamicQueryable class which can be used to dynamically construct certain portions of a LINQ query using strings. With this you can say myQuery.OrderBy("University.UniversityName") and it will handle building the expression. The same library also supports dynamic construction of SELECT and WHERE clauses.
You can find a copy of the source as part of the excellent EntityFramework.Extended package by Loresoft. Microsoft's file is at https://github.com/loresoft/EntityFramework.Extended/blob/master/Source/EntityFramework.Extended/Dynamic/DynamicQueryable.cs
In this query I have 3 records (int-(telefon = d.telefon), decimal-(pesel = d.pesel), decimal-(nip = d.nip)) another records are strings.
public ActionResult detail(int LoginID)
{
var user = (from d in baza.uzytkowniks
where LoginID == d.LoginID
select new uzytkownikModel {
imie = d.imie,
nazwisko = d.nazwisko,
telefon = d.telefon,
pesel = d.pesel,
nip = d.nip,
email = d.email,
adres_zamieszkania = d.adres_zamieszkania}).ToList();
ViewBag.daneuser = user;
return View();
}
And I have error:
Cannot implicitly convert type 'int?' to 'int'. An explicit conversion
exists (are you missing a cast?)
and two another errors with 'decimal?' instead 'int?'.
Model:
public class uzytkownikModel
{
[Required]
public string imie { get; set; }
[Required]
public string nazwisko { get; set; }
public decimal pesel { get; set; }
public decimal nip { get; set; }
public string adres_zamieszkania { get; set; }
public int telefon { get; set; }
public string email { get; set; }
}
Only imie and nazwisko are nonnullable, rest have allow null
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Now work. I change in model:
decimal to decimal?
and
int to int?
Thank you everyone for help
You'll need to post more code to get a definitive answer, but somewhere one of your variables is nullable, and to assign it to a non-nullable type you need to do .Value.
For example, if your object d's property imie is Nullable<int>, and that is what is causing your problem, you could do this:
imie = d.imie.Value
You will also need to watch for cases where d.imie is null. For example:
imie = d.imie.HasValue ? d.imie.Value : 0
Or, if you like the ?? operator (which evaluates to the first non-null value):
imie = d.imie ?? 0
(I am just using d.imie as an example -- there is not enough code posted to pintpoint the exact problem.)
In your uzytkownikModel class, the properties that are nullable should be declared as "int?" or "decimal?" instead of "int" and "decimal".
int? is the Nullable<int>. Since database columns may be null, the mapped properties are Nullable.
Consider this code which assigns int? values to int variables.
int? w = 2;
int? x = null;
int y = w ?? 3;
int z = x ?? 4;
The following will work:
int ID = (int)(i.ID == null ? 0 : i.ID);
I'm building a rather large filter based on an SearchObject that has 50+ fields that can be searched.
Rather than building my where clause for each one of these individually I thought I'd use some slight of hand and try building custom attribute suppling the necessary information and then using reflection to build out each of my predicate statements (Using LinqKit btw). Trouble is, that the code finds the appropriate values in the reflection code and successfully builds a predicate for the property, but the "where" doesn't seem to actually generate and my query always returns 0 records.
The attribute is simple:
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
public class FilterAttribute: Attribute
{
public FilterType FilterType { get; set; } //enum{ Object, Database}
public string FilterPath { get; set; }
//var predicate = PredicateBuilder.False<Metadata>();
}
And this is my method that builds out the query:
public List<ETracker.Objects.Item> Search(Search SearchObject, int Page, int PageSize)
{
var predicate = PredicateBuilder.False<ETracker.Objects.Item>();
Type t = typeof(Search);
IEnumerable<PropertyInfo> pi = t.GetProperties();
string title = string.Empty;
foreach (var property in pi)
{
if (Attribute.IsDefined(property, typeof(FilterAttribute)))
{
var attrs = property.GetCustomAttributes(typeof(FilterAttribute),true);
var value = property.GetValue(SearchObject, null);
if (property.Name == "Title")
title = (string)value;
predicate.Or(a => GetPropertyVal(a, ((FilterAttribute)attrs[0]).FilterPath) == value);
}
}
var res = dataContext.GetAllItems().Take(1000)
.Where(a => SearchObject.Subcategories.Select(b => b.ID).ToArray().Contains(a.SubCategory.ID))
.Where(predicate);
return res.ToList();
}
The SearchObject is quite simple:
public class Search
{
public List<Item> Items { get; set; }
[Filter(FilterType = FilterType.Object, FilterPath = "Title")]
public string Title { get; set; }
...
}
Any suggestions will be greatly appreciated. I may well be going way the wrong direction and will take no offense if someone has a better alternative (or at least one that works)
You're not assigning your predicate anywhere. Change the line to this:
predicate = predicate.Or(a => GetPropertyVal(a, ((FilterAttribute)attrs[0]).FilterPath) == value);
I know I can map two object types with LINQ using a projection as so:
var destModel = from m in sourceModel
select new DestModelType {A = m.A, C = m.C, E = m.E}
where
class SourceModelType
{
string A {get; set;}
string B {get; set;}
string C {get; set;}
string D {get; set;}
string E {get; set;}
}
class DestModelType
{
string A {get; set;}
string C {get; set;}
string E {get; set;}
}
But what if I want to make something like a generic to do this, where I don't know specifically the two types I am dealing with. So it would walk the "Dest" type and match with the matching "Source" types.. is this possible? Also, to achieve deferred execution, I would want it just to return an IQueryable.
For example:
public IQueryable<TDest> ProjectionMap<TSource, TDest>(IQueryable<TSource> sourceModel)
{
// dynamically build the LINQ projection based on the properties in TDest
// return the IQueryable containing the constructed projection
}
I know this is challenging, but I hope not impossible, because it will save me a bunch of explicit mapping work between models and viewmodels.
You have to generate an expression tree, but a simple one, so it's not so hard...
void Main()
{
var source = new[]
{
new SourceModelType { A = "hello", B = "world", C = "foo", D = "bar", E = "Baz" },
new SourceModelType { A = "The", B = "answer", C = "is", D = "42", E = "!" }
};
var dest = ProjectionMap<SourceModelType, DestModelType>(source.AsQueryable());
dest.Dump();
}
public static IQueryable<TDest> ProjectionMap<TSource, TDest>(IQueryable<TSource> sourceModel)
where TDest : new()
{
var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
var destProperties = typeof(TDest).GetProperties().Where(p => p.CanWrite);
var propertyMap = from d in destProperties
join s in sourceProperties on new { d.Name, d.PropertyType } equals new { s.Name, s.PropertyType }
select new { Source = s, Dest = d };
var itemParam = Expression.Parameter(typeof(TSource), "item");
var memberBindings = propertyMap.Select(p => (MemberBinding)Expression.Bind(p.Dest, Expression.Property(itemParam, p.Source)));
var newExpression = Expression.New(typeof(TDest));
var memberInitExpression = Expression.MemberInit(newExpression, memberBindings);
var projection = Expression.Lambda<Func<TSource, TDest>>(memberInitExpression, itemParam);
projection.Dump();
return sourceModel.Select(projection);
}
(tested in LinqPad, hence the Dumps)
The generated projection expression looks like that :
item => new DestModelType() {A = item.A, C = item.C, E = item.E}