How to change in elegant way List<> structure - c#

I am using LINQ to entitiy in my project.
I have this LINQ:
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
select new
{
Id = inspArch.Id,
clientId = inspArch.CustomerId,
authId = inspAuth.Id
}).ToList();
After LINQ is executed result has this value :
Is there any elegant way (for example using LINQ or change above existing LINQ) to create from the list above, new list like that:

I haven't built this to see if it compiles, but this should work. You need to aggregate the Id and AuthId fields.
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
select new
{
Id = inspArch.Id,
clientId = inspArch.CustomerId,
authId = inspAuth.Id
})
.GroupBy(g => g.clientId)
.select(s => new {
Id = string.Join(",", s.Select(ss => ss.Id.ToString())),
ClientId = s.Key,
AuthId = string.Join(",", s.Select(ss => ss.authId.ToString()).Distinct()),
}).ToList();

You need group by and you can apply String.Join on the resulting IGrouping:-
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
group new { inspArch, inspAuth } by inspArch.CustomerId into g
select new
{
Id = String.Join(",",g.Select(x => x.inspArch.Id),
clientId = x.Key,
authId = String.Join(",",g.Select(x => x.inspAuth.Id)
}).ToList();
The tricky part here is to group both objects i.e. new { inspArch, inspAuth } because we need to access properties from both.
Update:
Since this is entity framework, it won't be able to translate the method String.Join to SQL, so we can bring back the grouped object to memory using AsEnumerable and then project it like this:-
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
group new { inspArch, inspAuth } by inspArch.CustomerId into g
select g).AsEnumerable()
.Select(g => new
{
Id = String.Join(",",g.Select(x => x.inspArch.Id),
clientId = x.Key,
authId = String.Join(",",g.Select(x => x.inspAuth.Id)
}).ToList();

Related

LINQ Method - Optimization

I'm reading a CSV file splitting it into cols, then grouping into a new class.
It looks clunky just wondering is there is a more simple method for instance like not selecting them into the class first:
EDIT: so to clarify I'm trying to get the TimesheetHours grouped by all the other columns.
var rowList = csvFile.Rows.Select(row => row.Split(','))
.Select(cols => new UtilisationRow {
UploadId = savedUpload.Id,
FullName = cols[0],
TimesheetWorkDateMonthYear = Convert.ToDateTime(cols[1]),
TimesheetTaskJobnumber = cols[2],
TimesheetWorktype = cols[3],
TimesheetHours = Convert.ToDouble(cols[4]),
TimesheetOverhead = cols[5]
})
.GroupBy(d => new {
d.FullName,
d.TimesheetWorkDateMonthYear,
d.TimesheetTaskJobnumber,
d.TimesheetWorktype,
d.TimesheetOverhead
})
.Select(g => new UtilisationRow {
FullName = g.First().FullName,
TimesheetWorkDateMonthYear = g.First().TimesheetWorkDateMonthYear,
TimesheetTaskJobnumber = g.First().TimesheetTaskJobnumber,
TimesheetWorktype = g.First().TimesheetWorktype,
TimesheetHours = g.Sum(s => s.TimesheetHours),
TimesheetOverhead = g.First().TimesheetOverhead
})
.ToList();
Many thanks,
Lee.
The two problems in your code are that you call First() repeatedly on a group, while you should retrieve that same data from group's key, and that you are using UtilisationRow in the first Select, which should use an anonymous type instead:
var rowList = csvFile.Rows.Select(row => row.Split(','))
.Select(cols => new {
UploadId = savedUpload.Id,
FullName = cols[0],
TimesheetWorkDateMonthYear = Convert.ToDateTime(cols[1]),
TimesheetTaskJobnumber = cols[2],
TimesheetWorktype = cols[3],
TimesheetHours = Convert.ToDouble(cols[4]),
TimesheetOverhead = cols[5]
})
.GroupBy(d => new {
d.FullName,
d.TimesheetWorkDateMonthYear,
d.TimesheetTaskJobnumber,
d.TimesheetWorktype,
d.TimesheetOverhead
})
.Select(g => new UtilisationRow {
FullName = g.Key.FullName,
TimesheetWorkDateMonthYear = g.Key.TimesheetWorkDateMonthYear,
TimesheetTaskJobnumber = g.Key.TimesheetTaskJobnumber,
TimesheetWorktype = g.Key.TimesheetWorktype,
TimesheetHours = g.Sum(s => s.TimesheetHours),
TimesheetOverhead = g.Key.TimesheetOverhead
})
.ToList();
Now the "pipeline" of your method looks pretty clean:
The first Select does the initial parsing into a temporary record
GroupBy bundles matching records into a group
The final Select produces records of the required type.

Nesting a List<string> in a custom class List via a LINQ SQL Query

This is my code that retrieves information from the database about a list of Printer Drivers. The table has a list of Printer Drivers, as well as what servers they were found on.
public List<PrinterDrivers> GetPrinterDriversFromCache()
{
using (dbPrintSimpleDataContext db = new dbPrintSimpleDataContext())
{
var q = from p in db.GetTable<tblPrinterDriverCache>()
where p.CacheGUID == mostRecentCacheID()
group p by p.PrinterDriver into g
select new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Where(x => x.PrinterDriver == g.Key).Select(x => x.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
};
return q.ToList();
}
}
What I am trying to return is a List that contains a property that has a List in it that contains what servers that printer driver exists on. I think that I'm up against the limit of my current LINQ SQL knowledge :(
The resultant List should contain:
DriverName = Printer driver name, in this case the group key (string)
InstalledOn = List (containing the list of servers that this printer driver was found on)
Usable = A simple bool check if the servers that it was found on is the same amount as the servers we have in the preferences file.
Thanks for the help :)
Try this:
LINQ Lambda, Group by with list
The problem is that Linq does not know about ToList. Only part of the entire query is executed on the server as there is an extra ToList call before the final ToList call (Untested code below)
public List<PrinterDrivers> GetPrinterDriversFromCache()
{
using (dbPrintSimpleDataContext db = new dbPrintSimpleDataContext())
{
var q = (from p in db.GetTable<tblPrinterDriverCache>()
where p.CacheGUID == mostRecentCacheID()
group p by p.PrinterDriver.DriverName into g
select g
).ToList().Select(g => new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Where(x => x.PrinterDriver == g.Key).Select(x => x.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
});
return q.ToList();
}
}
Translating the same pattern from the answer I linked, yours would be:
var q = db.GetTable<tblPrinterDriverCache>()
.Where(p => p.CacheGUID == mostRecentCacheID())
.Select(o => new { DriverName = o.DriverName, PrintServer = o.PrintServer })
.GroupBy(g => g.DriverName)
.ToList()
.Select(g => new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Select(p => p.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
}
)
.ToList();

Converting the Linq to Entity from query syntax to method syntax?

I have this LINQ to Entity:
var de = from dm in _context.DamageEvents
where dm.StatusID == statusId
group dm by new { dm.ClientId, dm.Client.ClientName, dm.SiteId, dm.Site.Name, dm.SiteObjectId } into g
select new
{
g.Key.ClientId,
g.Key.ClientName,
g.Key.SiteId,
g.Key.Name,
g.Key.SiteObjectId,
icon = g.Select(i=>i.SiteObject.ObjectModel.ObjectType.Icon).FirstOrDefault()
};
How can I convert the linq above to from query syntax to method syntax.
Any idea how can I implement it?
something like this?
var de = _context.DamageEvents.Where(dm => dm.StatusID == statusId)
.GroupBy(dm => new {dm.ClientId, dm.Client.ClientName, dm.SiteId, dm.Site.Name, dm.SiteObjectId})
.Select(g => new
{
g.Key.ClientId,
g.Key.ClientName,
g.Key.SiteId,
g.Key.Name,
g.Key.SiteObjectId,
icon = g.Select(i => i.SiteObject.ObjectModel.ObjectType.Icon).FirstOrDefault()
});

Combine two list data into single list C#

So i have following two list using linq.
List<One> one= A.Common
.Join(B.Common,
a => a.ID,
b=> b.ID,
(a, b) => new One
{
ID = b.PID,
Name = b.PCName,
LastName = a.LName
}).ToList();
List<One> two = (from c in D.Options
select new One
{
MainName = c.mName
}).ToList();
List<One> sn = one.Concat(two).ToList();
I am concating both list. But when i debug i am getting MainName as null in sn list.
How do i get data from both list in single list??
This is how you do it:
var sn = one.Zip(two, (x, y) => new One{
ID = x.ID,
Name = x.Name,
LastName = x.LastName,
MainName = y.MainName
});
You want the MainName property assigned for all the list values in List ONE ?
As from above code concatenation will join two list and the MainName will be not set for list one elements.
one.Concat(two)
Above line will just concat the both lists to one list elements.
You can use the LINQ Concat and ToList methods:
var mergedList = YourFirstList.Concat(YourSecondList)
.Concat(YourThirdList)
.ToList();
Edit:
one.Concat(two).Select(g => g.Aggregate((p1,p2) => new One
{
ID = p1.ID,
Name = p1.PCName,
LastName = p1.LName,
MainName = p2.mName
}));
more efficient ways to do this - the above will basically loop through all the entries, creating a dynamically sized buffer.
var mergedList = new List<One>(YourFirstList.Count +
YourSecondList.Count +
YourThirdList.Count);
mergedList.AddRange(YourFirstList);
mergedList.AddRange(YourSecondList);
mergedList.AddRange(YourThirdList);
AddRange is special-cased for ICollection<T> for efficiency.
You can use the Zip method.
one.Zip(two,(o1, o2) => new One()
{
ID = o1.ID,
Name = o1.PCName,
LastName = o1.LName,
MainName = o2.mName
});

how to use entity framework to group by date not date with time

my code:
//get data
var myData = from log in db.OperationLogs
group log by log.CreateTime.Date into g
orderby g.Key
select new { CreateTime = g.Key, Count = g.Count() };
this code will throw an exception like entity framework does not support get Date operation.
because log.createtime both have date and time, i want to group by date, how should i do
Use EntityFunctions.TruncateTime Method (Nullable<DateTime>). It will be transalated into TRUNCATETIME() TSQL function in generated SQL query, which does what you need:
Returns the expression, with the time values truncated.
So your code should be as follows:
//get data
var myData = from log in db.OperationLogs
group log by EntityFunctions.TruncateTime(log.CreateTime) into g
orderby g.Key
select new { CreateTime = g.Key, Count = g.Count() };
Here is an easier way to do it for later Entity Framework versions.
var query = Data
.GroupBy(o => new { EventDate = o.EventDate.Date })
.Select(s => new SalesData()
{
EventDate = s.Key.EventDate,
Amount = s.Sum(o => o.Amount),
Qty = s.Sum(o => o.Qty),
RefundAmount = s.Sum(o => o.RefundAmount),
RefundQty = s.Sum(o => o.RefundQty),
})
.OrderBy(o => o.EventDate)
.ToList();
return query;
var result = from s in entitiesModel.TvysFuelTankDatas
orderby s.Datetime ascending
group s by new { y = s.Datetime.Year, m = s.Datetime.Month + "/", d = s.Datetime.Day + "/" } into g
select new WellDrillData { Date = Convert.ToDateTime(g.Key.d.ToString() + g.Key.m.ToString() + g.Key.y.ToString()), Depth = (double)g.Sum(x => x.Difference) };
List<WellDrillData> dailyFuelConsumptions = result.ToList();

Categories

Resources