How can I merge two outputs of two Linq queries? - linq

I'm trying to merge these two object but not totally sure how.. Can you help me merge these two result objects?
//
// Create Linq Query for all segments in "CognosSecurity"
//
var userListAuthoritative = (from c in ctx.CognosSecurities
where (c.SecurityType == 1 || c.SecurityType == 2)
select new {c.SecurityType, c.LoginName , c.SecurityName}).Distinct();
//
// Create Linq Query for all segments in "CognosSecurity"
//
var userListAuthoritative3 = (from c in ctx.CognosSecurities
where c.SecurityType == 3 || c.SecurityType == 0
select new {c.SecurityType , c.LoginName }).Distinct();
I think I see where to go with this... but to answer the question the types of the objects are int, string, string for SecurityType, LoginName , and SecurityName respectively
If you're wondering why I have them broken like this is because I want to ignore one column when doing a distinct. Here are the SQL queries that I'm converting to SQL.
select distinct SecurityType, LoginName, 'Segment'+'-'+SecurityName
FROM [NFPDW].[dbo].[CognosSecurity]
where SecurityType =1
select distinct SecurityType, LoginName, 'Business Line'+'-'+SecurityName
FROM [NFPDW].[dbo].[CognosSecurity]
where SecurityType =2
select distinct SecurityType, LoginName, SecurityName
FROM [NFPDW].[dbo].[CognosSecurity]
where SecurityType in (1,2)

You can't join these because the types are different (first has 3 properties in the resulting type, second has two).
If you can tolerate putting a null value in for the 3rd result of the second query this will help. I would then suggest you just do a userListAuthoritative.concat(userListAuthoritative3 ) BUT I think this will not work as the anonymous types generated by the linq will not be of the same class, even tho the structure is the same. To solve that you can either define a CustomType to encapsulate the tuple and do select new CustomType{ ... } in both queries or postprocess the results using select() in a similar fashion.
Acutally the latter select() approach will also allow you to solve the parameter count mismatch by implementing the select with a null in the post-process to CustomType.
EDIT: According to the comment below once the structures are the same the anonymous types will be the same.

I assume that you want to keep the results distinct:
var merged = userListAuthoritative.Concat(userListAuthoritative3).Distinct();
And, as Mike Q pointed out, you need to make sure that your types match, either by giving the anonymous types the same signature, or by creating your own POCO class specifically for this purpose.
Edit
If I understand your edit, you want your Distinct to ignore the SecurityName column. Is that correct?
var userListAuthoritative = from c in ctx.CognosSecurities
where new[]{0,1,2,3}.Contains(c.SecurityType)
group new {c.SecurityType, c.LoginName, c.SecurityName}
by new {c.SecurityType, c.LoginName}
select g.FirstOrDefault();

I'm not exactly sure what you mean by merge, since you're returning different (anonymous) types from each one. Is there a reason the following doesn't work for you?
var userListAuthoritative = (from c in ctx.CognosSecurities
where (c.SecurityType == 1 || c.SecurityType == 2 || c.SecurityType == 3 || c.SecurityType == 0)
select new {c.SecurityType, c.LoginName , c.SecurityName}).Distinct();

Edit: This assumed they were of the same type -- but they're not.
userListAuthoritative.Concat(userListAuthoritative3);

Try below code, you might need to implement IEqualityComparer<T> in your ctx type.
var merged = userListAuthoritative.Union(userListAuthoritative3);

Related

Using the result of one LINQ query as the parameter for the next

I'm trying to use the result of one LINQ query to be the parameter for the next part of a LINQ query, but to keep it all within one overall query.
For example, my (not-working) code looks like this
List<Categories> myCats = (from city in myCities
where city.CityName == myCityName
select city
from ids in city.IDs
where ids != 4
select ids).ToList();
Can this feed the result to be the be start of the next query be performed in this fashion and if it can, what am I missing to get this to work?
city.IDs is an int array
Unless you're actually trying to create a new projection, you can simply avoid using a select until the end.
List<Categories> myCats = (from city in myCities
where city.CityName == myCityName
//select city
from ids in city.IDs
where ids != 4
select ids).ToList();
I personally like breaking up my queries into pieces:
var matchingCities = from city in myCities
where city.CityName == myCityName
select city;
var matchingCityIds = (from city in matchingCities
from id in city.IDs
select id).ToList();
This approach has two main advantages:
The variable names give you automatic "documentation", allowing other developers to see what the intent of each transformation is.
It's easier to debug because you can step over each transformation and verify that you got the results you wanted.
If you really do need to follow one select by another, though, you can use the into keyword to chain the queries together.
List<Categories> myCats = (from city in myCities
where city.CityName == myCityName
// Not typically recommended
select city.IDs into cityIDs
from id in cityIDs
where id != 4
select id).ToList();

linq select clause syntax

Linq Select method takes Func as input parameter. This means I can have multiple statements in selector for Select, such as
var myresult = sources.Select(s =>
{int x; if (s.val = high) {x=1} else if (s.val = med) {x=2} else {x=3}; return x;
}
)
How can I do this using Linq query syntax
var myresult = from s in sources
select ...
Here, the code in Func part (if ... else if .. else) is artificial. What I really want to know is the syntax of select clause, which may be described as
select select-expression
What is the syntax of
select-expression
I wouldn't want to see your first version in my code. If you need to have what is basically a full function in the lambda, I would rather see the lambda simply invoke a full function! In other words...
theQuery.Select(s => GetX(s)); // just define a GetX function
And that would also be a straightforward translation to query expression syntax
from s in sources
select GetX(s);
You would not be able to put your full code block into the query expression syntax. You could translate your given logic to something usable (yet messy), however I'm quite sure your snippet is just a general example. On the offhand change it isn't, you might try
select s.val == high ? 1 : (s.val == med ? 2 : 3); // totally messy
Instead of special-casing values, with the if/else equivalent of a switch statement, it is more Linq-friendly to group and filter your values:
var myResult = from s in sources
group by s.val into g
select new { Val = g.Key, Sources = g };
var groupHigh = myResult.Where(i => i.Val == high);
var groupMedium = myResult.Where(i => i.Val == medium);
var groupOther = myResult.Except(groupHigh.Concat(groupMedium));
Note that the code I've provided is just a starting place, and isn't the best way to achieve your specific goal. I'd address this in one of these ways:
Change how group by is used (use SomeFunction(s.Val) instead of directly using s.Val)
Change the code around this query to flow better with the natural groupings, so I didn't require the groups to be transformed
This is not possible.
If you really want to, you could create an Func<T> from an anonymous method and invoke it, but that would be horrible.
MSDN indicates select is a contextual keyword of C# 4.0. So I checked the C# Language Specififcation 4.0. Its Select clauses section (7.16.2.5) specifies that
A query expression of the form
from x in e select v
is translated into
( e ) . Select ( x => v )
except when v is the identifier x, the translation is simply
( e )
As the result, the syntax for
select select-expresion
select-expression should be anything that can be used as TResult in Select Method. So the functionality can be done using anonymous Func in Select method may not be able to achieved using select clause.
Conclusion is that you should stick with Method syntax as this is how the code really runs behind the scene.

LINQ subquery question

Can anybody tell me how I would get the records in the first statement that are not in the second statement (see below)?
from or in TblOrganisations
where or.OrgType == 2
select or.PkOrgID
Second query:
from o in TblOrganisations
join m in LuMetricSites
on o.PkOrgID equals m.FkSiteID
orderby m.SiteOrder
select o.PkOrgID
If you only need the IDs then Except should do the trick:
var inFirstButNotInSecond = first.Except(second);
Note that Except treats the two sequences as sets. This means that any duplicate elements in first won't be included in the results. I suspect that this won't be a problem since the name PkOrgID suggests a unique ID of some kind.
(See the documentation for Enumerable.Except and Queryable.Except for more info.)
Do you need the whole records, or just the IDs? The IDs are easy...
var ids = firstQuery.Except(secondQuery);
EDIT: Okay, if you can't do that, you'll need something like:
var secondQuery = ...; // As you've already got it
var query = from or in TblOrganisations
where or.OrgType == 2
where !secondQuery.Contains(or.PkOrgID)
select ...;
Check the SQL it produces, but I think it should do the right thing. Note that there's no point in performing any ordering in the second query - or even the join against TblOrganisations. In other words, you could use:
var query = from or in TblOrganisations
where or.OrgType == 2
where !LuMetricSites.Select(m => m.FkSiteID).Contains(or.PkOrgID)
select ...;
Use Except:
var filtered = first.Except(second);

Combining 2 Linq queries into 1

Given the following information, how can I combine these 2 linq queries into 1. Having a bit of trouble with the join statement.
'projectDetails' is just a list of ProjectDetails
ProjectDetails (1 to many) PCardAuthorizations
ProjectDetails (1 to many) ExpenditureDetails
Notice I am grouping by the same information and selecting the same type of information
var pCardAccount = from c in PCardAuthorizations
where projectDetails.Contains(c.ProjectDetail)
&& c.RequestStatusId == 2
group c by new { c.ProjectDetail, c.ProgramFund } into g
select new { Key = g.Key, Sum = g.Sum(x => x.Amount) };
var expenditures = from d in ExpenditureDetails
where projectDetails.Contains(d.ProjectDetails)
&& d.Expenditures.ExpenditureTypeEnum == 0
group d by new { d.ProjectDetails, d.ProgramFunds } into g
select new {
Key = g.Key,
Sum = g.Sum(y => y.ExpenditureAmounts.FirstOrDefault(a => a.IsCurrent && !a.RequiresAudit).CommittedMonthlyRecords.ProjectedEac)
};
I don't think Mike's suggestion will work here. These two queries are sufficiently different that combining them into one query will just make it more difficult to read.
The objects have different types.
The where clauses are different.
The group by clause is different:
new { c.ProjectDetail, c.ProgramFund }
new { c.ProjectDetails, c.ProgramFunds }
The select clauses are different.
In fact there isn't really anything that is the same. I'd recommend leaving it as it is.
You could always just concat them together before evaluating them.
pCardAccount.Concat(expenditures).ToArray()
should generate a single sql statement with a union. As to whether there is any benefit to this in your situation, I don't know. There's also a chance that linq-to-sql won't be able to generate the SQL for the concat and will throw an exception whenever you use it. I'm not sure what causes it, but I've seen it a few times when I was playing around in a similar situation.
Edit: I just noticed that your keys are different in each of the queries. I'm not sure if this is a typo or not, but if it isn't, you won't be able to concat them due to the different types and it wouldn't make any sense to anyway

nhibernate.linq simple (read dumb) question

I'm trying to wrap my head around linq -> nhib
I have a simple bit of sql that i'm trying to get working in nhibernate.linq
select * from
ColModel where ColModel.DataIndex
not in ('CostElement1', 'CostElement2', 'CostElement3')
and ColModel.ReportId = 1
The list of excluded DataIndex values comes in in the form of a List<string> called excludeNames
Here is what I have tried but it seems that it's not really feeling the love:
var s = base._sessionManager.OpenSession();
var query = from col in s.Linq<ColModel>()
where col.Report.Id == reportid &&
!(from c in s.Linq<ColModel>() select c.DataIndex).Contains(excludeNames)
select col;
return query.ToList();
the error:
The type arguments for method 'System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource>, TSource)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
I'm pretty sure I'm borfing this from the offset so any pointers would be well appreciated :)
w://
Contains doesn't accept a list.
There are ways to work around this in LINQ, but I'm not sure which, if any, of those will work in NH Linq
I think you have your exclusion backwards.
s = base._sessionManager.OpenSession();
var query = from col in s.Linq<ColModel>()
where col.Report.Id == reportid &&
!excludeNames.Contains(col.DataIndex)
select col;
return query.ToList();
Collection.Contains(item) will produce the SQL item in (...collection...), adding the negation will get you what you want.

Resources