Runtime error while using a linq query - c#

I am able to write this code for a Web Api. In this code i have written tow queries and combined both in a way that comments and follower data should be merged (not combined) with respect to time based on ctime and startedfollowing. If a user has a new comment, the comment should come first and if the follower is first, the follower data should come first.
public IQueryable<Object> GetCommentsandFollowActivityCommnets()
{
var combo1 = from c in db.comments
join p in db.picturedetails on c.targetpictureid equals p.idpictures
join u in db.users on c.iduser equals u.iduser
select new TCommentDTO
{
idcomments=c.idcomments,
comment1 = c.comment1,
targetpictureid = c.targetpictureid,
ctime = c.ctime,
iduofpic=p.iduser,
iduofcommentor=c.iduser,
profilepicofcommentor=u.profilepic,
usernameofcommentor=u.username,
picFilename=p.picFilename,
picTitle=p.picTitle
};
var combo2= from f in db.followers
join u in db.users on f.iduser equals u.iduser
select new TfollowerDTO
{
idfollowers=f.idfollowers,
iduser=f.iduser,
targetiduser=f.targetiduser,
startedfollowing=f.startedfollowing,
unoffollower=u.username,
ppoffollower=u.profilepic,
status=u.status
};
var result1 = from c in combo1
select new UserTimeLineDTO
{ SortKey = c.ctime, Member =c};
var result2 = from c in combo2
select new UserTimeLineDTO{ SortKey = c.startedfollowing, Member = c };
var result = result1.Concat(result2).OrderBy(x =>x.SortKey).Select(x => x.Member);
return result;
}
The code is not giving any compile time error. It runs fine in compiler, but on run time i am getting an exception:
DbUnionAllExpression requires arguments with compatible collection ResultTypes.
How to remove this exception?

As a workaround, I would try to evaluate the last expression in memory:
var result1 = (from c in combo1
select new UserTimeLineDTO
{ SortKey = c.ctime, Member =c}).ToList();
var result2 = (from c in combo2
select new UserTimeLineDTO{ SortKey = c.startedfollowing, Member = c }).ToList();
var result = result1.Concat(result2).OrderBy(x =>x.SortKey).Select(x => x.Member);
The union should now succeed, as well as ordering and the final projection.

Related

LINQ select new with collection

Context
I am trying to get a list from select new:
var portfolioresult =
(from port in _context.Portfolio
join u in _context.Universe on port.CUSIP equals u.ID_CUSIP
join m in _context.MarketDataEvent on u.ID_CUSIP equals m.CUSIP_NUMBER_REALTIME
//select new { m, port.Name }).ToList();
select new ViewResult() { MarketDataEvents = m, PortfolioName = port.Name })
.ToList();
I want to get MarketDataEvents as List<MarketDataEvent>
Corresponding SQL query
SELECT me.*, p.Name FROM MarketDataEvent me
INNER JOIN universe u ON u.ID_CUSIP=me.CUSIP_NUMBER_REALTIME
INNER JOIN portfolio p ON p.CUSIp=me.CUSIP_NUMBER_REALTIME
Problem
I am not able to get a List inside select new. Is it possible to get something like this?
select new ViewResult() { MarketDataEvents = List<MarketDataEvents>, PortfolioName = port.Name })
Expected result
List<MarketDataEvents> "XYZ"
List<MarketDataEvents> "ABC"
Actual result
MarketDataEvent "XYZ
MarketDataEvent "XYZ"
MarketDataEvent "ABC"
Yes, it is possible:
var query =
from port in _context.Portfolio
select new ViewResult
{
MarketDataEvents =
(from u in _context.Universe.Where(u => port.CUSIP == u.ID_CUSIP)
join m in _context.MarketDataEvent on u.ID_CUSIP equals m.CUSIP_NUMBER_REALTIME
select m).ToList(),
PortfolioName = port.Name
};
var portfolioresult = query.ToList();
Essentially your m reference is out of scope. The input available to a select statement is only a single value out of the set available as a result of the select/joins you're looking at, which is why you don't see a list of all available from m, only a single value in each record.
Rather, you need to use a SelectMany since it'll expose an IEnumerable as the input to the function and you can split out the individual XYZ values out of that.

Linq - Linq expression different context error

I have 3 tables
- ERPEntry
- ERPEntryType
- ERPApp
I am trying to get data from these 3 tables using the below query but i got the error :
specified linq expression contains references to queries that are
associated with different contexts
var erpEntryInfo = (from s in ERPDB.ERPEntrys
JOIN t in ERPDB.ERPEntryTypes
on s.EntryTypeID equals t.EntryTypeID
join a in APPDB.ERPApps
on s.AppId equals a.AppId
where s.UserIDAdded == '250176'
select new ERPInfo
{
EntryId = s.EntryID,
EntryType = t.EntryTypeName,
ERPApp = a.ApplicationName,
DateAdded = s.DateAdded
}).OrderByDescending(d => d.DateAdded).Take(10).ToList();
I searched based on the errror and tried to split the above query into 2 as below.
var res = (from s in ERPDB.ERPEntrys
join t in ERPDB.ERPEntryTypes
on s.EntryTypeID equals t.EntryTypeID
where s.UserIDAdded == '250176'
select new {s.EntryTypeID, s.DateAdded, t.EntryTypeName, s.AppID }).OrderByDescending(d => d.DateAdded).Take(10).ToArray();
var y = (from a in APPDB.ERPApps
join b in res on a.AppId equals //??//
select new ERPInfo
{
EntryId = b.EntryID,
EntryType = b.EntryTypeName,
ERPApp = a.ApplicationName,
DateAdded = b.DateAdded
}).ToList();
I am having an issue in the above query to access AppId which i got into the result res..i commented with //??// in the above code
can i get any help on this.

Join 2 table and group 2 field in linq

I have a very simple SQL
SELECT s.shop_code
,SUM(im.amt) sum_amt
,s.cell_no#1 shop_cell
FROM tb_sn_so_wt_mst im
,tb_cm_shop_inf s
WHERE im.shop_code = s.shop_code
GROUP BY s.shop_code, s.cell_no#1)
then i try to code linq
var listResult = from warrantyMaster in listWarrantyMasters2.Records
join shopInfo in listShopInfos
on warrantyMaster.ShopCode equals shopInfo.ShopCode
i don't know group by shop code and cell no and sum atm, any one help me out of this problem
The group by syntax with some examples is explained here group clause (C# Reference) and related links.
Here is the direct translation of your SQL query (of course the field names are just my guess since you didn't provide your classes):
var query = from im in listWarrantyMasters2.Records
join s in listShopInfos
on im.ShopCode equals s.ShopCode
group im by new { s.ShopCode, s.CellNo } into g
select new
{
g.Key.ShopCode,
g.Key.CellNo,
SumAmt = g.Sum(e => e.Amt)
};
You can try this code:
var results = from warrantyMaster in listWarrantyMasters2.Records
from shopInfo in listShopInfos
.Where(mapping => mapping.ShopCode == warrantyMaster.ShopCode )
.select new
{
ShopCode = warrantyMaster.ShopCode,
ATM = listWarrantyMasters2.ATM,
ShellNo = shopInfo.ShellNo
}
.GroupBy(x=> new { x.ShopCode, x.ShellNo })
.Select(x=>
new{
ShopCode = x.Key.ShopCode,
ShellNo = x.Key.ShellNo,
SumATM = x.Sum(item=>item.ATM)
});

Loop linq query n times by updating property name

I have this query
var query = from v in this._venueRepository.Table
join s in this._storeRepository.Table on v.VenueID equals s.VenueID
join w in this._workstationRepository.Table on s.StoreID equals w.StoreID
join t in this._tillSummaryRepository.Table on w.WorkstationID equals t.TillOpID
group new { v.DiscItemName_1, t.DiscItem_1, t.QDiscItem_1 } by new { v.DiscItemName_1 } into g
select new { Discount = g.Key, Amount = g.Sum(p => p.DiscItem_1), Qty = g.Sum(p => p.QDiscItem_1) };
I would like to execute this query (probably asynchronously) but each time I execute this query I would like to update the parameter "_1" -> "_2" -> "_3" etc for example
var query = from v in this._venueRepository.Table
join s in this._storeRepository.Table on v.VenueID equals s.VenueID
join w in this._workstationRepository.Table on s.StoreID equals w.StoreID
join t in this._tillSummaryRepository.Table on w.WorkstationID equals t.TillOpID
group new { v.DiscItemName_2, t.DiscItem_2, t.QDiscItem_2 } by new { v.DiscItemName_2 } into g
select new { Discount = g.Key, Amount = g.Sum(p => p.DiscItem_2), Qty = g.Sum(p => p.QDiscItem_2) };
etc, etc Any ideas how I can do this? Perhaps reflection?
One way of doing this is of course reflection as you mention. A sample based on your example is below:
var t = ._venueRepository.Table.FirstOrDefault().GetType();
for(int iterationCount = 1; iterationCount < MAX_ITERATIONS ; iterationCount++)
{
PropertyInfo itemNameProperty = t.GetProperty(String.Format("DiscItemName_{0}", iterationCount));
PropertyInfo discItermProperty = t.GetProperty(String.Format("DiscItem_{0}", iterationCount));
//Repeat the above for all properties.
var query = from v in this._venueRepository.Table
join s in this._storeRepository.Table on v.VenueID equals s.VenueID
join w in this._workstationRepository.Table on s.StoreID equals w.StoreID
join t in this._tillSummaryRepository.Table on w.WorkstationID equals t.TillOpID
//Repeat the below for other properties
group new { itemNameProperty.GetValue(v), dicItemProperty.GetValue(v) , qDiscProperty.GetValue(v) } by new { itemNameProperty.GetValue(v) } into g
//Similarly do for the select new.
select new { Discount = g.Key, Amount = g.Sum(p => p.DiscItem_1), Qty = g.Sum(p => p.QDiscItem_1) };
//Other code here.
}
If you are using the Entity framework, the above query will not work. The reason this will not work is that the Entity Framework will try to translate your query to SQL, and will fail at the reflection parts. What you have to do is split your query in two. Do the filter and join in one step and retrieving it to a List and then using reflection to create the anonymous types in another query. You will be using more memory however for this operation and you are losing benefits from using Linq to Entity.

Converting SQL to LINQ query when I cannot use "IN"

I'm trying to convert this very simple piece of SQL to LINQ:
select * from Projects p
inner join Documents d
on p.ProjectID = d.ProjectID
left join Revisions r
on r.DocumentID = d.DocumentID
and r.RevisionID IN (SELECT max(r2.RevisionID) FROM Revisions r2 GROUP BY r2.DocumentID)
WHERE p.ProjectID = 21 -- Query string in code
This says, if any revisions exist for a document, return me the highest revision ID. As it's a left join, if not revisions exist, I still want the results returned.
This works as expected, any revisions which exist are shown (and the highest revision ID is returned) and so are all documents without any revisions.
When trying to write this using LINQ, I only get results where revisions exist for a document.
Here is my attempt so far:
var query = from p in db.Projects
join d in db.Documents on new { ProjectID = p.ProjectID } equals new { ProjectID = Convert.ToInt32(d.ProjectID) }
join r in db.Revisions on new { DocumentID = d.DocumentID } equals new { DocumentID = Convert.ToInt32(r.DocumentID) } into r_join
from r in r_join.DefaultIfEmpty()
where
(from r2 in db.Revisions
group r2 by new { r2.DocumentID }
into g
select new { MaxRevisionID = g.Max(x => x.RevisionID) }).Contains(
new { MaxRevisionID = r.RevisionID }) &&
p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
select new { d.DocumentID, d.DocumentNumber, d.DocumentTitle, RevisionNumber = r.RevisionNumber ?? "<No rev>", Status = r.DocumentStatuse == null ? "<Not set>" : r.DocumentStatuse.Status };
I'm not very good at LINQ and have been using the converter "Linqer" to help me out, but when trying I get the following message:
"SQL cannot be converted to LINQ: Only "=" operator in JOIN expression
can be used. "IN" operator cannot be converted."
You'll see I have .DefaultIfEmpty() on the revisions table. If I remove the where ( piece of code which does the grouping, I get the desired results whether or not a revision exists for a document or not. But the where clause should return the highest revision number for a document IF there is a link, if not I still want to return all the other data. Unlike my SQL code, this doesn't happen. It only ever returns me data where there is a link to the revisions table.
I hope that makes a little bit of sense. The group by code is what is messing up my result set. Regardless if there is a link to the revisions table, I still want my results returned. Please help!
Thanks.
=======
The code I am now using thanks to Gert.
var query = from p in db.Projects
from d in p.Documents
where p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
select new
{
p.ProjectID,
d.DocumentNumber,
d.DocumentID,
d.DocumentTitle,
Status = d.Revisions
.OrderByDescending(rn => rn.RevisionID)
.FirstOrDefault().DocumentStatuse.Status,
RevisionNumber = d.Revisions
.OrderByDescending(rn => rn.RevisionID)
.FirstOrDefault().RevisionNumber
};
gvDocumentSelection.DataSource = query;
gvDocumentSelection.DataBind();
Although this works, you'll see I'm selecting two fields from the revisions table by running the same code, but selecting two different fields. I'm guessing there is a better, more efficient way to do this? Ideally I would like to join on the revisions table in case I need to access more fields, but then I'm left with the same grouping problem again.
Status = d.Revisions
.OrderByDescending(rn => rn.RevisionID)
.FirstOrDefault().DocumentStatuse.Status,
RevisionNumber = d.Revisions
.OrderByDescending(rn => rn.RevisionID)
.FirstOrDefault().RevisionNumber
Final working code:
var query = from p in db.Projects
from d in p.Documents
where p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
select new
{
p.ProjectID,
d.DocumentNumber,
d.DocumentID,
d.DocumentTitle,
LastRevision = d.Revisions
.OrderByDescending(rn => rn.RevisionID)
.FirstOrDefault()
};
var results = from x in query
select
new
{
x.ProjectID,
x.DocumentNumber,
x.DocumentID,
x.DocumentTitle,
x.LastRevision.RevisionNumber,
x.LastRevision.DocumentStatuse.Status
};
gvDocumentSelection.DataSource = results;
gvDocumentSelection.DataBind();
If you've got 1:n navigation properties there is a much simpler (and recommended) way to achieve this:
from p in db.Projects
from d in p.Documents
select new { p, d,
LastRevision = d.Revisions
.OrderByDescending(r => r.RevisionId)
.FirstOrDefault() }
Without navigation properties it is similar:
from p in db.Projects
join d in db.Documents on new { ProjectID = p.ProjectID }
equals new { ProjectID = Convert.ToInt32(d.ProjectID) }
select new { p, d,
LastRevision = db.Revisions
.Where(r => d.DocumentID = Convert.ToInt32(r.DocumentID))
.OrderByDescending(r => r.RevisionId)
.FirstOrDefault() }
Edit
You can amend this very wide base query with all kinds of projections, like:
from x in query select new { x.p.ProjectName,
x.d.DocumentName,
x.LastRevision.DocumentStatus.Status,
x.LastRevision.FieldA,
x.LastRevision.FieldB
}

Categories

Resources