I want an EF query to return an entire column, but I want to choose that column with a variable. Can that be done? I.E. use a variable instead of a lambda expression as such:
FieldValues = db.PbiData
.Where(x => DatasetList.Contains(x.DatasetId))
.Select(FieldName)
.ToList()
FieldName will always match one of the column names of the PbiData table. For example, the first FieldName value is "Department", and the query below works just fine:
FieldValues = db.PbiData
.Where(x => DatasetList.Contains(x.DatasetId))
.Select(x=>x.Department)
.ToList()
The where clause in each of these queries simply restricts the data returned to the data relevant to the current user.
My attempt, per a comment below:
foreach(var F in model.FieldMetaData)
{
if (F.FieldType == "String")
{
PbiFieldData PbiFieldData = new PbiFieldData()
{
FieldName = F.FieldName,
FieldValues = await db.PbiData.Where(x => DatasetList.Contains(x.DatasetId)).Select(F.FieldName).ToListAsync()
};
model.PbiData.Add(PbiFieldData);
};
}
yes, you can use Expression
ParameterExpression param = Expression.Parameter(typeof(Table), yourVariable);
MemberExpression propExpression = Expression.PropertyOrField(param, yourVariable);
Expression<Func<Table, string>> selector = Expression.Lambda<Func<Table, string>>(propExpression, param);
var result = db.Table.Select(selector).First();
You could use System.Linq.Dynamic Nuget for the purpose
var fieldName = "Department";
var results = context.PbiData
.Where(x=> DatasetList.Contains(x.DatasetId))
.Select(fieldName);
Based on comment, to convert to List, one can use
var results = await context.PbiData
.Where(x=> DatasetList.Contains(x.DatasetId))
.Select(fieldName).ToListAsync();
You can write native sql query:
var FieldValues = ctx.Database.SqlQuery<string>
(
$#"select {fieldName} from PbiData
where DatasetId in ({string.Join(", ", DatasetList.Select(x => $"'{x}'"))})"
).ToList();
Related
I have a question for you regarding the creation of a Dynamic select query in Entity Framework.
I already have a dynamic query for the select based on rights etc. But for each table I get 30+ fields that I have to parse via the .GetType().GetProperties().
Its complex and its quite costly in terms of resource due to the amount of data we have.
I have a service that tells me which fields I should select for each table. I would like to find a way to transform that into the query but I can't find something that is really dynamic.
That is not dynamic but manual:
using (var context = new StackOverflowContext())
{
var posts = context.Posts
.Where(p => p.Tags == "<sql-server>")
.Select(p => new {p.Id, p.Title});
// Do something;
}
I need to say, select only those fields but only the fields with this names.
I have the field list in a list of string but that could be changed.
Could you please help me?
Here is a .Net Fiddle code (made by msbendtsen) that allows to dynamically select columns (properties).
https://dotnetfiddle.net/3IMR1r
The sample is written for linq to objects but it should work with entity frameworks.
The key section is:
internal static IQueryable SelectProperties<T>(this IQueryable<T> queryable, IEnumerable<string> propertyNames)
{
// get propertyinfo's from original type
var properties = typeof(T).GetProperties().Where(p => propertyNames.Contains(p.Name));
// Create the x => expression
var lambdaParameterExpression = Expression.Parameter(typeof(T));
// Create the x.<propertyName>'s
var propertyExpressions = properties.Select(p => Expression.Property(lambdaParameterExpression, p));
// Creating anonymous type using dictionary of property name and property type
var anonymousType = AnonymousTypeUtils.CreateType(properties.ToDictionary(p => p.Name, p => p.PropertyType));
var anonymousTypeConstructor = anonymousType.GetConstructors().Single();
var anonymousTypeMembers = anonymousType.GetProperties().Cast<MemberInfo>().ToArray();
// Create the new {} expression using
var anonymousTypeNewExpression = Expression.New(anonymousTypeConstructor, propertyExpressions, anonymousTypeMembers);
var selectLambdaMethod = GetExpressionLambdaMethod(lambdaParameterExpression.Type, anonymousType);
var selectBodyLambdaParameters = new object[] { anonymousTypeNewExpression, new[] { lambdaParameterExpression } };
var selectBodyLambdaExpression = (LambdaExpression)selectLambdaMethod.Invoke(null, selectBodyLambdaParameters);
var selectMethod = GetQueryableSelectMethod(typeof(T), anonymousType);
var selectedQueryable = selectMethod.Invoke(null, new object[] { queryable, selectBodyLambdaExpression }) as IQueryable;
return selectedQueryable;
}
I am trying to come up with a utility method to build a Linq Query or Linq Predicate to add to an Linq to EF query to do search for all terms in a list of terms in a variable number of columns.
I am trying to use PredicateBuilder to build the where clause. With one search term and a fixed list of columns it is relatively easy.
The pseudo code that I am trying to work up looks like this so far:
private static Predicate<Project> CreateDynamicSearch(IEnumerable<strings> searchableColumns, string[] searchTerms)
{
var predicate = PredicateBuilder.True<Project>();
foreach (var columnName in searchableColumns)
{
foreach (var term in searchTerms)
{
predicate = predicate.And(a => a.**columnName**.Contains(term));
}
predicate = predicate.Or(predicate);
}
return predicate;
}
The biggest issue I have is handling the expression for the columnName. Previous advice was to use an expression tree but I do not understand how that works into this scenario.
** Update **
I've taken the code as you have it after the update. It builds but when I actually make the call it errors on the Extension.Property(param,columnName); line, with the error Instance property 'Name' is not defined for type 'System.Func`2[Myclass,System.Boolean]' message. The columnName = "Name"
** Update 2 **
The way it's called:
var test = CreateDynamicSearch<Func<Project, bool>>(searchCols, searchTerms);
You can build expression for predicate yourself, in this case it's relatively easy:
private static Expression<Func<T, bool>> CreateDynamicSearch<T>(IEnumerable<string> searchableColumns, string[] searchTerms) {
// start with true, since we combine with AND
// and true AND anything is the same as just anything
var predicate = PredicateBuilder.True<T>();
foreach (var columnName in searchableColumns) {
// start with false, because we combine with OR
// and false OR anything is the same as just anything
var columnFilter = PredicateBuilder.False<T>();
foreach (var term in searchTerms) {
// a =>
var param = Expression.Parameter(typeof(T), "a");
// a => a.ColumnName
var prop = Expression.Property(param, columnName);
// a => a.ColumnName.Contains(term)
var call = Expression.Call(prop, "Contains", new Type[0], Expression.Constant(term));
columnFilter = columnFilter.Or(Expression.Lambda<Func<T, bool>>(call, param));
}
predicate = predicate.And(columnFilter);
}
return predicate;
}
In response to comment
I was just curious if there was some way you could combine the
expression created by Expression.Property(param, columnName) with the
one the compiler generates for (string s) -> s.Contains(term)
You can do that with (for example) like this:
// a =>
var param = Expression.Parameter(typeof(T), "a");
// a => a.ColumnName
var prop = Expression.Property(param, columnName);
// s => s.Contains(term)
Expression<Func<string, bool>> contains = (string s) => s.Contains(term);
// extract body - s.Contains(term)
var containsBody = (MethodCallExpression)contains.Body;
// replace "s" parameter with our property - a.ColumnName.Contains(term)
// Update accepts new target as first parameter (old target in this case is
// "s" parameter and new target is "a.ColumnName")
// and list of arguments (in this case it's "term" - we don't need to update that).
//
var call = containsBody.Update(prop, containsBody.Arguments);
columnFilter = columnFilter.Or(Expression.Lambda<Func<T, bool>>(call, param));
I have expression:
var newValues = MetarDecoder.Decode(telegram)
.OfType<MeteoParameter<decimal>>()
.Select(parameter => MeteoParameterFactory
.Create(parameter.ParameterId, parameter.DateTime.ToLocalTime(), parameter.Status, parameter.Value))
.ToList();
MeteoParameterFactory cannot be changed for some reasons, just take it as it is.
MeteoParameter also have string Info property.
I need to copy Info from old parameter to MeteoParameterFactory.Create() result.
Without LINQ it looks like:
var val = MetarDecoder.Decode(telegram).OfType<MeteoParameter<decimal>>().ToList();
foreach (var param in val)
{
var parameter = MeteoParameterFactory.Create(param.ParameterId, param.DateTime.ToLocalTime(), param.Status, param.Value);
parameter.Info = param.Info;
newValues.Add(parameter);
}
So, is there any way to add this part in LINQ expression shown below?
In Select you can create an anonymous function that returns the parameter created inside of it.
var newValues = MetarDecoder.Decode(telegram)
.OfType<MeteoParameter<decimal>>()
.Select(param => {
var parameter = MeteoParameterFactory.Create(param.ParameterId, param.DateTime.ToLocalTime(), param.Status, param.Value);
parameter.Info = param.Info;
return parameter;
}).ToList();
var val = MetarDecoder
.Decode(telegram)
.OfType<MeteoParameter<decimal>>()
.ToList()
.ForEach(param =>
{
var parameter = MeteoParameterFactory.Create(param.ParameterId, param.DateTime.ToLocalTime(), param.Status, param.Value);
parameter.Info = param.Info;
newValues.Add(parameter);
});
I'm working on the Sitecore ContentSearch API.I have indexed a set of fields in an Item.is it possible to query more than one fields at the same time?
public static List<MovieSearchResultItem> SearchResults(string txtQuery)
{
using (var context = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
{
var query = context.GetQueryable<MovieSearchResultItem>().Where(result => result.Body.Contains(txtQuery)).ToList();
return query;
}
}
In the above query I'm just using the Body field.how to include more than one fields.My search data might be title or body or someother field so i want to check all three fields.
Try to add another condition to the Where clause:
var query = context.GetQueryable<MovieSearchResultItem>()
.Where(result => result.Body.Contains(txtQuery)
|| result.Title.Contains(txtQuery)
|| result.OtherField.Contains(txtQuery)).ToList();
Simple enough, you can add it expression like so:
var query = context.GetQueryable<MovieSearchResultItem>().Where(result => result.Body.Contains(txtQuery) || result.Title.Contains(txtQuery))
Or you can use the PredicateBuilder, more info on that right here:
http://www.sitecore.net/Learn/Blogs/Technical-Blogs/Sitecore-7-Development-Team/Posts/2013/05/Sitecore-7-Predicate-Builder.aspx
I can recommend the PredicateBuilder. Example:
Expression<Func<SearchResultItem, bool>> predicate = PredicateBuilder.True<SearchResultItem>();
predicate = predicate.And(p => p.TemplateName.Equals("News"));
predicate = predicate.And(p => p.Language == Context.Language.Name);
List<SearchResultItem> results = context
.GetQueryable<SearchResultItem>()
.Where(predicate)
.ToList();
I have a few checkboxes on top of my webpage which correspond to the columns of my Table. Like there is a column studentId then there will be a checkbox studentId and so on. I want to write such a linq/lamda expression for List which will filter the on the basis of the checkboxes selected. For example if selects studentId and studentType checkbox then linq/lambda expression should bring all the rows matching the selection.
example:
If studentId and studentType checked then:
foreach (Child c in SomeList)
{
if (chkStudentId.checked)
{
List.FindAll (h=> h.StudentId == c.studentId);
}
if (chkStudentType.checked)
{
List.FindAll (h => h.StudentType == c.studentType)
}
}
}
I can't figure out how am I going to write such a code that if user selects multiple checkboxes, the query should compare to all the columns and bring the values based only on the checkboxes checked. The above is only static and does not help. please help. Thanks.
Expression trees are a great help if you want your query to be totally dynamic. But if the number of checkboxes is static you can also choose for the following solution:
var students = <your list of students>.AsQueryable();
if ( chkStudentId.checked)
{
students = students.Where(s => s.StudentId == c.StudentId);
}
if (chkStudentType.checked))
{
students = students.Where(s => s.StudentType== h.StudentType);
}
In such a way you can combine the where clauses in a dynamic way.
Disclaimer: I'm fairly new to this and there is probably much better ways to do this. Any feedback is higlhy appreciated.
Note that this method has no error/null checking. Since it uses reflection, you should profile it if you plan to use it in production.
public static IEnumerable<T> Filter<T>(IEnumerable<T> collection, Dictionary<string, object> filters) {
var type = typeof (T);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var queryable = collection.AsQueryable();
var instance = Expression.Parameter(type, "instance");
var expressions = new Stack<Expression>();
foreach (var filter in filters) {
var propertyName = filter.Key;
var property = properties.FirstOrDefault(x => x.Name == propertyName);
if (property == null)
continue;
var left = Expression.Property(instance, property);
var right = Expression.Constant(filter.Value, property.PropertyType);
var expr = Expression.Equal(left, right);
expressions.Push(expr);
}
Expression call = null;
Expression previousExpression = null;
while(expressions.Count > 0) {
var expr = expressions.Pop();
if(previousExpression == null) {
previousExpression = expr;
call = expr;
} else {
var and = Expression.AndAlso(previousExpression, expr);
call = and;
previousExpression = and;
}
}
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { queryable.ElementType },
queryable.Expression,
Expression.Lambda<Func<T, bool>>(call, new[] { instance }));
return queryable.Provider.CreateQuery<T>(whereCallExpression);
}
It overates all filters and tries to find a matching property. If it does find a property, it creates a EqualExpression which compares the actual value and the value you want to filter by. It then creates a MethodCallExpression which is passed to the query provider.
Theese expressions are then combined. I think the part with the stack is wrong, and that there is a better way to do it.
Usage:
var persons = new List<Person> {new Person {Name = "Alex", Age = 22}, new Person {Name = "Jesper", Age = 30}};
var filters = new Dictionary<string, object>();
filters.Add("Name", "Alexander Nyquist");
var results = Filter(persons, filters);
Since it's building expressions, it does works with Linq 2 Sql (tested) and probably Entity Framework. Linq 2 sql produces the following query:
SELECT [t0].[Id], [t0].[Name], [t0].[Email]
FROM [dbo].[Persons] AS [t0]
WHERE [t0].[Name] = #p0
-- #p0: Input VarChar (Size = 8000; Prec = 0; Scale = 0) [Alexander Nyquist]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
Hope this helps.
From your description of your problem it seemed fairly straight forward that you have a list of predicates, that are enabled or disable based on the check boxes, and then you want to aggregate them to create a single composite filter. Easy!
Your predicates look like this:
h => h.StudentId == c.studentId
h => h.StudentType == c.studentType
But because of the check boxes, you really want them to look like this:
h => chkStudentId.Checked ? h.StudentId == c.studentId : true
h => chkStudentId.Checked ? h.StudentType == c.studentType : true
You're effectively extending the predicates to include the check box. Here's a function that does that:
Func<CheckBox, Func<Student, bool>, Func<Student, bool>> extend =
(cb, p) =>
s => cb.Checked ? p(s) : true;
Now you can write your list of predicates like this:
var predicates = new Func<Student, bool>[]
{
extend(chkStudentId, h => h.StudentId == c.studentId),
extend(chkStudentType, h => h.StudentType == c.studentType),
// etc
};
Next LINQ has an easy way to turn a list of somethings into a single something:
Func<Student, bool>
predicate =
predicates.Aggregate((a, p) => s => a(s) && p(s));
Now you just have to execute this code to get your filtered list:
var filtered = SomeList.Where(predicate);
Now anytime any of the check boxes change you just need to enumerate the filtered collection and you'll get your filtered results.
Expression Trees can do what you want, but they are rather complicated. They allow you to dynamically build the linq query.
http://msdn.microsoft.com/en-us/library/bb882637.aspx
http://msdn.microsoft.com/en-us/library/bb397951.aspx
The Dynamic Linq Library may also be of use. I haven't personally used it though.
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
The solution that worked for me was a bit modification of what Wouter de Kort
suggest and was like this:
var students = StudentList.AsQueryable();
foreach (Student sc in GroupingList)
{
students = students.Where(s => (chkStudentId.Checked? h.StudentId.Trim() == sc.StudentId.Trim() : true) && (chkStudentType.Checked? h.StudentType.Trim() == sc.StudentType.Trim() : true) );
}