Dynamic where clause in LINQ? - c#

I am trying to load data based on Dynamic where condition.
string tempQry = string.Empty;
if (!string.IsNullOrEmpty(cusid) && !string.IsNullOrEmpty(mktid))
tempQry = "x=>x.MarketID==" + mktid + "&& x.MasterCustomerID==" + cusid;
if (string.IsNullOrEmpty(cusid))
tempQry = "x=>x.MarketID==" + mktid;
if (string.IsNullOrEmpty(mktid))
tempQry = "x=>x.MasterCustomerID==" + cusid;
_lstOptInInterest = new LinkedList<OptInInterestArea>(
(from a in _lstOptInInterest
join b in _marketoEntities.CustCommPreferences.Where(tempQry)
on new { CODE = a.Code, SUBCODE = a.SubCode } equals new { CODE = b.Option_Short_Name, SUBCODE = b.Option_Short_Subname }
into leftGroup
from b in leftGroup.DefaultIfEmpty()
select new OptInInterestArea()
{
Code = a.Code,
SubCode = a.SubCode,
SubCodeDescription = a.SubCodeDescription,
CodeDescription = a.CodeDescription,
PrevOptIn = b != null && b.OptedIn == true
}).ToList());
It is giving compilation error Where(tempQry).
'System.Data.Entity.DbSet<Market.Data.CustCommPreference>' does not contain a definition for 'Where' and the best extension method overload 'System.Linq.Queryable.Where<TSource>(System.Linq.IQueryable<TSource>, System.Linq.Expressions.Expression<System.Func<TSource,bool>>)' has some invalid arguments
How to handle this?

Where awaits conditions in form of lambdas rather than strings, so you have to refactor your code a little bit (just an idea below):
IQueryable<CustCommPreference> query = _marketoEntities.CustCommPreferences.AsQueryable();
if (!string.IsNullOrEmpty(cusid))
query = query.Where(x => x.MasterCustomerID == cusid);
if (!string.IsNullOrEmpty(mktid))
query = query.Where(x => x.MarketID == mktid);
and later use it:
...
join b in query
...

see this blog by Scott. It should help you sort your issue

The error you are seeing appears to indicate that you are using EF not LINQ to SQL. Please correct your tags if that is the case. If you want to use strings, consider using ObjectQuery's Where method instead of using DBSet. Alternatively, you could build the entire query using EntitySQL.

Related

C# Linq where clause

I'm trying to create a linq query where clause is a constructed variable based on user choices
if (!string.IsNullOrEmpty(txbbox.Text))
{
query = "s.date== DateTime.Parse(txbbox.Text)";
}
if (!string.IsNullOrEmpty(txbbox.Text))
{
query = query + " & (s.type.Contains(txbbox.Text))";
}
there is a way to pass the variable built in the where clause in a LINQ query?
Stemp = (from s in SList
where ***query***
orderby s.DataScadenza
select s).ToList();
Yes; quite simple in this case, actually:
IEnumerable<Whatever> query = SList; // avoid var here, as will impact later assignment
if (!string.IsNullOrEmpty(txbbox.Text))
{
var when = DateTime.Parse(txbbox.Text); // to avoid parsing per item
query = query.Where(s => s.date == when);
}
if (!string.IsNullOrEmpty(txbbox.Text))
{
query = query.Where(s => s.type.Contains(txbbox.Text));
}
Stemp = query.OrderBy(s => s.DateScadenze).ToList();

Error converting SQL with join to LINQ

I have a SQL query that I'm trying to convert to LINQ and am having trouble understanding the obscure error messages when the query is enumerated.
The SQL query (which works as intended), is:
select a.TestGuid, MIN(a.StartTime) as StartTime, COUNT(b.TestCaseId) as NumTests, COUNT(DINSTINCT a.Id) as NumScenarios
from LoadTestSummary as a
join LoadTestTestSummaryData as b
on a.LoadTestRunid = b.LoadTestRunId
where
a.TargetStack = env and
a.TestGuid IS NOT NULL AND
a.StartTime IS NOT NULL AND
a.LoadTestRunId IS NOT NULL
group by a.TestGuid
Converting to LINQ, I get the following:
var q = from a in _context.LoadTestSummary
where
a.TargetStack == env &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
join b in _context.LoadTestTestSummaryData on new
{
LoadTestRunId = Convert.ToInt32(a.LoadTestRunId)
} equals new
{
LoadTestRunId = b.LoadTestRunId
}
group new { a, b } by new
{
a.TestGuid
}
into g
select new
{
DateCreated = g.Min(p => p.a.StartTime),
NumScenarios = g.Count(),
TestGuid = g.Key.TestGuid
NumTests = // ???
};
Two problems I have:
1) When the query is enumerated I get a run-time error that I'm having trouble deciphering. The query works fine in Linqpad, but gives me a run-time error in my program. I am not sure what would cause this. Just staring at this makes my head hurt:
ArgumentException: Expression of type 'System.Func``2[Microsoft.Data.Entity.Query.EntityQueryModelVisitor+TransparentIdentifier``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData],<>f__AnonymousType7``1[System.String]]' cannot be used for parameter of type 'System.Func``2[<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData],<>f__AnonymousType7``1[System.String]]' of method 'System.Collections.Generic.IEnumerable``1[System.Linq.IGrouping``2[<>f__AnonymousType7``1[System.String],<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData]]] _GroupBy[<>f__AnonymousType5``2,<>f__AnonymousType7``1,<>f__AnonymousType5``2](System.Collections.Generic.IEnumerable``1[<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData]], System.Func``2[<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData],<>f__AnonymousType7``1[System.String]], System.Func``2[<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData],<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData]])'
2) I am not quite sure how to get the COUNT(DISTINCT a.Id) into the NumTests field. It looks like this isn't supported in LINQ but it looks like other people have asked this question to so I may be able to figure it out once #1 is resolved.
Any thoughts on what's wrong here? I am not even sure exactly what the error is telling me.
All help is appreciated!
Looking just at the SQL query and your LINQ code, I came up with something like this:
from a in LoadTestSummary
join b in LoadTestTestSummaryData
on a.LoadTestRunId equals b.LoadTestRunId
where
a.TargetStack == env &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
group new { a, b } by a.TestGuid into g
select new
{
TestGuid = g.Key,
DateCreated = g.Min(el => el.a.StartTime),
NumTests = g.Select(el => el.b.TestCaseId).Count(),
NumScenarios = g.Select(el => el.a.Id).Distinct().Count()
};
Note, that you don't need to convert LoadTestRunId to int, you may just use standard string comparision.
That horrendous error is most likely caused by grouping and comparing using anonimous objects, thou I prefer not to read that error too much as it's an eldritch abomination not ment to be seen nor comprehend by mere mortals, it seems.

LINQ throws error on using Regex.Split()

ERROR
LINQ to Entities does not recognize the method 'System.String[] Split(System.String, System.String)' method, and this method cannot be translated into a store expression.
FOR CODE
var getfrmbid = (from e in _dbEntity.FormNames
where e.form_id == id & e.type == "Form"
select new FormsCreationModel
{
form_name = e.form_name,
form_id = e.form_id,
formfields = (from i in _dbEntity.FormDetails
where e.form_id == i.form_id
select i).AsEnumerable().Select(x=> new FormDetailsModel()
{
field_default = x.field_default,
field_id = x.field_id,
field_mandatory = x.field_mandatory,
field_maxlength = x.field_maxlength,
field_name = x.field_name,
field_type = x.field_type,
field_validation = x.field_validation,
field_value = Regex.Split(x.field_value, " ^ ").Select(item => new DropDownValue() { DDValue = item }).ToList()
}).ToList()
}).Single();
NOTE
Error Spot at field_value is of type List<DropDownValue> in FormDetailsModel
x.field_value is a String and I am converting it into an String[] using Regex.Split() and then to List<DropDownValue> to assign it on field_value.
How could I assign field_value from x.field_value after splitting?
You must replace
select i).AsEnumerable().Select(x=> new FormDetailsModel()
with
select i).ToList().Select(x=> new FormDetailsModel()
When you use .ToList(), DB is queried and Select extension runs locally. Otherwise lambda expression is compiled into sql query and clearly SQL doesn't know Regex.Split(). In Linq2Objects you can use that methods since it runs locally. It's important have in mind what LinqTo* you are using.

What are the two ways I can write this statement with LINQ?

I understand that there are two different ways I can write LINQ code. Can someone show me the two ways for this simple code block. Which is the most commonly used or considered most easy to debug
var subTopics = _subTopicService.GetSubTopics(Id);
var subTopicsSelect = (from subTopic in subTopics
select new
{
id = subTopic.SubTopicId,
name = subTopic.Name
});
Since your query consists solely of a from and select clause, all you need to do to convert this to fluent syntax is call .Select.
In fluent syntax, that would be:
var subTopicsSelect = subTopics.Select(x =>
new
{
id = x.SubTopicId,
name = x.Name
});
Further Reading
How to: Write LINQ Queries in C#
You have displayed the first way "SQL-like syntax" the second would be "Lambda syntax":
subTopics.Select(s => new { id = s.SubTopicId, name = s.Name });
This really confuses me as I have a completely different way to select here are the two methods:
var emailsToSend = db.emailQueues.Where(
e => e.sent == false
).Take(5);
var emailsToSend2 = from e2 in db.emailQueues
.Take(5)
.Where(
e => e.sent == false
)
select e2;
They both seem to do exactly the same thing but i prefer the syntax of the first method. Its easier to remember.

Using Intersect I'm getting a Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator

I'm using a Linq to SQL query to provide a list of search term matches against a database field. The search terms are an in memory string array. Specifically, I'm using an "intersect" within the Linq query, comparing the search terms with a database field "Description". In the below code, the description field is iss.description. The description field is separated into an array within the Linq query and the intersect is used to compare the search terms and description term to keep all of the comparing and conditions within the Linq query so that the database is not taxed. In my research, trying o overcome the problem, I have found that the use of an in-memory, or "local" sequence is not supported. I have also tried a few suggestions during my research, like using "AsEnumerable" or "AsQueryable" without success.
searchText = searchText.ToUpper();
var searchTerms = searchText.Split(' ');
var issuesList1 = (
from iss in DatabaseConnection.CustomerIssues
let desc = iss.Description.ToUpper().Split(' ')
let count = desc.Intersect(searchTerms).Count()
where desc.Intersect(searchTerms).Count() > 0
join stoi in DatabaseConnection.SolutionToIssues on iss.IssueID equals stoi.IssueID into stoiToiss
from stTois in stoiToiss.DefaultIfEmpty()
join solJoin in DatabaseConnection.Solutions on stTois.SolutionID equals solJoin.SolutionID into solutionJoin
from solution in solutionJoin.DefaultIfEmpty()
select new IssuesAndSolutions
{
IssueID = iss.IssueID,
IssueDesc = iss.Description,
SearchHits = count,
SolutionDesc = (solution.Description == null)? "No Solutions":solution.Description,
SolutionID = (solution.SolutionID == null) ? 0 : solution.SolutionID,
SolutionToIssueID = (stTois.SolutionToIssueID == null) ? 0 : stTois.SolutionToIssueID,
Successful = (stTois.Successful == null)? false : stTois.Successful
}).ToList();
...
The only way I have been successful is to create two queries and calling a method as shown below, but this requires the Linq Query to return all of the matching results (with the number of hits for search terms in the description) including the non-matched records and provide an in-memory List<> and then use another Linq Query to filter out the non-matched records.
public static int CountHits(string[] searchTerms, string Description)
{
int hits = 0;
foreach (string item in searchTerms)
{
if (Description.ToUpper().Contains(item.Trim().ToUpper())) hits++;
}
return hits;
}
public static List<IssuesAndSolutions> SearchIssuesAndSolutions(string searchText)
{
using (BYCNCDatabaseDataContext DatabaseConnection = new BYCNCDatabaseDataContext())
{
searchText = searchText.ToUpper();
var searchTerms = searchText.Split(' ');
var issuesList1 = (
from iss in DatabaseConnection.CustomerIssues
join stoi in DatabaseConnection.SolutionToIssues on iss.IssueID equals stoi.IssueID into stoiToiss
from stTois in stoiToiss.DefaultIfEmpty()
join solJoin in DatabaseConnection.Solutions on stTois.SolutionID equals solJoin.SolutionID into solutionJoin
from solution in solutionJoin.DefaultIfEmpty()
select new IssuesAndSolutions
{
IssueID = iss.IssueID,
IssueDesc = iss.Description,
SearchHits = CountHits(searchTerms, iss.Description),
SolutionDesc = (solution.Description == null)? "No Solutions":solution.Description,
SolutionID = (solution.SolutionID == null) ? 0 : solution.SolutionID,
SolutionToIssueID = (stTois.SolutionToIssueID == null) ? 0 : stTois.SolutionToIssueID,
Successful = (stTois.Successful == null)? false : stTois.Successful
}).ToList();
var issuesList = (
from iss in issuesList1
where iss.SearchHits > 0
select iss).ToList();
...
I would be comfortable with two Linq Queries, but with the first Linq Query only returning the matched records and then maybe using a second, maybe lambda expression to order them, but my trials have not been successful.
Any help would be most appreciated.
Ok, so after more searching more techniques, and trying user1010609's technique, I managed to get it working after an almost complete rewrite. The following code first provides a flat record query with all of the information I am searching, then a new list is formed with the filtered information compared against the search terms (counting the hits of each search term for ordering by relevance). I was careful not to return a list of the flat file so there would be some efficiency in the final database retrieval (during the formation of the filtered List<>). I am positive this is not even close to being an efficient method, but it works. I am eager to see more and unique techniques to solving this type of problem. Thanks!
searchText = searchText.ToUpper();
List<string> searchTerms = searchText.Split(' ').ToList();
var allIssues =
from iss in DatabaseConnection.CustomerIssues
join stoi in DatabaseConnection.SolutionToIssues on iss.IssueID equals stoi.IssueID into stoiToiss
from stTois in stoiToiss.DefaultIfEmpty()
join solJoin in DatabaseConnection.Solutions on stTois.SolutionID equals solJoin.SolutionID into solutionJoin
from solution in solutionJoin.DefaultIfEmpty()
select new IssuesAndSolutions
{
IssueID = iss.IssueID,
IssueDesc = iss.Description,
SolutionDesc = (solution.Description == null) ? "No Solutions" : solution.Description,
SolutionID = (solution.SolutionID == null) ? 0 : solution.SolutionID,
SolutionToIssueID = (stTois.SolutionToIssueID == null) ? 0 : stTois.SolutionToIssueID,
Successful = (stTois.Successful == null) ? false : stTois.Successful
};
List<IssuesAndSolutions> filteredIssues = new List<IssuesAndSolutions>();
foreach (var issue in allIssues)
{
int hits = 0;
foreach (var term in searchTerms)
{
if (issue.IssueDesc.ToUpper().Contains(term.Trim())) hits++;
}
if (hits > 0)
{
IssuesAndSolutions matchedIssue = new IssuesAndSolutions();
matchedIssue.IssueID = issue.IssueID;
matchedIssue.IssueDesc = issue.IssueDesc;
matchedIssue.SearchHits = hits;
matchedIssue.CustomerID = issue.CustomerID;
matchedIssue.AssemblyID = issue.AssemblyID;
matchedIssue.DateOfIssue = issue.DateOfIssue;
matchedIssue.DateOfResolution = issue.DateOfResolution;
matchedIssue.CostOFIssue = issue.CostOFIssue;
matchedIssue.ProductID = issue.ProductID;
filteredIssues.Add(matchedIssue);
}
}

Categories

Resources