Using Function in Select Clause of Entity Framework Query - c#

I would like to implement the following logic against Entity Frameworks.
var items = from item in myContext
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};
protected int TweakValue(int value)
{
// Custom processing here
return value;
}
This won't work because of the call to TweakValue() in the select clause. I understand that the query is converted to SQL, and that the problem is that TweakValue() cannot be converted to SQL. My question is what is the most economical way to implement this. Do I need a second loop to convert the values?
I'm still trying to get comfortable with LINQ expressions.

The simplest way is probably to just "move" the execution to the client to perform the transformation. In this case you'd just use:
var items = myContext.Select(item => new { item.Value1, item.Value2 })
.AsEnumerable()
.Select(item => new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
});
Note that you don't have to reuse the names for Value1 and Value2 - it's just easiest to do so.
If you really want to use query expressions:
var query = from item in myContext
select new { item.Value1, item.Value2 };
var items = from item in query.AsEnumerable()
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};
If you want to perform filtering first, you can get that to occur in the database by putting the filtering, ordering etc before the call to AsEnumerable(). For example:
var query = from item in myContext
where item.Foo == bar
orderby item.Something
select new { item.Value1, item.Value2 };
var items = from item in query.AsEnumerable()
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};

You don't need a loop, just another projection:
var items = myContext.Select(i => new {
Value1 = item.Value1,
Value2 = item.Value2
})
.AsEnumerable()
.Select(i => new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
});
Edit: Depending on what TweakValue actually does, you can push the whole thing to the server. Riffing on your current example:
public Expression<Func<Item, ItemProjection>> TweakValue()
{
return item => new ItemProjection
{
Value1 = item.Value1,
Value2 = item.Value2 + 0 // or something else L2E can understand...
};
}
Now use it like:
var exp = TweakValue();
var items = myContext.Select(exp);
Note I'm storing exp in a variable so that L2E doesn't try to directly invoke TweakValue in the query, which would fail.
Naturally, this only works if TweakValue does stuff that L2E can do.

Related

C# linq query with where subquery for each property

I have this table:
I want to do a linq query where I'll get each property value based on the adminfilterfieldid.
I have this so far:
newDto = from d in DbContext.AdminFilterItems
where d.AdminFilterID == filterId
select new ReservationDto
{
BookingDate = d.Value
};
Now since BookingDate is AdminFilterFieldId is 2, I was hoping I can do something like BookingDate = d.Value.Where(s => s.AdminFilterFieldID = 3) or null in case there is no value for that adminfilterfieldid.
I want to do this for all the fields for that adminfilterid.
Is that achievable somehow with this kind of query or will I need to do multiple queries?
It looks simple enough just another clause in your where.
from d in DbContext.AdminFilterItems
where d.AdminFilterID == filterId
&& d.AdminFilterFieldID = 3
select new ReservationDto
{
BookingDate = d.Value
};
If you need to filter it on multiple values just keep your initial query and run other filters on it then do your projection (select).
var qry = from d in DbContext.AdminFilterItems
where d.AdminFilterID == filterId
// Filter 1
qry.Where(d => d.AdminFilterFieldID = 3).Select(d => new ReservationDto
{
BookingDate = d.Value
});
// Filter 2
qry.Where(d => d.AdminFilterFieldID = 2).Select(d => new ReservationDto
{
BookingDate = d.Value
});

Reusable functions/expressions in LINQ to Entities select clauses

I have a lot of code that casts entities to data transfer objects similar to this:
var result = from t in DatabaseContext.SomeTable
where t.Value1 = someValue
select new SomeTableDTO()
{
Value1 = t.Value1,
Value2 = t.Value2,
SomeParent = new SomeParentDTO()
{
Value3 = t.SomeParent.Value3,
Value4 = t.SomeParent.Value4
}
};
This works, but the problem is I am repeating the same code over and over again because there are many DTOs that have a SomeParent property.
var result = from t in DatabaseContext.SomeOtherTable
where t.Value5 = someValue
select new SomeOtherTableDTO()
{
Value5 = t.Value5,
Value6 = t.Value6,
SomeParent = new SomeParentDTO()
{
Value3 = t.SomeParent.Value3,
Value4 = t.SomeParent.Value4
}
};
I'd like to do something like this so the conversion for the SomeParentDTO can be shared:
var result = from t in DatabaseContext.SomeTable
where t.Value1 = someValue
select new SomeTableDTO()
{
Value1 = t.Value1,
Value2 = t.Value2,
SomeParent = SomeParentConverter(t.SomeParent)
};
.
Func<SomeParent, SomeParentDTO> SomeParentConverter = (parent) =>
{
return new SomeParentDTO()
{
Value3 = parent.Value3,
Value4 = parent.Value4
};
};
But of course this doesn't work because Invoke is not supported in LINQ to Entities. This posting seems to be going in the direction I want, but that uses a single Expression that is passed to .Select(), and that doesn't solve my DRY problem.
Is there any way I can accomplish what I want?
You can use Automapper. It supports mappings in IQueryable extensions.
Part of the sample from the documentation:
public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
Mapper.CreateMap<OrderLine, OrderLineDTO>()
.ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name);
using (var context = new orderEntities())
{
return context.OrderLines.Where(ol => ol.OrderId == orderId)
.Project().To<OrderLineDTO>().ToList();
}
}

Incorrect sub-array returned from Linq

Preparing data for jqGrid I get an unexpected array of cells for the subgrid. The simplified code looks like:
var result = new
{
total = 1,
page = 1,
records = qstList.Count(),
rows = qstList.Select(( c, i ) => new
{
Id = c.QuestionId,
Text = c.Text,
Type = c.Type,
Points = c.Points,
Ordinal = c.Ordinal,
subgrid = new
{
subtotal = 1,
subpage = 1,
cell = qstList.Where(
q => q.QuestionId == c.QuestionId).Select(
q => q.Answers).Select((d, j) => new
{
Id = d.Select(a => a.AnswerId),
Text = d.Select(a => a.Text),
Correctness = d.Select(a => a.Correctness),
Ordinal = d.Select(a => a.Ordinal)
}).ToArray()
}
}).ToArray()
};
The rows are fine, but the array of cells for the subgrid are odd. I expected something like:
{[Id, Text, Correctness, Ordinal], ..., [Id, Text, Correctness, Ordinal]}
but it turns out to be:
{[Id, Id, ...], ..., [Ordinal, Ordinal, ...]}
How to get the expected "layout". Thanks for any help!
This will lead you to the right answer:
Create normal classes for all anonymous classes (Row, Subgrid, Cel etc.)
Refactor all code so that there is only one Linq statement on a line
Now it is much easier to debug als you can see each subresult of all the Linq statements. I am quite confident that you will find the right answer this way. (If not, just add the single Linq statement that not work to your post).
#Vladimir, thanks! Yes, the SelectMany will do:
subgrid = new
{
subtotal = 1,
subpage = 1,
cell = qstList.Where(q => q.QuestionId == c.QuestionId).SelectMany(q => q.Answers).Select((d, j) =>
new
{
Id = d.AnswerId,
Text = d.Text,
Correctness = d.Correctness,
Ordinal = d.Ordinal
}).ToArray()
}

Build a dynamic where clause over multiple properties

What I have is a List<string> IndexFields which contains a list of property names.
My issue is that I need to build a where clause based on the elements in the list.
So far I have;
var sitem = List1.Where(p => (p.GetType().GetProperty(IndexFields[0])
.GetValue(p, null) as string) == "red").FirstOrDefault();
But that only allows me to specify a single property. What I need is a builder that can build based on all the names in the List<string> IndexFields list.
The most flexible way to create dynamic queries at runtime is by using the Expression API:
For example:-
var type = typeof(T);
var properties = IndexFields.Select(x => type.GetProperty(x));
// x
var paramter = Expression.Parameter(type);
// x.Foo, x.Bar, ...
var leftHandSides = properties.Select(
x => Expression.Property(parameter, x));
// "Baz"
var rightHandSide = Expression.Constant(...);
// x.Foo == "Baz", x.Bar = "Baz", ...
var equalityExpressions = leftHandSides.Select(
x => Expression.Equal(x, rightHandSide));
// x.Foo == "Baz" && x.Bar == "Baz" && ...
var aggregatedExpressions = equalityExpressions.Aggregate(
(x, y) => Expression.AndAlso(x, y));
// x => x.Foo == "Baz" && x.Bar == "Baz" && ...
var lambda = Expression.Lambda<Func<T,bool>>(
aggregatedExpressions, parameter)
var item = List1.Where(lambda).FirstOrDefault();
A huge advantage of building your queries like this is that the resulting expression can still be e.g. translated into SQL for use with Entity Framework, wheras using reflection inside the body of your lambda is really limiting.
I really do recommend taking some time to really understand the expression framework before using it, though. If you can grok what's going on, it saves you a ton of time in the long run.
You can read more at e.g:-
http://www.digitallycreated.net/Blog/37/dynamic-queries-in-entity-framework-using-expression-trees
https://stackoverflow.com/questions/1217539/net-expression-trees-tutorial
If you're looking for something quicker and dirtier, however, you can just go ahead and chain up those Where clauses inside a foreach:-
IEnumerable<T> query = List1;
foreach (var property in IndexFields)
{
// The variable "property" gets hoisted out of local context
// which messes you up if the query is being evaluated with
// delayed execution.
// If you're working in C# 6 though, you don't need to do this.
var localProperty = property;
query = query.Where(
p => (p.GetType().GetProperty(localProperty)
.GetValue(p, null) as string) == "red");
}
var sitem = query.FirstOrDefault();
You can use a PredicateBuilder for this:
var predicate = PredicateBuilder.New<string>();
if (aCondition)
{
predicate = predicate.And(s => s == "this");
}
if (bCondition)
{
predicate = predicate.And(s => s == "that");
}
you can try something like this, worked for me in linqpad
void Main() {
var listFields = new string[] { "Field1", "Field2" };
var listValues = new string[] { "value1", "value2" };
// prepare & show dummy data
var listItems = Enumerable.Range(1, 100).Select(aaIndex => new MyItem {
Name = string.Format("item{0}", aaIndex),
Field1 = string.Format("value{0}", aaIndex % 3),
Field2 = string.Format("value{0}", aaIndex % 7)
});
listItems.Dump();
// apply filtering
var filtered = listItems.Where(aaItem => Enumerable.Range(0, listFields.Length).All(aaIndex => {
var value1 = aaItem.GetType().GetProperty(listFields[aaIndex]).GetValue(aaItem, null);
var value2 = listValues[aaIndex];
if (value1 is IComparable) {
return ((IComparable)value1).CompareTo(value2) == 0;
}
return Convert.ToString(value1) == Convert.ToString(value2);
}));
filtered.Dump();
}
// Define other methods and classes here
class MyItem {
public string Name { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
The dynamic linq library worked well for stuff like this:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
It added overloads to take the different clauses as strings ie:
var query = List1.Where("Color1=""Red"" or Color2=""Red""");
In your case you could build the string from your index fields (probably in a loop but simplified here)
var query = List1.Where(IndexFields[0] + "=""Red"" or " IndexFields[1] + "=Red");
For use, download the sample package, and then grab LinqSamples\DynamicQuery\DynamicQuery\Dynamic.cs and compile with your project.

Parse string and order by parsed value

i am trying to build linq expression to solve my problem. I have list of strings
List<string> arr = new List<string>();
arr.Add("<desc><ru>1</ru><en>3</en></desc>");
arr.Add("<desc><ru>2</ru><en>4</en></desc>");
i want to parse every item and order results
fake sample:
arr.Select(ParseItem("en")).OrderBy(x)
then we have two items in ru in order 1,2
Thanks for all and sorry for my bad English
Thanks for all response but how to convert now results to IQueryable
class Test { public string data { get; set; } }
List<Test> arr = new List<Test>();
arr.Add(new Test { data = "<desc><ru>AAA</ru><en>One</en></desc>" });
arr.Add(new Test { data = "<desc><ru>1</ru><en>Two</en></desc>" });
arr.Add(new Test { data = "<desc><ru>22</ru><en>Ab</en></desc>" });
IQueryable<Test> t = arr.AsQueryable();
// here the trouble how to convert to IQueryable<Test>
t = t.Select(s => XElement.Parse(s.data)).Select(x => x.Element("en")).
OrderBy(el => el.Value);
Thanks again
After the question update - this will return your ordered data by <en> node value:
var result = arr
.OrderBy(t=>
XElement.Parse(t.data).Element("en").Value
);
The result valiable is of IOrderedEnumerable<Test> type.
This will produce a list of the values in ru tags (assuming they are integers), ordered by the values in en tags (again, assuming integers).
List<string> items = arr.Select(s => XElement.Parse(s))
.OrderBy(xml => (int)xml.Element("en"))
.Select(xml => (int)xml.Element("ru"))
.ToList();
If you simply want to enumerate, you can omit the ToList call:
foreach (var item in arr.Select(s => XElement.Parse(s))
.OrderBy(xml => (int)xml.Element("en"))
.Select(xml => (int)xml.Element("ru")))
{
// do something with item
}
I'm not sure I've got what the excepted results are, but if you need to select values in en ordered by the value in ru then here it is:
var orderedItems = (
from item in arr
let x = XElement.Parse(item)
let ruValue = (int)x.Element("ru")
let enValue = (int)x.Element("en")
orderby ruValue
select enValue
).ToList();
I don't know if it is too late, but if you are wanting to parse the text and if it is an integer then sort by value otherwise sort by text, then this might help.
You need to define a function like this to enable parsing in LINQ expressions:
Func<string, int?> tryParseInteger = text =>
{
int? result = null;
int parsed;
if (int.TryParse(text, out parsed))
{
result = parsed;
}
return result;
};
Then you can do queries like this:
var xs = new [] { "Hello", "3ff", "4.5", "5", };
var rs =
(from x in xs
select tryParseInteger(x)).ToArray();
// rs == new int?[] { null, null, null, 5, };
In your case you possibly want something like this:
var elements = new []
{
"<desc><ru>AAA</ru></desc>",
"<desc><ru>1</ru></desc>",
"<desc><ru>42</ru></desc>",
"<desc><ru>-7</ru></desc>",
"<desc><ru>BBB</ru></desc>",
"<desc><ru>22</ru></desc>",
};
var query =
from e in elements
let xe = XElement.Parse(e)
let v = xe.Element("ru").Value
orderby v
orderby tryParseInteger(v)
select v;
Which would give you:
{ "AAA", "BBB", "-7", "1", "22", "42" }
If you want to treat non-integers (ie parsed as null) to be zero then change the query by using this line:
orderby tryParseInteger(v) ?? 0
Then you'll get this:
{ "-7", "AAA", "BBB", "1", "22", "42" }
I hope this helps.

Categories