I need this query to be translated to Linq query
SELECT DISTINCT (pf.Id)
FROM
PF pf
LEFT JOIN FA fa on pf.id = fa.PFId
LEFT JOIN Fan f ON pf.FId = f.Id
WHERE
pf.PId=2 AND fa.AId IN (1,26) AND fa.AId NOT IN(27)
This is the LINQ query I have so far as requested
var allFansSavedAsLeads = _dbContext.PF
.Where(e => e.F.S != null &&
e.A.Any(x => x.AId==27 &&
x.AId.Equals(1) /*&&
x.AId != 27*/) &&
e.PId == pId);
I get zero results with this.
I suggest you Create two lists of Ids representing the Activities that can be included and activities which needs to be excluded. use them like the following:
List<int> IncludedIds = new List<int>(){1,26};
List<int> ExcludedIds = new List<int>(){27};
_dbContext.ProfileFans.Where(e => e.Fan.SNUrl != null &&
e.Activities.Any(x => IncludedIds.Any(x.ActivityId) &&
!ExcludedIds.Any(x.ActivityId) &&
e.ProfileId == profileId);
Please note: I used List<int> because of the example that you are given, you have to create the lists based on the data type of ActivityId
You can create a temporary ActivityList AS
var List<int> ActivityList = new List<int>() {1, 26}
and use something like
ActivityList.Contains(x => x.ActivityId)
But see sujith's answer for a more complete solution.
You don't need a whitelist and a blacklist. It's either one or the other. So I'm making a whitelist. If the allowed ActivityId is 1 or 26, then by definition it is definitely not 27, so there is no need to try and exclude it. I'm using int[] instead of List<int> given that the whitelist is likely to be static, but feel free to change this to a List<int> if you want to dynamically modify it.
int[] whiteList = { 1, 26 };
var allFansSavedAsLeads = _dbContext.ProfileFans.Where(pf =>
pf.Fan.SNUrl.HasValue &&
pf.Activities.Any(fa => whiteList.Contains(fa.ActivityId)));
If you want the JOINs as well, you may want to look into .Include(), but from your original SQL query you seem like you're not going to actually need the contents of the joined tables.
Related
I'm getting a sum of the checks that have been printed but haven't been cashed yet by checking 2 tabled in the database thru entitiyframework
I have tried multiple queries but I'm not too experienced in LINQ and I'm not getting the desired results, below is some of my implementations.
select sum(checks.Check_Amount) from dbo.ODC_Register checks
left join dbo.vw_BMO_Daily cashed
on checks.Check_Number = cashed.DCheckNo
where cashed.Id is null
and checks.Check_Date < '2019-9-3'
This is what i tried last
var missing = from checks in Context.ODC_Register
where(!Context.vw_BMO_Daily.Where(ma => Convert.ToInt32(ma.DCheckNo) == checks.Check_Number && checks.Check_Date <= ma.DatePosted).Any())
select new {checks.Check_Amount };
var missingSum = missing.Sum( x => x.Check_Amount);
All I need is to find a way to make this into a LINQ query
While a straight forward translation of your SQL is possible, perhaps using the GroupJoin would be a more LINQ friendly approach:
var ans = (from checks in Context.ODC_Register
where checks.Check_Date < new DateTime(2019, 9, 3)
join cashed in Context.vw_BMP_Daily on checks.Check_Number equals cashed.DCheckNo into cashedj
where !cashedj.Any()
select checks.Check_Amount).Sum();
PS Not sure why the range variable for ODC_Register is named checks since it is for one check at a time - I would call it check.
PPS In SQL and LINQ, a not exists test is usually preferable to using an empty left join:
var an2 = (from checks in Context.ODC_Register
where checks.Check_Date < new DateTime(2019, 9, 3)
where !Context.vw_BMP_Daily.Any(cashed => cashed.DCheckNo == checks.Check_Number)
select checks.Check_Amount).Sum();
New to LINQ and not sure on the correct syntax for what I want to do.
I have a "Blocklist", an array or list (could be either) of bad codes that I don't want put into this new "keys" list that I am making
currently...
var keys = (from s in context.Keys
where s.Code != "BadCode1"
where s.Code != "BadCode2"
where s.Code != "BadCode3"
where s.Code != "BadCode4"
where s.Code != "BadCode5"
orderby s.Name
select s).ToList<Keys>();
how would I trim it down to one line and have it read from the "blocklist" ?
so more like...
var keys = (from s in context.Keys
where s.Code != ANY OF THE VALUES FROM "BLOCKLIST"
orderby s.Name
select s).ToList<Keys>();
Add it to an array and then use Contains:
var badCodes = new[] { "BadCode1", "BadCode2", "BadCode3", "BadCode4", "BadCode5"};
var keys = (from s in context.Keys
where !badCodes.Contains(s.Code)
orderby s.Name
select s).ToList<Keys>();
All other answers are correct, but I'm personally a fan of this:
Assuming that you have a list of strings called blackList containing all strings you want to filter out.
var keys = context.Keys.Where(x => !blackList.Contains(x.Code))
.OrderBy(x => x.Name)
.ToList();
I just like the way this makes my code readable and easy to understand. But again, every other answer is correct.
var keys = (from s in context.Keys
where !blackList.Contains(s.Code)
orderby s.Name
select s).ToList(); // do you really need the ToList?
an array or list (could be either)
If you're doing this in memory (linq to objects) then a HashSet would perform better than either an array or a list. If you're doing it in a database then it won't make any difference.
Also, do you really need it to be in a list? If you're going to loop through the results or otherwise do something for which IEnumerable<> or IQueryable<> will serve on its own, you're probably better leaving it out.
Create a List<string> with all invalid values
List<string> sBlackList = new List<string>(){"BadCode1", "BadCode2", "BadCode3"};
and replace
where s.Code != ANY OF THE VALUES FROM "BLACKLIST"
with
where !sBlackList.Contains(s.Code)
You can also use the Except Method similar to this:
var badCodes = new[] { "BadCode1", "BadCode2", "BadCode3", "BadCode4", "BadCode5"};
var blackList = context.Keys.Where(s => badCodes.Contains(s.Code));
var filteredKeys = context.Keys.Except(blackList);
For detect intersection of any collections you can use .Contains() method.
Its simply:
1) determine you blackList:
var blackList = new List<string> {"BadCode1",
"BadCode2",
"BadCode3",
"BadCode4",
"BadCode5"};
2) filter the request by the intersection with the blackList (!blackList.Contains(s.Code)):
var keys = (from s in context.Keys
where !blackList.Contains(s.Code)
orderby s.Name
select s).ToList<Keys>();
You could have a list of codes of black list and check if it does not contain relevant codes
var keys = (from s in context.Keys
where !blackList.Contains(s.Code)
orderby s.Name
select s).ToList<Keys>();
Try to put all badcodes in a Hashset, and look for values not in "blacklist-hashest"
there is a good example here: https://stackoverflow.com/a/3944821/1117305
What I have is a string of comma separated IDs that I'm receiving from a query string (e.g. 23,51,6,87,29). Alternately, that string could just say "all".
In my Linq query I need a way to say (in pseudo code):
from l in List<>
where l.Id = all_of_the_ids_in_csv
&& other conditions
select new {...}
I'm just not sure how to go about doing that. I'm not even sure what to google to get me going in the right direction. Any pointing in the right direction would be extremely helpful.
I would suggest to split your query in 2 - first part will select by ID, and the select one will select other conditions.
First of all: check if query string contains numbers, or is just all:
var IEnumerable<ListItemType> query = sourceList;
if(queryStringValue != "All")
{
var ids = queryStringValue.Split(new[] { ',' })
.Select(x => int.Parse(x)) // remove that line id item.Id is a string
.ToArray();
query = query.Where(item => ids.Contains(item.Id));
}
from l in query
// other conditions
select new {...}
Because LINQ queries have deffered execution you can build queries like that without performance drawback. Query won't be executed until you ask for results (by ToList call or enumeration).
If you really want it with just one LINQ query:
var idArray = all_of_the_ids_in_csv.Split(',');
from l in List<>
where (all_of_the_ids_in_csv == "All" || idArray.Contains(l.Id))
&& other conditions
select new {...}
The trick is using string.Split
var ids = string.split(rawIdString, ",").ToList();
var objects = ids.Where(id=> /*filter id here */).Select(id=>new { /* id will be the single id from the csv */ });
// at this point objects will be an IEnumerable<T> where T is whatever type you created in the new statement above
I retrieve data from two different repositories:
List<F> allFs = fRepository.GetFs().ToList();
List<E> allEs = eRepository.GetEs().ToList();
Now I need to join them so I do the following:
var EFs = from c in allFs.AsQueryable()
join e in allEs on c.SerialNumber equals e.FSerialNumber
where e.Year == Convert.ToInt32(billingYear) &&
e.Month == Convert.ToInt32(billingMonth)
select new EReport
{
FSerialNumber = c.SerialNumber,
FName = c.Name,
IntCustID = Convert.ToInt32(e.IntCustID),
TotalECases = 0,
TotalPrice = "$0"
};
How can I make this LINQ query better so it will run faster? I would appreciate any suggestions.
Thanks
Unless you're able to create one repository that contains both pieces of data, which would be a far preferred solution, I can see the following things which might speed up the process.
Since you'r always filtering all E's by Month and Year, you should do that before calling ToList on the IQueryable, that way you reduce the number of E's in the join (probably considerably)
Since you're only using a subset of fields from E and F, you can use an anonymous type to limit the amount of data to transfer
Depending on how many serialnumbers you're retrieving from F's, you could filter your E's by serials in the database (or vice versa). But if most of the serialnumbers are to be expected in both sets, that doesn't really help you much further
Reasons why you might not be able to combine the repositories into one are probably because the data is coming from two separate databases.
The code, updated with the above mentioned points 1 and 2 would be similar to this:
var allFs = fRepository.GetFs().Select(f => new {f.Name, f.SerialNumber}).ToList();
int year = Convert.ToInt32(billingYear);
int month = Convert.ToInt32(billingMonth);
var allEs = eRepository.GetEs().Where(e.Year == year && e.Month == month).Select(e => new {e.FSerialNumber, e.IntCustID}).ToList();
var EFs = from c in allFs
join e in allEs on c.SerialNumber equals e.FSerialNumber
select new EReport
{
FSerialNumber = c.SerialNumber,
FName = c.Name,
IntCustID = Convert.ToInt32(e.IntCustID),
TotalECases = 0,
TotalPrice = "$0"
};
I have the following scenario: Two lists of different Types which happen to contain 3 matching properties (in reality, the names are not the same as they are from different systems/database tables, but their contents match).
In my example I have named the properties the same just to make it easier!
I'd like to get a list of Prefix+Number+Suffix for accounts where there is a matching item in lookup (NOTE: Lookup can contain the same values multiple times - the rest of the properties in the objects are different)
This is the code I am currently using, but it feels clunky. Is there a cleaner, better way to acheive the same result? I tried "Contains()" but wasn't sure how to restrict to all three properties.
var accounts = new List<Account>{
new Account{Prefix="001", Number="10101", Suffix="666"},
new Account{Prefix="001", Number="10202", Suffix="777"},
new Account{Prefix="001", Number="10303", Suffix="777"},
new Account{Prefix="002", Number="20101", Suffix="666"},
new Account{Prefix="002", Number="20101", Suffix="777"}
};
var lookup = new List<Lookup>{
new Lookup{Prefix="001", Number="10101", Suffix="666"},
new Lookup{Prefix="001", Number="10101", Suffix="666"},
new Lookup{Prefix="002", Number="20101", Suffix="666"},
new Lookup{Prefix="001", Number="10101", Suffix="666"},
};
var match = ((from a in accounts
select a)
.Intersect(from l in lookup
from a in accounts
where l.Prefix == a.Prefix &&
l.Number == a.Number &&
l.Suffix == a.Suffix
select a)
).Select(a => string.Format("{0}{1}{2}", a.Prefix, a.Number, a.Suffix));
You can use the following code to get the match:
var match = (from a in accounts
select new { P = a.Prefix, N = a.Number, S = a.Suffix })
.Intersect(from l in lookup
select new { P = l.Prefix, N = l.Number, S = l.Suffix })
.Select(t => string.Format("{0}{1}{2}", t.P, t.N, t.S));;
You make use here of the automatically generated equality operators on anonymous types.
Why not just simply:
match = (from l in lookup
from a in accounts
where l.Prefix == a.Prefix &&
l.Number == a.Number &&
l.Suffix == a.Suffix
select string.Format("{0}|{1}|{2}", l.Prefix, l.Number, l.Suffix))
.Distinct();
Try this:
from a in accounts
join l in lookup on
new
{
a.Prefix, a.Number, a. Suffix
}
equals
new
{
l.Prefix, l.Number, l. Suffix
}
into gls
select a
I would not work directly with the tables but use a database view in cases like this. Create a view which performs the union for you and returns a normalised data structure, like so:
CREATE VIEW ExampleView
AS
SELECT
Prefix = a.Prefix,
Number = a.Number,
Suffix = a.Suffix
FROM
FirstTable AS a
UNION ALL
SELECT
Prefix = l.Prefix,
Number = l.NumberWithDifferentName,
Suffix = l.WeirdlyNamedSuffix
FROM
SecondTable AS l
Then you can run a simple select on that view instead of performing complex database logic in your application, where it does not really belong anyway:
SELECT Prefix, Number, Suffix FROM ExampleView; /* or obviously the LINQ equivalent */
Here a link to an article on that matter (why to use views): http://www.tdan.com/view-articles/5109. To cite the part that explains why its better practice to let the database do what it does best, not the application:
Developers find having to work with normalized data structures awkward and time-consuming, since it involves coding complex SQL queries that join data from multiple tables. [...] "refactoring" non-normalized data structures into normalized ones after the fact is always extremely difficult and labor-intensive, and sometimes isn't even possible (because data in non-key fields must be "refactored" into key fields, and data in these fields may have missing or incorrect values).
why dont you try join between them
from a in accounts
join l in lookup
on
new { a.Prefix, a.Number, a. Suffix}
equals
new { l.Prefix, l.Number,l. Suffix}
select a;