Perform group and join using LINQ - c#

Does anyone have any suggestions about how I might do this using just LINQ?
var statements = db.vwOutstandingStatements.ToList();
var amounts = (from s in db.vwOutstandingStatements
group s by s.Id into v
select new
{
Id = v.Key,
amount = v.Sum(a => a.Amount)
}).ToList();
List<vmVendorStatements> statementsToProcess = new List<vmVendorStatements>();
foreach (var amount in amounts)
{
var statement = statements.Find(s => s.Id == amount.Id);
statementsToProcess.Add(new vmVendorStatements()
{
Id = statement.Id,
PropertyAddress = statement.PropertyNumber + " " + statement.PropertyStreet + " " + statement.PropertyTown,
StatementDate = statement.StatementDate.ToLongDateString(),
Amount = amount.amount.ToString()
});
}
Statements is from a sql view via EF5. I run the LINQ to get the data grouped by the sum of the amounts in the returned view and then join it back to show some of the detail from the returned view along with the sums amounts. StatementsToView is my view model to get the data into an MVC view.
I'm sure it could be done in SQL, and I might do that in any case, but there also seems as though there must be a neater solution to the above too.
Thanks,
Jason.

You can just grab out the first item in the group rather than re-querying just to find the first item:
var statementsToProcess =
(from s in db.vwOutstandingStatements
group s by s.Id into v
let first = v.First()
select new vmVendorStatements()
{
Id = v.Key,
amount = v.Sum(a => a.Amount),
PropertyAddress = first.PropertyNumber + " " + first.PropertyStreet + " " + first.PropertyTown,
StatementDate = first.StatementDate.ToLongDateString(),
}).ToList();

Related

LinQ query - Cannot use Date as its non-primitive EDM type. Also cannot convert DateTime to String

I have below linQ Query for which I am getting the error:
LINQ to Entities does not recognize the method 'System.String ToString(System.DateTime)' method, and this method cannot be translated into a store expression.
var query = from i in Expenditure
join d in Department on i.DepartId equals d.dID
where i.Status == "Audit"
group i by new { i.InvoiceId, d.Title, i.InvoiceDate } into g
select new
{
id = g.Key.InvoiceId,
txt = g.Key.Title + " // " + g.Key.InvoiceId + " // " + Convert.ToString(g.Key.InvoiceDate)
};
I want txt output as follow:
Health // Inv001 // 12-03-2018
Education // Inv002 // 23-03-3018
Problem is with InvoiceDate (Datetime) field for which I initially wrote (without the Convert)as: g.Key.InvoiceDate
but I got error as: Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
Can anyone suggest an easy solution? Thanks.
ToString isn't an SQL function, therefore it can't be done within the SQL query.
Try this:
var query = (from i in Expenditure
join d in Department on i.DepartId equals d.dID
where i.Status == "Audit"
group i by new { i.InvoiceId, d.Title, i.InvoiceDate } into g
select new
{
g.Key.InvoiceId,
g.Key.Title,
g.Key.InvoiceDate,
}).ToList()
.select new
{
id = InvoiceId,
txt = Title + " // " + InvoiceId + " // " + Convert.ToString(InvoiceDate)
};
Everything after the .ToList() will be done in memory, where you can use any .NET method.
Instead of assembling txt in database and force Linq To Entities to transform your logic into SQL, first retrieve the data and then apply transformations
Try following
var query =
(from i in Expenditure
join d in Department on i.DepartId equals d.dID
where i.Status == "Audit"
group i by new { i.InvoiceId, d.Title, i.InvoiceDate } into g
select new
{
id = g.Key.InvoiceId,
title = g.Key.Title,
invoiceId = g.Key.InvoiceId,
invoiceDate = g.Key.InvoiceDate
})
.ToList()
.Select(it => new
{
id = it.id,
txt = it.title + " // " + it.invoiceId + " // " + it.invoiceDate
});

pagination in linq to sql into list

I have a linq to sql query that performs several joins in several tables to return a me list of data. The query works fine and returns me all the correct data. However, I have decided to add pagination in my data table, and have modified the below code a bit, so that not all data is fetched from the db at once.
The issue that I am having is how to add the data returned by the query inside the var data into the allTrucks list correctly ?
The exception that I am having is the following :
Expression expected in Dynamic Linq
List<TruckList> allTrucks = new List<TruckList>();
try
{
using (PaginationContext _db = new PaginationContext())
{
var data = (from lad in _db.Jobs
join users in _db.Users on lad.Id equals users.Id into
ul
from users in ul.DefaultIfEmpty()
join ladAddressLoading in _db.Addresses.Where(a => a.TAD_N_ID == 1) on lad.Id equals ladAddressLoading.Id
join ladAddressDelivery in _db.Addresses.Where(a => a.TAD_N_ID == 2) on lad.Id equals ladAddressDelivery.Id
join countryLoading in _db.Countries on ladAddressLoading.Id equals countryLoading.Id
join countryDelivery in _db.Countries on ladAddressDelivery.Id equals countryDelivery.Id
join volume in _db.Measurements on lad.Id equals volume.Id
select new
{
Coordinator = users == null ? "No User" : users.FirstName + " " + users.LastName,
Volume = ladAddressLoading.VolumeTotal,
DeliveryCountry = countryDelivery.ISO2,
DeliveryDate = ladAddressDelivery.From,
LoadingCountry = countryLoading.ISO2,
LoadingDate = ladAddressLoading.From,
}).AsEnumerable()
.Select(x => new TruckList()
{
Coordinator = x.Coordinator,
Volume = x.Volume,
Delivery = x.DeliveryCountry + " - " + x.DeliveryDate.Value.ToString("dd/MM/yyyy"),
Loading = x.LoadingCountry + " - " + x.LoadingDate.Value.ToString("dd/MM/yyyy"),
});
allTrucks.AddRange(data);
totalRows = allTrucks.Count;
if (!string.IsNullOrEmpty(searchVal))
{
// filter
allTrucks = allTrucks.Where(x => x.FileID.ToLower().Contains(searchVal.ToLower())).ToList<TruckList>();
}
totalRowsAfterFilter = allTrucks.Count;
// sorting
allTrucks = allTrucks.OrderBy(sortColumnName + " " + sortDirection).ToList<TruckList>();
// paging
allTrucks = allTrucks.Skip(start).Take(length).ToList<TruckList>();
How can I assign the linq-to-sql directly to the List<TruckList> directly ?
Could someone advise a better approach of doing this ? Please help.
Break into two queries. The variable data is not a TruckList. See code below
var data = from lad in _db.Jobs
join users in _db.Users on lad.Id equals users.Id into ul
from users in ul.DefaultIfEmpty()
join ladAddressLoading in _db.Addresses.Where(a => a.TAD_N_ID == 1) on lad.Id equals ladAddressLoading.Id
join ladAddressDelivery in _db.Addresses.Where(a => a.TAD_N_ID == 2) on lad.Id equals ladAddressDelivery.Id
join countryLoading in _db.Countries on ladAddressLoading.Id equals countryLoading.Id
join countryDelivery in _db.Countries on ladAddressDelivery.Id equals countryDelivery.Id
join volume in _db.Measurements on lad.Id equals volume.Id
select new
{
Coordinator = users == null ? "No User" : users.FirstName + " " + users.LastName,
Volume = ladAddressLoading.VolumeTotal,
DeliveryCountry = countryDelivery.ISO2,
DeliveryDate = ladAddressDelivery.From,
LoadingCountry = countryLoading.ISO2,
LoadingDate = ladAddressLoading.From,
};
List<TruckList> trucks = data.Select(x => new TruckList()
{
Coordinator = x.Coordinator,
Volume = x.Volume,
Delivery = x.DeliveryCountry + " - " + x.DeliveryDate.Value.ToString("dd/MM/yyyy"),
Loading = x.LoadingCountry + " - " + x.LoadingDate.Value.ToString("dd/MM/yyyy"),
});
allTrucks.AddRange(trucks);
It's just like the exception says -- .OrderBy expects an Expression and you are passing a string.
allTrucks = allTrucks.OrderBy(sortColumnName + " " + sortDirection).ToList<TruckList>();
What it should be is something like
allTrucks = allTrucks.OrderBy(truck => truck.SomeProp).ToList();

EnumerateFiles in Linq

I have LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable1[System.String] EnumerateFiles(System.String)' method, and this method cannot be translated into a store expression.` error. I need to get links to images from folders, I get ID from the database.
// Collect flat items and add in List<>
var nearestItems = from item in _db.Flats
select new listItem()
{
Price = item.Price,
Address = item.Address,
Bathroom = item.Bathroom,
BesprovodnoiInternet = item.BesprovodnoiInternet,
City = item.City,
FloorAll = item.FloorAll,
FloorCurrent = item.FloorCurrent,
Funiture = item.Funiture,
Kondicioner = item.Kondicioner,
PartyFree = item.PartyFree,
RoomQuantity = item.RoomQuantity,
TipArendy = item.TipArendy,
TV = item.TV,
ImagesString = Directory.EnumerateFiles(Server.MapPath("~/Content/Prop/" + item.FlatID + "/"))
.Select(fn => "~/Content/Prop/" + item.FlatID + "/" + Path.GetFileName(fn)).ToList()
};
Are there fix for this or alternate code?
Your LINQ query should be translated to SQL query to run on SQL Server. It is obvious that the engine cant translate Directory.EnumerateFiles to SQL query.
You can add new property FlatId to your listItem and try this:
// Collect flat items and add in List<>
var nearestItems = (from item in _db.Flats
select new listItem()
{
Price = item.Price,
Address = item.Address,
Bathroom = item.Bathroom,
BesprovodnoiInternet = item.BesprovodnoiInternet,
City = item.City,
FloorAll = item.FloorAll,
FloorCurrent = item.FloorCurrent,
Funiture = item.Funiture,
Kondicioner = item.Kondicioner,
PartyFree = item.PartyFree,
RoomQuantity = item.RoomQuantity,
TipArendy = item.TipArendy,
TV = item.TV,
FlatId = item.FlatID,
}).ToList();
foreach(var item in nearestItems)
{
item.ImagesString = Directory.EnumerateFiles(Server.MapPath("~/Content/Prop/" + item.FlatId + "/"))
.Select(fn => "~/Content/Prop/" + item.FlatId + "/" + Path.GetFileName(fn)).ToList();
}
EntityFramework will build query based on your LINQ which will execute in the database. So there are some constarints while using LINQ to Entity. How must EF convert this code to the database query? Directory.EnumerateFiles
There is no way.
So, you must select only required properties and then change them as you wish in .net:
var nearestItems = (from item in _db.Flats
select new listItem()
{
Price = item.Price,
Address = item.Address,
Bathroom = item.Bathroom,
BesprovodnoiInternet = item.BesprovodnoiInternet,
City = item.City,
FloorAll = item.FloorAll,
FloorCurrent = item.FloorCurrent,
Funiture = item.Funiture,
Kondicioner = item.Kondicioner,
PartyFree = item.PartyFree,
RoomQuantity = item.RoomQuantity,
TipArendy = item.TipArendy,
TV = item.TV,
FlatId = item.FlatID,
}).ToList();
And in you class change the get accessor of your property ImagesString:
public List<string> ImagesString
{
get
{
return Directory.EnumerateFiles(Server.MapPath("~/Content/Prop/" + FlatID + "/"))
.Select(fn => "~/Content/Prop/" + FlatID + "/" + Path.GetFileName(fn))
.ToList();
}
}

linq orderby query inside of an if statement

I'm trying to do some code which basically says do search, if orderByLastName is true order by lastname and if false order by first name.
if(orderByLastName)
{
var query = from p in db.People
orderby p.LastName
select new {Name = p.FirstName + " " + p.LastName}
}
else
{
var query = from p in db.People
orderby p.FirstName
select new {Name = p.FirstName + " " + p.LastName}
}
The above code is my attempt at accomplishing this. It doesn't work because query does not exist outside of the if context which is clearly bad! And also I'm pretty sure the code violates the dry principle. Can someone see a nicer way of doing this? Thanks.
You can run as many queries on your IQueryable collection as you want, all of them will be executed right in place where you will make first cast of conversion to IEnumerable.
var query = db.People;
if(orderByLastName)
{
query = query.OrderBy(t=>t.LastName)
}
else
{
query = query.OrderBy(t=>t.FirstName)
}
var result = query.Select(t=> new {Name = t.FirstName + " " + t.LastName});
This is another solution :
Func<PeopleType, string> orderby;
if(orderByLastName)
orderby = t => t.LastName;
else
orderby = t => t.FirstName;
var result = db.People.OrderBy(orderby).Select(t => new { Name = t.FirstName + " " + t.LastName });

return one string with multiple column value linq

I have a query.
Is it possible to return One string with multiple column value in linq ??
Like this
string ProductName = ((from P in DataModel.DIS_PRODUCT
join M in DataModel.SET_PACK_UNIT on P.PUNIT_ID equals M.ID
where P.PRODUCT_ID == ProdictId
select P.PRODUCT_NAME +" " + P.WEIGHT + " "+ M.UNIT_SYMBLE)
.Take(1)
).ToString();
You're using Take(1) which means you're still getting an IEnumerable<string> or an IQueryable<string>. Just use First() (or possibly FirstOrDefault()) instead of Take(1) and you can drop the ToString() call as well.
string productName = (from P in DataModel.DIS_PRODUCT
join M in DataModel.SET_PACK_UNIT
on P.PUNIT_ID equals M.ID
where P.PRODUCT_ID == ProdictId
select P.PRODUCT_NAME + " " + P.WEIGHT + " "+ M.UNIT_SYMBLE)
.FirstOrDefault();
That will only work if your LINQ provider supports the string concatenation operation. An alternative is to fetch just the columns you want, and then concatenate at the caller:
var query = from P in DataModel.DIS_PRODUCT
join M in DataModel.SET_PACK_UNIT
on P.PUNIT_ID equals M.ID
where P.PRODUCT_ID == ProdictId
select new { P.PRODUCT_NAME, P.WEIGHT, M.UNIT_SYMBLE };
var result = query.FirstOrDefault();
if (result != null)
{
string productName = result.PRODUCT_NAME + " " +
result.WEIGHT + " " +
result.UNIT_SYMBLE;
// Use the name
}
else
{
// No results
}
Another more clear way is the following
var ProductsQueryItem = (from p in Products
select new
{
Name = e.ProductName+ " " + e.weight +e.UNIT_SYMBLE
})
.FirstOrDefault();
Now you can use it directly with ProductsQueryItem .Name

Categories