Linq group outer joins projected on a viewmodel - c#

I have been scouting the internet for the past 2 days to find a solution to grouping the below linq query on BookId to no avail. The query works but I want to reoganise it so that It can group on BookId or BookTitle.
models
Book(BookId, Title, Author, ISBN, Location, BookTypeId, StockLogValue)
Booktype(BookTypeId, BookTypeName)
Stock(StockId, bookId, quantity, date_added)
Transact (transactionId, TransactionTypeId, BookId, Quantity, TransactDate)
TransactionType( TransactionTypeId, TransactionTypeName)
Controller
public ActionResult Report(int? year, int? month, int? BkId)
{
var query = ReportYrMn( year, month, BkId);
return View(query);
}
public IEnumerable ReportYrMn(int? year, int? month, int? BkId)
{
var query =
(from bk in db.Books
join tr in db.Transacts.Where(a => a.TransactDate.Month == month && a.TransactDate.Year == year && a.TransactType.TransactTypeName == "sale") on bk.BookId equals tr.BookId into trs
from x in trs.DefaultIfEmpty()
join tr2 in db.Transacts.Where(a => a.TransactDate.Month == month && a.TransactDate.Year == year && a.TransactType.TransactTypeName == "gift") on bk.BookId equals tr2.BookId into trs2
from x2 in trs2.DefaultIfEmpty()
select new ReportViewModel { BookTitle = bk.BookTitle ,BookId = bk.BookId, StockLogValue=bksty.StockLogValue, SaleTotal = trs.Sum(c => c.TransactQty), GiftTotal = trs2.Sum(c => c.TransactQty), SalesCount = trs.Count(), GiftCount = trs2.Count() });
return query.AsEnumerable();
}
Thanks for any help

The immediate solution to your problem is to remove the from a in b.DefaultIfEmpty() lines. A join - into is the same as GroupJoin, which creates collections related to the left item, i.e. collections Transacs belonging to a book. That's exactly what you want here.
A subsequent from is equivalent to a SelectMany, which flattens these collections again, leaving you with a flat list of book-transact rows.
So this will do what you want:
var query =
(from bk in db.Books
join tr in db.Transacts
.Where(a => a.TransactDate.Month == month && a.TransactDate.Year == year && a.TransactType.TransactTypeName == "sale")
on bk.BookId equals tr.BookId into trs
join tr2 in db.Transacts
.Where(a => a.TransactDate.Month == month && a.TransactDate.Year == year && a.TransactType.TransactTypeName == "gift")
on bk.BookId equals tr2.BookId into trs2
select new ReportViewModel
{
BookTitle = bk.BookTitle,
BookId = bk.BookId,
StockLogValue=bksty.StockLogValue,
SaleTotal = trs.Sum(c => c.TransactQty),
GiftTotal = trs2.Sum(c => c.TransactQty),
SalesCount = trs.Count(),
GiftCount = trs2.Count()
});
I asked about navigation properties, because nearly always they make queries easier to write, since you don't need the cluncky joins. In your case the difference isn't that big though. If Book would have a navigation property Transacts the query could look like:
var query =
(from bk in db.Books
let sales = bk.Transacts
.Where(a => a.TransactDate.Month == month && a.TransactDate.Year == year && a.TransactType.TransactTypeName == "sale")
let gifts = bk.Transacts
.Where(a => a.TransactDate.Month == month && a.TransactDate.Year == year && a.TransactType.TransactTypeName == "gift")
select new ReportViewModel
{
BookTitle = bk.BookTitle,
BookId = bk.BookId,
StockLogValue=bksty.StockLogValue,
SaleTotal = sales.Sum(c => c.TransactQty),
GiftTotal = gifts.Sum(c => c.TransactQty),
SalesCount = sales.Count(),
GiftCount = gifts.Count()
});

Related

Linq - Join resultset of 1 query with another table

I have a Compilations table which have the details of employees and their compliances, I am getting the Employees whose ComplaianceStateId is either 6 or 9.
I have to get the employee FullName of these employees from the master tblEmployee table.
Table Name is tblEmployee and Column Name is FullName
EmployeeID is the Common key between these 2 tables tblEmployee and tblEmployeeCompliation
List<Int> employeeIds = new List<int>();
List<string> employeeNames = new List<string>();
employeeIds = EMPDB.tblEmployeeCompliations.Where( e=> e.IsActive == true && (e.ComplianceStateId == 6 || e.ComplianceStateId == 9)).Select( e => e.EmployeeID).Distinct().ToList();
employeeNames = //**//
I have to get the employee FullName of these employees from the master
tblEmployee table
From your requirement, you should select FullName instead of EmployeeID
var result = EMPDB.tblEmployeeCompliations
.Where(e => e.IsActive && (e.ComplianceStateId == 6 || e.ComplianceStateId == 9))
.Select(e => e.FullName).Distinct().ToList();
You need to join tblEmployeeCompliation and tblEmployee based on EmployeeID.
Example:
var employeeNames = EMPDB.tblEmployeeCompliations.Join( EMPDB.tblEmployee,
comp => comp.EmployeeID,
cus => cus.EmployeeID,
(comp, cus) => new { comp, cus })
.Where(e => e.comp.IsActive && (e.comp.ComplianceStateId == 6 || e.comp.ComplianceStateId == 9))
.GroupBy(g=>g.cus.FullName)
.Select(x=>x.Key);
OR
var employeeNames = from comp in EMPDB.tblEmployeeCompliations
join cus in EMPDB.tblEmployee on comp.EmployeeID equals cus.EmployeeID
where comp.IsActive=true && (comp.ComplianceStateId == 6 || comp.ComplianceStateId == 9)
group new { cus, comp } by new { cus.FullName } into g
select g.Key.FullName;
Please read this article: LINQ: Distinct() does not work as expected

Convert Sql to LINQ complex C# function to readable sql server string

I read a lot about getting a readable sql server query string from a c# linq to sql function, but could not figure out how to actually do so, maybe because the project i am working on is really complicated.
I'm pasting a pice of a code that i want to convert to sql (select * from bonds join etc.)
public List<EntBondPortfolio> GetBondsForPortfolio(List<int> groups, List<int> ratings,
List<int> collaterals, List<int> companies, List<int> countries, List<int> fields,
List<int> currencies, DateTime? fromExpirationDate, DateTime? untilExpirationDate, bool? perpetual, int? sortOrder = 1)
{
// Get Bonds
return (from bond in DALLinqClasses.Instance.BONDs
join company in DALLinqClasses.Instance.COMPANies on bond.Company_ID equals company.CompanyID
into companyJoin
from companyJ in companyJoin.DefaultIfEmpty()
join sellBuy in DALLinqClasses.Instance.SELL_BUY_OPERATIONs on bond.BondID equals sellBuy.Investment_ID
join account in DALLinqClasses.Instance.BANK_ACCOUNTs on sellBuy.BankAccount_ID equals account.BankAccountID
join groupsTable in DALLinqClasses.Instance.GROUPs on account.GroupAccount_ID equals groupsTable.GroupID
join customer in DALLinqClasses.Instance.CUSTOMERs on groupsTable.Customer_ID equals customer.CustomerID
join rating in DALLinqClasses.Instance.RATINGs on bond.Rating_ID equals rating.RatingID
into ratingJoin
from ratingJ in ratingJoin.DefaultIfEmpty()
join currency in DALLinqClasses.Instance.CURRENCies on bond.Currency_ID equals currency.CurrencyID
join country in DALLinqClasses.Instance.COUNTRies on bond.Country_ID equals country.CountryID
into countryJoin
from countryJ in countryJoin.DefaultIfEmpty()
join field in DALLinqClasses.Instance.FIELDs on bond.Field_ID equals field.FieldID
into fieldJoin
from fieldJ in fieldJoin.DefaultIfEmpty()
join collateral in DALLinqClasses.Instance.RISC_LEVELs on bond.RiscLevel_ID equals collateral.RiscLevelID
into collateralJoin
from collateralJ in collateralJoin.DefaultIfEmpty()
join orderi in // For order by
DALLinqClasses.Instance.PORTFOLIO_ORDERs.Where(x => x.InvestmentType_ID == (int)INVESTMENT_TYPES.BONDS)
on new EntTwoInts { BankAccountID = bond.BondID, GroupID = groupsTable.GroupID }
equals
new EntTwoInts { BankAccountID = orderi.Investment_ID, GroupID = orderi.Group_ID }
into orderJoin
from orderJ in orderJoin.DefaultIfEmpty()
where sellBuy.InvestmentType_ID == (int)INVESTMENT_TYPES.BONDS
&& groups.Contains(groupsTable.GroupID)
&& (collaterals.Contains(bond.RiscLevel_ID ?? -1) || collaterals.Contains(-1))
&& (ratings.Contains(bond.Rating_ID ?? -1) || ratings.Contains(-1))
&& (companies.Contains(bond.Company_ID ?? -1) || companies.Contains(-1))
&& (countries.Contains(bond.Country_ID ?? -1) || countries.Contains(-1))
&& (fields.Contains(bond.Field_ID ?? -1) || fields.Contains(-1))
&& (currencies.Contains(bond.Currency_ID) || currencies.Contains(-1))
&& (bond.ExpirationDate >= fromExpirationDate || fromExpirationDate == null)
&& (bond.ExpirationDate <= untilExpirationDate || untilExpirationDate == null)
&& (perpetual == bond.Perpetual || perpetual == null)
group sellBuy by new { bond, companyJ, groupsTable, account, orderJ, customer, ratingJ, collateralJ, countryJ, fieldJ } into g
orderby g.Key.orderJ != null ? false : true, g.Key.orderJ.SortOrder, g.Key.groupsTable.GroupName, g.Key.companyJ.CompanyName
select new EntBondPortfolio
{
Rating = g.Key.ratingJ.RatingName,
RiscLevel = g.Key.collateralJ.RiscLevelName,
Group = g.Key.groupsTable.GroupName,
Client = g.Key.customer.FirstName + ' ' + g.Key.customer.LastName,
GroupId = g.Key.groupsTable.GroupID,
Company = g.Key.companyJ.CompanyName,
CurrencyId = g.Key.bond.Currency_ID,
BondId = g.Key.bond.BondID,
CallText = g.Key.bond.CallText,
Field = g.Key.fieldJ.FieldName,
Country = g.Key.countryJ.CountryName,
CouponPercent = g.Key.bond.CouponPercent,
// Get total sum of coupons in checking account of current bond
CouponsPaid = DALLinqClasses.Instance.CHECKING_ACCOUNTs.
Where(x => x.ActionType_ID == (int)ACTION_TYPES.COUPON
&& g.Select(y => y.SellBuyOperationID).
Contains(x.SellBuyOperation_ID.GetValueOrDefault()))
.Sum(s => s.ActualSum),
ExpirationDate = g.Key.bond.ExpirationDate,
FaceValue = (g.Where(x => x.IsSell == false).Sum(x => x.FaceValue) - (g.Where(x => x.IsSell == true).Sum(x => x.FaceValue) ?? 0)),
Isin = g.Key.bond.Isin,
LastBuyDate = g.Where(x => x.IsSell == false).Max(x => x.SellBuyOperationDate),
CurrentPrice = DALInvestmentTypes.Instance.GetCurrentPriceOfBond(g.Key.bond.BondID, BONDS_HISTORY_TYPES.MarketValue),
CurrentYield = DALInvestmentTypes.Instance.GetCurrentPriceOfBond(g.Key.bond.BondID, BONDS_HISTORY_TYPES.Yield),
CurrentAcrInterest = DALInvestmentTypes.Instance.GetCurrentPriceOfBond(g.Key.bond.BondID, BONDS_HISTORY_TYPES.AcrInterest),
PurchasePrice = g.Where(x => x.IsSell == false).Sum(x => x.FaceValue) == 0 ? 0 :
g.Where(x => x.IsSell == false).Sum(x => x.FaceValue * x.Price)
/ g.Where(x => x.IsSell == false).Sum(x => x.FaceValue),
Instruction = DALInstructions.Instance.IsInvestmentHaveInstruction(INVESTMENT_TYPES.BONDS, g.Key.bond.BondID, g.Key.account.BankAccountID),
}).ToList();
}

How to Select new model list after GroupBy mothod in Linq?

I want select grouped rows to a new model list.this is my code:
List<Model_Bulk> q = (from a in db.Advertises
join c in db.Companies on a.AdvertiseCompanyID equals c.CompanyID
where a.AdvertiseActive == true
&& a.AdvertiseExpireDate.HasValue
&& a.AdvertiseExpireDate.Value > DateTime.Now
&& (a.AdvertiseObjectType == 1
|| a.AdvertiseObjectType == 2)
select c)
.GroupBy(a => a.CompanyID).Select(a => new Model_Bulk
{
CompanyEmail = a.CompanyContactInfo.Email,
CompanyID = a.CompanyID,
CompanyName = a.CompanyName,
Mobile = a.CompanyContactInfo.Cell,
UserEmail = a.User1.Email,
categories = a.ComapnyCategories
}).ToList();
After group by, i can not use Select and naturally this syntax error raised:
System.Linq.IGrouping' does not contain a definition for 'CompanyContactInfo' and no extension method 'CompanyContactInfo' accepting a first argument of type
System.Linq.IGrouping' could be found (are you missing a using directive or an assembly reference?)
If i try with SelectMany() method.but the result will repeated and groupby method not work properly:
List<Model_Bulk> q = (from a in db.Advertises
join c in db.Companies on a.AdvertiseCompanyID equals c.CompanyID
where a.AdvertiseActive == true
&& a.AdvertiseExpireDate.HasValue
&& a.AdvertiseExpireDate.Value > DateTime.Now
&& (a.AdvertiseObjectType == 1
|| a.AdvertiseObjectType == 2)
select c)
.GroupBy(a => a.CompanyID).SelectMany(a => a).Select(a => new Model_Bulk
{
CompanyEmail = a.CompanyContactInfo.Email,
CompanyID = a.CompanyID,
CompanyName = a.CompanyName,
Mobile = a.CompanyContactInfo.Cell,
UserEmail = a.User1.Email,
categories = a.ComapnyCategories
}).ToList();
Instead of .SelectMany(a => a) you can use .Select(g => g.First()).That will give you the first item of each group.
(from a in db.Advertises
join c in db.Companies on a.AdvertiseCompanyID equals c.CompanyID
where a.AdvertiseActive == true && a.AdvertiseExpireDate.HasValue && a.AdvertiseExpireDate.Value > DateTime.Now && (a.AdvertiseObjectType == 1 || a.AdvertiseObjectType == 2)
select c)
.GroupBy(a => a.CompanyID)
.Select(g => g.First())
.Select(a => new Model_Bulk
{
CompanyEmail = a.CompanyContactInfo.Email,
CompanyID = a.CompanyID,
CompanyName = a.CompanyName,
Mobile = a.CompanyContactInfo.Cell,
UserEmail = a.User1.Email,
categories = a.ComapnyCategories
}).ToList();
Note that this might not be supported, if that is the case add an AsEnumerable call before .Select(g => g.First())
You should understand that after you do GroupBy() in your LinQ expresstion you work with a group so in your example it will be good to write like this:
List<Model_Bulk> q =
(from a in db.Advertises join c in db.Companies on a.AdvertiseCompanyID equals c.CompanyID
where a.AdvertiseActive == true
&& a.AdvertiseExpireDate.HasValue
&& a.AdvertiseExpireDate.Value > DateTime.Now
&& (a.AdvertiseObjectType == 1 || a.AdvertiseObjectType == 2)
select c)
.GroupBy(a => a.CompanyID)
.Select(a => new Model_Bulk
{
CompanyEmail = a.First().CompanyContactInfo.Email,
CompanyID = a.Key, //Note this line, it's can be happened becouse of GroupBy()
CompanyName = a.First().CompanyName,
Mobile = a.First().CompanyContactInfo.Cell,
UserEmail = a.First().User1.Email,
categories = a.First().ComapnyCategories
}).ToList();
Instead you could try something like this, instead of mixing query expressions and methods... (using FirstOrDefault() in the where / select as necessary)
(from a in db.Advertises
join c in db.Companies on a.AdvertiseCompanyID equals c.CompanyID
group a by new { a.CompanyId } into resultsSet
where resultsSet.AdvertiseActive == true && resultsSet.AdvertiseExpireDate.HasValue && resultsSet.AdvertiseExpireDate.Value > DateTime.Now && (resultsSet.AdvertiseObjectType == 1 || resultsSet.AdvertiseObjectType == 2)
select new Model_Bulk
{
CompanyEmail = resultsSet.CompanyContactInfo.Email,
CompanyID = resultsSet.CompanyID,
CompanyName = resultsSet.CompanyName,
Mobile = resultsSet.CompanyContactInfo.Cell,
UserEmail = resultsSet.User1.Email,
categories = resultsSet.ComapnyCategories
}).ToList();

join with multiple group by element and select extra columns

Can you please help to convert this from sql to linq, i am new to linq and been trying this and couldn't succeed. Let me know if this is even possible or not?
SELECT max(Products.ProductID) as ProductID, Products.SKU, Products.Name, Products.RRP, Products.Price,
max(Products.FrontTall) as FrontTall,
ProductsCategory.SortOrder,
Products.ColorValue,
max(Products.ColorImg) as ColorImg,
Products.IsPrimary,
Products.Visible
FROM Products INNER JOIN ProductsCategory ON Products.ProductID = ProductsCategory.ProductID
WHERE (ProductsCategory.CategoryID = 5 ) AND (Products.Visible = 1) AND (Products.Inactive = 0) AND (Products.Deleted = 0 or Products.Deleted is null)
GROUP BY SKU, Products.Name, RRP, Price, ColorValue, ProductsCategory.SortOrder, IsPrimary, Visible
AND this is what i am trying
var products = (from p in db.Products
join cp in db.ProductsCategories on p.ProductID equals cp.ProductID
where cp.CategoryID == catId && p.Visible == true && p.Inactive == false && (p.Deleted == null || p.Deleted == false)
group p by new {
p.SKU,
p.Name,
p.RRP,
p.Price,
p.ColorValue,
p.IsPrimary,
p.Visible,
cp.SortOrder
} into grouped
select new {
ProductID,
FrontTall,
ColorImg,
SKU = grouped.Key.SKU,
Name = grouped.Key.Name,
RRP = grouped.Key.RRP,
Price = grouped.Key.Price,
ColorValue = grouped.Key.ColorValue,
IsPrimary = grouped.Key.IsPrimary,
Visible = grouped.Key.Visible,
SortOrder = grouped.Key.SortOrder
})
.OrderBy(x => x.SortOrder);
I need to get the ProductID, FrontTall and ColorImg field value without including in group by
Thanks in advance.
Kish
select new {ProductID = grouped.Max(x => x.ProductID)}

Convert from query to method

How to convert following code to method operator:
var myOrders = from c in customers
where c.Field<string>("Region") == "WA"
from o in orders
where c.Field<string>("CustomerID") == o.Field<string>("CustomerID")
&& (DateTime)o["OrderDate"] >= cutoffDate
select new {
CustomerID = c.Field<string>("CustomerID"),
OrderID = o.Field<int>("OrderID")
};
---------or----------
var myOrders = from c in customers
where c.Region == "WA"
from o in orders
where c.CustomerID == o.CustomerID
&& o.OrderDate >= cutoffDate
select new {
CustomerID = c.CustomerID,
OrderID = o.OrderID
};
same code in object form
I'd actually rewrite this as a join - probably via intermediate variables:
var washingtonCustomers = customers.Where(c => c.Field<string>("Region") == "WA");
var recentOrders = orders.Where(o => (DateTime)o["OrderDate"] >= cutoffDate);
var query = washingtonCustomers.Join(recentOrders,
c => c.Field<string>("CustomerID"),
o => o.Field<string>("CustomerID"),
(c, o) => new {
CustomerID = c.Field<string>("CustomerID"),
OrderID = o.Field<int>("OrderID")
});
You can try with this code - based on IEnumerable<KeyValuePair<string, Int32>
public IEnumerable<KeyValuePair<string, Int32>> YourQuery(DateTime date, string code)
{
var result =
from c in customers
where c.Field<string>("Region") == code
from o in orders
where c.Field<string>("CustomerID") == o.Field<string>("CustomerID")
&& (DateTime)o["OrderDate"] >= date
select new
{
CustomerID = c.Field<string>("CustomerID"),
OrderID = o.Field<int>("OrderID")
};
return result;
}
Are you just wanting to use the functional Linq syntax instead of query syntax? That would look like:
var myOrders = customers
.Where(c => c.Region == "WA")
.SelectMany(c =>
orders
.Where(o => (o.CustomerID == c.CustomerID)
&& (o.OrderDate > cutoffDate))
.Select(o => new {
CustomerID = c.CustomerID,
OrderID = o.OrderID
})
);

Categories

Resources