Convert foreach to linq expression with select - c#

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);
});

Related

Lambda Select Columns Without an Expression

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();

Converting object returned from Linq.Dynamic

I am using the Linq.Dynamic Library and EF6. I am attempting to select only the fields from a database that my user selects.
However, all that is returned is a List<object>, I have attempted to Cast<dynamic>, and every way I can think of, but no matter what the object has 0 fields.
I have also tried explicitly declaring as an IEnumerable and that too was unsuccessful, and was unable to call .ToList(), without first calling Cast<T> which too was unsuccessful.
When converting one of the objects to string I get: "{filenumber=345400, custom2=, custom3=, custom6=4076995332, custom8=4072121417}".
The data is being returned I simply cannot cast it to the appropriate type.
var query = cmax.dbases
.Where(w => statuses.Any(a => w.statusname == a) && portfolios.Any(a => w.portfolio == a))
.Select(string.Format("new ({0})", string.Join(",", fields)))
.Take(Math.Min((int) takeAmount, count - taken));
var take = await query.ToListAsync();
take.ForEach(data => {
var type = take.GetType();
var properties = type.GetProperties();
var propCount = properties.Count();
properties.ToList().ForEach(prop => {
var name = prop.Name;
});
});
In one of my use cases I have converted the results to List<string[]> through an extension for IQueryable. As I know my column order in Select("New (columnNames...) "), I could easily figure out which one is which.
so here is the code of the extension
public static IList<string[]> ToStringArray(this IQueryable queryFinal)
{
var query = (IQueryable<dynamic>)queryFinal;
IList<dynamic> data = query.ToList();
System.Reflection.PropertyInfo[] props = null;
if (data.Count > 0) props = data[0].GetType().GetProperties();
if (props == null) return new List<string[]>();
/*I do other things using reflection here*/
return data.Select(d => props.Select(p => (p.GetValue(d, null) ?? string.Empty).ToString()).OfType<string>().ToArray()).ToList();
}
use it as
var result = query.ToStringArray();
The only viable solution I found was to manually parse the data in the string:
take.ForEach(data =>
{
var dbtrData = new DebtorData();
var cleaned = data.ToString().Replace("{", "").Replace("}", "");
var pairs = cleaned.Split(',');
pairs.ToList().ForEach(pair =>
{
var key = pair.Split('=')[0].Replace(" ", "");
var value = pair.Split('=')[1];
//USEDATA
}
});

Writing a basic Expression Builder

I am using Linq-to-Entities and one query requires a property to be loaded dynamically. The query looks like this:
var found = Context.IntegrateViews.GroupBy(x => x.TypeOfBed)
.Select(type => new TypeCounts
{ Name = type.Key, Count = type.Count() }).ToList();
The property on which Group By clause is being run is TypeOfBed. Now I want to run this query on different properties e.g., next time I want to run it on, let's say x.TypeOfChair and so on. For this, I need to have this property in there dynamically.
I am trying to write a Expression builder which is like this.
public Expression<Func<LookupFilterItem>> PropertyLambda(string propertyName)
{
var param = Expression.Parameter(typeof(LookupFilterItem), "lookupItem");
//type of x in above example is LookupFilterItem
var propertyExpression = Expression.Property(param, propertyName);
var returnExpr = Expression.Lambda<Func<LookupFilterItem>>(propertyExpression);
return returnExpr;
//If I Pass string "TypeOfBed" to this method, I am expecting it to return something like
// lookupItem => lookupItem.TypeOfBed
}
And I intend to use it like this:
var found = Context.IntegrateViews.GroupBy(PropertyLambda("TypeOfBed"))
.Select(type => new TypeCounts
{ Name = type.Key, Count = type.Count() }).ToList();
But this is not working, I am getting a compile time error which says:
error CS0411: The type arguments for method
'System.Linq.Queryable.GroupBy(System.Linq.IQueryable,
System.Linq.Expressions.Expression>)' cannot
be inferred from the usage. Try specifying the type arguments
explicitly.
What am I doing wrong here?
This seems to be a syntax issue. Try this:
public Expression<Func<LookupFilterItem, string>> PropertyLambda(string propertyName)
{
var param = Expression.Parameter(typeof(LookupFilterItem), "lookupItem");
var propertyExpression = Expression.Property(param, propertyName);
var returnExpr = Expression.Lambda<Func<LookupFilterItem, string>>(propertyExpression, param);
return returnExpr;
}
And the usage:
var found = Context.IntegrateViews.GroupBy(PropertyLambda("TypeOfBed").Compile())
.Select(type => new TypeCounts
{ Name = type.Key, Count = type.Count() }).ToList();
Another option is to use LINQ Dynamic Query Library :
using System.Linq.Dynamic;
var found = Context.IntegrateViews.AsQueryable().GroupBy("new(TypeOfBed)", "it")
.Select(new (Key.TypeOfBed as Name, Count() as Count)).ToList();

Creating a dynamic query using IQueryable

I'm trying to iterate for over an string array and dynamically create a IQueryable query. Its pretty straightforward but here's where I'm stuck
var query = context.QuestionsMetaDatas.AsQueryable();
var keywords=new List<string>(){ "Test1","Test2" };
foreach(var key in keywords)
{
query=query.Where(a=>a.Text.Contains(key));
}
Now the problem is that when the query gets generated its compiles to
select * from QuestionsMetaDatas where Text Like "Test1" AND Text Like "Test2"
Instead of AND I wanted the query to generate OR...Now how can I achieve this?
I have used predicate builder like Raphael suggested, it's just one file to include in your project, then your example becomes:
var keywords=new List<string>(){ "Test1","Test2" };
var predicate = PredicateBuilder.False<QuestionsMetaDatas>();
foreach (var key in keywords)
{
predicate = predicate.Or (a => a.Text.Contains (key));
}
var query = context.QuestionsMetaDatas.AsQueryable().Where(predicate);
Generating the OR query your are looking for.
Have you tried contains the other way?
var keywords=new List<int>(){ "Test1","Test2" };
query=query.Where(a=>keywords.Contains(a));
this is like an IN clause
you could look at predicate builder, or build your own expression (here a possble solution with a static extension method on IQueryable<QuestionsMetadatas> )
public static IQueryable<QuestionsMetaDatas> FilterText(this IQueryable<QuestionsMetaDatas> queryable, IEnumerable<string> keywords)
{
var entityType = typeof(QuestionsMetaDatas);
var parameter = Expression.Parameter(entityType, "a");
var containsMethod = typeof(string).GetMethod("Contains"
, new[] { typeof(string) });
var propertyExpression = Expression.Property(parameter, "Text");
Expression body = Expression.Constant(false);
foreach (var keyword in keywords)
{
var innerExpression = Expression.Call(propertyExpression, containsMethod, Expression.Constant(keyword));
body = Expression.OrElse(body, innerExpression);
}
var lambda = Expression.Lambda<Func<QuestionsMetaDatas, bool>>(body, new[] { parameter });
return queryable.Where(lambda);
}
where lambda will look like that :
a => ((False OrElse a.Text.Contains("firstKeyWord")) OrElse a.Text.Contains("secondKeyWord"))
and usage is
var query = context.QuestionsMetaDatas.AsQueryable();
var keywords=new List<string>(){ "Test1","Test2" };
query = query.FilterText(keywords);
or shorter
var query = context.QuestionsMetaDatas.AsQueryable().FilterText(new[]{"Test1", "Test2"});

How to produce a Subquery using non-generic Lambda

How would you translate the following generic Lambda function into a lambda expression :
context.AssociateWith<Product>(p => p.Regions.Where(r => r.Country == 'Canada')
I'm trying to create a full lambda expression without any <T> or direct call. Something like :
void AddFilter(ITable table, MetaDataMember relation)
{
var tableParam = Expression.Parameter(table.ElementType, "e");
var prop = Expression.Property(tableParam, relation.Name);
var func = typeof(Func<,>).MakeGenericType(table.ElementType, relation.type)
var exp = Expression.Lambda(func, prop, tableParam);
}
This will produce e.Regions... but I'm unable to get the Where part from there...
I know I'm very late in the game with my answer and likely this is not the exact solution you are looking for (still uses the frequently), but maybe it will help you and others building their expression:
/*
example: Session.Query.Where(m => m.Regions.Where(f => f.Name.Equals("test")))
*/
var innerItem = Expression.Parameter(typeof(MyInnerClass), "f");
var innerProperty = Expression.Property(innerItem, "Name");
var innerMethod = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var innerSearchExpression = Expression.Constant(searchString, typeof(string));
var innerMethodExpression = Expression.Call(innerProperty, innerMethod, new[] { innerSearchExpression });
var innerLambda = Expression.Lambda<Func<MyInnerClass, bool>>(innerMethodExpression, innerItem);
var outerItem = Expression.Parameter(typeof(MyOuterClass), "m");
var outerProperty = Expression.Property(outerItem, info.Name);
/* calling a method extension defined in Enumerable */
var outerMethodExpression = Expression.Call(typeof(Enumerable), "Where", new[] { typeof(MyInnerClass) }, outerProperty, innerLambda);
var outerLambda = Expression.Lambda<Func<MyOuterClass, bool>>(outerMethodExpression, outerItem);
query = query.Where(outerLambda);
Based on an answer posted here: Creating a Linq expression dynamically containing a subquery.
Try this, it's not pretty but it gives you a valid expression for the whole structure. You could define the inner lambda as an expression but you would still have to compile it before you could pass it to Where(), so for the purposes of this answer it seems redundant.
Expression<Func<Product, IEnumerable<Region>>> getRegions =
p => p.Regions.Where(r => r.Country == "Canada");

Categories