Create Multiple Objects Single LINQ EF Method - c#

List<MyObject> objects = await item.tables.ToAsyncEnumerable()
.Where(p => p.field1 == value)
.Select(p => new MyObject(p.field1,p.field2))
.ToList();
^ I have something like that, but what i'm wondering, is there anyway way to add a second object creation, in the same select? eg. new MyObject(p.field3,p.field4) ? and add it to the same list? order does not matter.
I know could do this with multiple calls to database or splitting up lists into sections, but is there way to do this in single line?

You could create it as a tuple.
List<Tuple<MyObject1, MyObject2>> = query.Select(x => Tuple.Create(
new MyObject1
{
// fields
},
new MyObject2
{
//fields
}))
.ToList();
From my testing in Linqpad, it seems that this will only hit the database once.
Alternatively, you could just select all the fields you know you'll need from the database to create both:
var myList = query.Select(x => new { FieldA = x.FieldA, FieldB = x.FieldB }).ToList(); //hits db once
var object1s = myList.Select(x => new MyObject1(x.FieldA));
var object2s = myList.Select(x => new MyObject1(x.FieldB));
var bothLists = object1s.Concat(object2s).ToList();

What you'd want to do is use the SelectMany method in linq. Which will select all the items from an array. The array can be created anonymously as seen below.
List<MyObject> objects = await item.tables.ToAsyncEnumerable()
.Where(p => p.field1 == value)
.SelectMany(p => new []{new MyObject(p.field1,p.field2), new MyObject(p.field3,p.field4)})
.ToList();
Hope that solves you problem!

If you use query syntax instead of method chaining, you can use the let operator to accomplish this. Note that the SQL generated may not be exactly performant as this article shows, but it should work for you if you're after a subquery.

You could try creating an array of objects and then flattening with SelectMany:
List<MyObject> objects = await item.tables.ToAsyncEnumerable()
.Where(p => p.field1 == value)
.Select(p => new [] {
new MyObject(p.field1,p.field2),
new MyObject(p.field3,p.field4)
})
.SelectMany(g => g)
.ToList();
But I suspect you'll have problems getting EF to translate that to a query.

Related

LINQ Lambda efficiency of code groupby orderby

I have this code, but I think that it could run faster, or I just hope to. But I have plenty of data. I'd like to have it as effective as it can be.
Here is the code:
(Need to return newest translations of words (Language and value) from resources grouped by resource and language based on Expression<Func<ResourcesTranslation, bool>> ConditionExpression)
KeyValues = item.Resources
.Where(ConditionExpression)
.GroupBy(g => new { g.ResourceId, g.Language })
.Select(m => m.OrderByDescending(o => o.Changed ?? o.Created))
.Select( s => new KeyValues
{
Language = s.FirstOrDefault().Language,
KeyValue = s.FirstOrDefault().Value
}).ToList();
As you need only one element after grouping, you can return it right in GroupBy clause, it will simplify your code:
KeyValues = item.Resources
.Where(ConditionExpression)
.GroupBy(g => new { g.ResourceId, g.Language },
(x, y) => new { Max = y.OrderByDescending(o => o.Changed ?? o.Created).First() })
.Select(s => new KeyValues
{
Language = s.Max.Language,
KeyValue = s.Max.Value
})
.ToList();
Even though you can get some performance by removing the first, unneeded select (depending on the volume of data this could be minimal to medium improvement) like this:
KeyValues = item.Resources
.Where(ConditionExpression)
.GroupBy(g => new { g.ResourceId, g.Language })
.OrderByDescending(o => o.Changed.HasValue ? o.Changed : o.Created)
.Select( s => new KeyValues
{
Language = s.Language,
KeyValue = s.Value
}).ToList();
Depending on your case, you could:
If your data is in a database, you can create database improvements like adding indexes, updating statistics, using hints etc.
if this is local data, you can use some strategy to split new and old data between various enumerables.
There is no other way to significantly improve your linq query. You need to find another strategy to achieve that.
I found out that Visual Studio translates it in to selects, so I realized that, the best solution for stuff like this is to make some View.. Just giving answer to own Q for another guys.

How do I select all distinct strings in a list of lists of another type?

I'm still very new with LINQ. I have the following "simplified" data structure:
List<List<Field>> myData = new List<List<Field>>();
Field consists of two string members, Type and Name.
My goal is to get a List<string> containing all distinct Name corresponding to a given Type. My first approach is this:
var test = myData
.Where(a => a.FindAll(b => b.Type.Equals("testType"))
.Select(c => c.Name)
.Distinct());
Does somebody have a hint for me? =)
You just need to use SelectMany to flatten your list of lists and then proceed as normal
var test = myData.SelectMany(x => x)
.Where(x => x.Type == "testType")
.Select(x => x.Name)
.Distinct()
.ToList();
Or in query syntax
var test = (from subList in myData
from item in subList
where item.Type == "testType"
select item.Name).Distinct().ToList();
Another way to do it using query notation:
var test= from list in myData
from e in list
where e.Type=="testType"
group e.Name by e.Name into g
select g.Key;
But is better go for one of the #juharr's solutions

Group By struct list on multiple columns in C#

I am having a struct as
public struct structMailJob
{
public string ID;
public string MailID;
public int ResendCount;
public int PageCount;
}
and a list as
List<structMailJob> myStructList = new List<structMailJob>();
I have loaded data in myStructList from database and want myStructList data in a new list after grouping by MailID and ResendCount.
I am trying as:
List<structMailJob> newStructList = new List<structMailJob>();
newStructList = myStructList.GroupBy(u => u.MailID, u=>u.ResendCount)
.Select(grp => new { myStructList = grp.ToList() })
.ToList();
but unable to do that as getting error message - cant implicitly convert generic list to structMailJob.
I think that you are looking for is the following:
var newStructList = myStructList.GroupBy(smj => new { smj.MailID, smj.ResendCount })
.Select(grp => new
{
MailID = grp.Key.MailID,
ResendCount = grp.Key.ResendCount
MailJobs = grp.Select(x=>new
{
x.ID,
x.PageCount
}).ToList()
})
.ToList();
Note that we changed the GroupBy clause to the following one:
GroupBy(smj => new { smj.MailID, smj.ResendCount })
Doing so, the key on which the groups would be created would be consisted of both MailID and ResendCount. By the way the former GroupBy clause isn't correct.
Then having done the grouping, we project each group to an object with three properties, MailID and ResendCout, which are the components of the key and list of anonymous type object with two properties, ID and PageCount, which we gave it the name MailJobs.
Last but not least you will notice that I didn't mention the following
List<structMailJob> newStructList = new List<structMailJob>();
I just used the var and declared the newStructList. I don't think that you stated in your post makes sense. How do we expect to get a list of the same objects after grouping them? So I assumed that you might want is the above.
However, I thought you might want also something like this and you didn't want to refer to Grouping.
myStructList = myStructList.OrderBy(smj => smj.MailID)
.ThenBy(smj => smj.ResendCount)
.ToList();
Linq Query is completely incorrect, following are the important points:
myStructList.GroupBy(u => u.MailID, u=>u.ResendCount) // Incorrect grouping
myStructList.GroupBy(u => new {u.MailID, u.ResendCount }) // Correct grouping, which will do by two columns MailID and ResendCount, last one was only doing by MailID and was using ResendCount for result projection
Now the result is of type IEnumerable<IGrouping<AnonymousType,structMailJob>>, so when you do something like Select, it will end up creating Concatenated List of type IEnumerable<List<structMailJob>> (Removed the assignment to myStructList inside the Select, as that was not correct):
.Select(grp => grp.ToList())
Correct code would require you to flatten using SelectMany as follows:
newStructList = myStructList.GroupBy(u => new {u.MailID, u.ResendCount})
.SelectMany(grp => grp.ToList()).ToList();
Assign it to newStructList, but this code has little use, since literally newStructList is exactly same as myStructList post flattening, ideally you shall be able to use the grouping, so that you can get a subset and thus the correct result, however that depends on your business logic
I don't know if I got your question right but it seems to me you missed the 'Group by' signature.
List<structMailJob> myStructList = new List<structMailJob>();
List<structMailJob> newStructList = new List<structMailJob>();
newStructList = myStructList
// .GroupBy(/*Key Selector */u => u.MailID, /*Element Selector*/u=>u.ResendCount)
.GroupBy(u => new { u.MailID, u.ResendCount }) // broup by MailID, ResendCount
// Note no Element Selector , the 'STRUCT' is 'SELECTED'
.Select(grp => {
// NOte: Key == Anonymous {MailID, ResendCount }
return grp;
})
// otherwise you get a IEnumerable<IEnumerable<T>> instead of IEnumerable<T> because you grouped it
.SelectMany(x=>x)
.ToList();
If Mrinal Kamboj's answer is what you are looking for, then you could use the following as an alternative:
var orderedList = myStructList.OrderBy(x => x.MailID).ThenBy(x => x.ResendCount);

Linq using Distinct() in C# Lambda Expression

SFC.OrderFormModifiedMonitoringRecords
.SelectMany(q => q.TimeModify, w => w.DateModify)
.Distinct()
.OrderBy(t => t)
.SelectMany(t => new { RowID = t.rowID, OFnum = t.OFNo });
It's Error did i missed something or is it Completely Coded The Wrong Way? After this i'm gonna use this on a Foreach method to gather up multiple data without the duplicates.
The delegate you pass to SelectMany must return an IEnumerable and is for collapsing multiple collections into one. So yes, something's definitely wrong here. I think you've confused it with Select which simply maps one collection to another.
Without knowing what your goal is, it's hard to know exactly how to fix it, but I'm guessing you want something like this:
SFC.OrderFormModifiedMonitoringRecords
.OrderBy(t => t.DateModify)
.ThenBy(t => t.TimeModify)
.Select(t => new { RowID = t.rowID, OFnum = t.OFNo })
.Distinct();
Or in query syntax:
(from t in SFC.OrderFormModifiedMonitoringRecords
orderby t.DateModify, t.TimeModify
select new { RowID = t.rowID, OFnum = t.OFNo })
.Distinct();
This will order the records by DateModify then by TimeModify, select two properties, rowID and OFNo and return only distinct pairs of values.

How to put an Entity Framework query result into a List

How to Put the following query result into a List
var result = from c in sb.Swithches_SW_PanalComponents
select new { c.ID,c.SW_PanalComponents.ComponentsName,c.ComponentValue };
FINAL EDIT
Based on your last comment, this is all you ever needed
List<Swithches_SW_PanalComponents> result =
sb.Swithches_SW_PanalComponents.ToList();
which of course is identical to
var result = sb.Swithches_SW_PanalComponents.ToList();
EDIT
Based on your comments, I think this is what you want:
List<SW_PanalComponents> result = sb.Swithches_SW_PanalComponents
.Select(c => new SW_PanalComponents { /* initialize your fields */ })
.ToList();
END EDIT
The ToList method is what you want. But consider using dot notation. For simple queries like this, it's much cleaner and trimmer.
var result = sb.Swithches_SW_PanalComponents
.Select(c => new { c.ID, c.SW_PanalComponents.ComponentsName, c.ComponentValue })
.ToList();
Also note that, if you're just trying to execute your query immediately, and only need to enumerate over it, you can also call AsEnumerable()
var result = sb.Swithches_SW_PanalComponents
.Select(c => new { c.ID, c.SW_PanalComponents.ComponentsName, c.ComponentValue })
.AsEnumerable();
The advantage here is that result is a less specific type—IEnumerablt<T>.
Like this:
var result =(from c in sb.Swithches_SW_PanalComponents
select new
{ c.ID,
c.SW_PanalComponents.ComponentsName,
c.ComponentValue
}).ToList();
That what i came with finally:
List<Swithches_SW_PanalComponents> MyList = new List<Swithches_SW_PanalComponents>();
var Result = from all in sb.Swithches_SW_PanalComponents
select all
;
MyList.AddRange(Result.ToList<Swithches_SW_PanalComponents>());

Categories

Resources