LINQ Where clause with four && - c#

I'm trying to create an LINQ Query with 4 arguments in the Where clause. It's a Windows 8 App project and I'm using an SQLite Database. (SQLite implementation )
Here's the code snippet:
public List<FinancialListBoxExpenseItem> retrieveExpenseItems(int month, int year, bool isPaid, StaticResources.FrequencyEnum frequencyEnum)
{
List<FinancialListBoxExpenseItem> tmpList = null;
connection.RunInTransaction(() =>
{
var items = from s in connection.Table<FinancialListBoxExpenseItem>()
where (s.expenseDateNextPayment.Month == month)
&& (s.expenseDateNextPayment.Year == year)
&& (s.expensePaidForCurrentPeriod == isPaid)
&& (s.expenseFrequencyTypeEnum == frequencyEnum)
select s;
tmpList = items.ToList<FinancialListBoxExpenseItem>();
});
return tmpList;
}
It throws a NotSupportedAction: Member access failed to compile expression Exception
I have no idea what does this mean and how i'm supposed to fix it.
Edit: it works without the where clause therefore the error must be related to this where clause part of the code

Probably .Month is not supported by your LINQ provider. You'll have to work around that, possibly by creating specialized columns for the month and the year.

This is how i solved the problem:
public List<FinancialListBoxExpenseItem> retrieveExpenseItems(int month, int year, bool isPaid, StaticResources.FrequencyEnum frequencyEnum)
{
List<FinancialListBoxExpenseItem> tmpList = new List<FinancialListBoxExpenseItem>();
connection.RunInTransaction(() =>
{
var items = from s in connection.Table<FinancialListBoxExpenseItem>()
let convertedDate = (DateTime)s.expenseDateNextPayment
where (convertedDate.Month == month)
&& (convertedDate.Year == year)
&& (s.expensePaidForCurrentPeriod == isPaid)
&& (s.expenseFrequencyTypeEnum == frequencyEnum)
select s;
tmpList = items.ToList();
});
return tmpList;
}

In my App I was getting a NotSupportedException when running a LINQ query, and the details of the exception showed
Member access failed to compile expression
As this thread let me know, the issued seemed to be caused by a DateTime variable that was being referenced in the query. Outside of finding a StackOverflow thread like this one to point you in the right direction, I'm not sure how anyone is supposed to figure that out on their own, but for me, changing the way I was writing the query fixed the problem.
This syntax was throwing the "NotSupportedException":*
IEnumerable<Foo> foos = connection.Table<Foo>().Where(foo => foo.Timestamp.Year == year);
Switching to this syntax worked just fine:
IEnumerable<Foo> foos = connection.Table<Foo>().Where(
delegate(Foo foo)
{
return (foo.Timestamp.Year == year);
});

Related

Entity Framework Non static method needs a target. Null values in Lambda

I'm having a problem in Entity Framework with Lambda Expressions with objects that can be null. I'm doing a Query to search in my Database by some fields, the fields are optionally and if the field is null I do not use it for conditions in the Query and if the field is not null I use it in the Query.
For this I'm trying to do something like this.
//Can be null or not
DateTime? date;
//This thorows an Exception "Non Static method needs a target"
List <> data = db.T_USER.Where(U =>
(date == null || U.JoinDate == date)
).ToList();
This code is throwing an Exception "Non static method needs a target"
Searching for solutions here I have found that there are problems with null values in querys so I try to do this. But it also didn't work even if the second par of the OR will never be evaluated because the first part is true.
bool DateIsNull = (date == null);
//This thorows an Exception "Non Static method needs a target"
List <> data = db.T_USER.Where(U =>
(DateIsNull || U.JoinDate == date)
).ToList();
My finally solution is this and it works. But this code i think is ugly. Imagine this stupid code for 5 optional fields or more...
//Can be null or not
DateTime? date;
bool DateIsNull = (date == null);
DateTime _filterDate = !DateIsNull ? date.Value : DateTime.Now;
List <> data = db.T_USER.Where(U =>
(DateIsNull || U.JoinDate == _filterDate)
).ToList();
Does anyone have a better and more elegant solution for my problem?
Could someone explain me the details of the reason of throwing the Exception?
The way I've typically handled this type of scenario is to build the query based on which parameters have values. Something like this:
// optional query parameters coming from somewhere...
DateTime? date;
int? age;
string username;
IQueryable<T_USER> query = db.T_USER.AsQueryable();
if(date != null)
query = query.Where(u => u.JoinDate == date);
if(age != null)
query = query.Where(u => u.Age == age);
if(username != null)
query = query.Where(u => u.Username == username);
var results = query.ToList();
To me, this is easier to read and it avoids the issue of putting your local expression or variable into the lambda expression that gets passed on to the EF provider.

C# LINQ DataTime List

Have a some problem with converting to List
public List<DateTime> findDateBetween(DateTime start,DateTime end)
{
var query = from entry in sd.gw_chemistry
where (entry.insert_datetime >=start & entry.insert_datetime<=end & a == entry.well_id & b == entry.indicator_id)
select entry.insert_datetime;
return (List<DateTime>)query;
}`
Error:
System.InvalidCastException: Unable to cast object of type "System.Data.Objects.ObjectQuery1[System.Nullable1[System.DateTime]]" to type "System.Collections.Generic.List`1[System.DateTime]".
There are a number of problems with your code.
Your query is selecting elements of type DateTime? (or Nullable<DateTime>). You will need to decide what you want to do if a date is null. Exclude it from the results? Return a default value? If you can be sure it will never be null, you can select entry.insert_datetime.Value.
Your query does not return a list, you will have to convert it to a list using ToList().
For the conditional AND operator (&&) it appears you are using &.
You are using variables a and b that do not seem to be defined anywhere (unless they are member variables).
So assuming that a and b are member variables, and an insert_datetime is never null you can do:
return sd.gw_chemistry
.Where(e =>
e.insert_datetime >= start && e.insert_datetime <= end &&
a == entry.well_id && b == entry.indicator_id)
.Select(e => e.insert_datetime.Value)
.ToList();
As the error is trying to tell you, that isn't a List<T>, and you can't cast it to a type that it isn't.
You can create a List<T> from your query by calling .ToList().
public List<DateTime> findDateBetween(DateTime start,DateTime end)
{var query = from entry in sd.gw_chemistry
where (entry.insert_datetime >=start & entry.insert_datetime<=end & a == entry.well_id & b == entry.indicator_id)
select entry.insert_datetime;
return query.ToList();}
Nothing serious here. You just need to call ToList() on your query. However I do see the usage of & instead of && as a serious problem.
Correct code should look like:
public List<DateTime> findDateBetween(DateTime start,DateTime end)
{
var query =
from entry in sd.gw_chemistry
where (entry.insert_datetime >=start &&
entry.insert_datetime<=end &&
a == entry.well_id &&
b == entry.indicator_id)
select entry.insert_datetime.Value;
return query.ToList();
}`

How to call a method in Linq query?

Of course I made some research and tried something but can't make it.
I have this code so far:
var workLogs = (from wl in _db.WorkLogs
where (wl.Users_UserId == userId && wl.Works_WorkId == workId && wl.Date > beginDate && wl.Date < endDate)
select new
{
wl.Users_UserId,
wl.Works_WorkId,
wl.Time,
wl.Date
})
.AsEnumerable()
.Select(wl => new
{
userName = GetUserNameFromId(wl.Users_UserId),
workName = GetWorkNameFromId(wl.Works_WorkId),
wl.Time,
wl.Date
});
As you can see I tried enumeration but when I run the application, workLogs comes empty. And when I inspect it with a breakpoint, I see "Enumeration yielded no results."
What I trying to make is return work and user name in workLogs. And I wrote two method to do this. I just cant call them in my Linq query.
Without my methods, it works good as you can see it: http://i.imgur.com/ALb5f0K.png
And my methods also works good outside of query.
I hope I made myself clear.
If there are no results then it means your underlying source doesn't have data, or your where clause is filtering out more than you think it should be. There is no problem that you could have with your select statement(s) that would cause a sequence that would normally return results to instead return no results. At most you could cause it to throw an exception.
There is no reason why the following code should not work. You do not need other code to make this work.
If this return no results, it just means that the where clause is filtering everything.
Make a manual foreach with a if statement to debug this and you will see that there is no problem with your LINQ request.
var workLogs = from wl in _db.WorkLogs
where (wl.Users_UserId == userId && wl.Works_WorkId == workId && wl.Date > beginDate && wl.Date < endDate)
select new
{
GetUserNameFromId(wl.Users_UserId),
GetWorkNameFromId(wl.Works_WorkId),
wl.Time,
wl.Date
}
Try replacing .AsEnumerable() with .ToArray().

Multiple WHERE's in same LINQ 2 SQL Method

I have the below LINQ Method I am trying to create. The issue seems to be the Second WHERE clause. I am getting this error -->
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<MatrixReloaded.Data.CMO.tblWorkerHistory>' to 'bool'
I also had && there vs WHERE but I was getting a similar error. I don't NEED anything from tblWorkerHistories except the EndDate stuff.
There is a Many To Many relationship between the 2 tables with EnrollmentID being a FK on both.
public static DataTable GetCurrentWorkersByEnrollmentID(int enrollmentID)
{
using (var context = CmoDataContext.Create())
{
context.Log = Console.Out;
var currentWorkers = from enrollment in context.tblCMOEnrollments
where enrollment.EnrollmentID == enrollmentID
where enrollment.tblWorkerHistories.Where(a => a.EndDate == null || a.EndDate > DateTime.Now)
select
new
{
enrollment.CMONurseID,
enrollment.CMOSocialWorkerID,
SupportWorkerName = enrollment.tblSupportWorker.FirstName + " " + enrollment.tblSupportWorker.LastName,
SupportWorkerPhone = enrollment.tblSupportWorker.Phone
};
return currentWorkers.CopyLinqToDataTable();
}
}
This is the problem:
where enrollment.tblWorkerHistories.Where(/* stuff */)
Where returns a sequence... whereas you need something that will return a Boolean value. What are you trying to do with that embedded Where clause?
As Marc says, it could be that you just need an Any call instead of Where... but if you could explain what you're trying to do, that would make it a lot easier to help you. Note that Any does return a Boolean value, instead of a sequence.
EDIT: Okay, so in SQL you'd use a join, but you don't need an explicit join here because LINQ is implicitly doing that for you, right? If you're trying to find enrollments where any of the histories match the date, and you don't care about the histories themselves, then Any is indeed what you want:
var currentWorkers = from enrollment in context.tblCMOEnrollments
where enrollment.EnrollmentID == enrollmentID
where enrollment.tblWorkerHistories.Any
(a => a.EndDate == null || a.EndDate > DateTime.Now)
select ...
I suspect you mean .Any instead of .Where in the sub-query; the outermost .Where (i.e. the second where) expects a predicate expression, but yours is currently a selector - try:
where enrollment.tblWorkerHistories.Any(
a => a.EndDate == null || a.EndDate > DateTime.Now)

Why does this LINQ-to-SQL query get a NotSupportedException?

The following LINQ statement:
public override List<Item> SearchListWithSearchPhrase(string searchPhrase)
{
List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase);
using (var db = Datasource.GetContext())
{
return (from t in db.Tasks
where searchTerms.All(term =>
t.Title.ToUpper().Contains(term.ToUpper()) &&
t.Description.ToUpper().Contains(term.ToUpper()))
select t).Cast<Item>().ToList();
}
}
gives me this error:
System.NotSupportedException: Local
sequence cannot be used in LINQ to SQL
implementation of query operators
except the Contains() operator.
Looking around it seems my only option is to get all my items first into a generic List, then do a LINQ query on that.
Or is there a clever way to rephrase the above LINQ-to-SQL statement to avoid the error?
ANSWER:
Thanks Randy, your idea helped me to build the following solution. It is not elegant but it solves the problem and since this will be code generated, I can handle up to e.g. 20 search terms without any extra work:
public override List<Item> SearchListWithSearchPhrase(string searchPhrase)
{
List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase);
using (var db = Datasource.GetContext())
{
switch (searchTerms.Count())
{
case 1:
return (db.Tasks
.Where(t =>
t.Title.Contains(searchTerms[0])
|| t.Description.Contains(searchTerms[0])
)
.Select(t => t)).Cast<Item>().ToList();
case 2:
return (db.Tasks
.Where(t =>
(t.Title.Contains(searchTerms[0])
|| t.Description.Contains(searchTerms[0]))
&&
(t.Title.Contains(searchTerms[1])
|| t.Description.Contains(searchTerms[1]))
)
.Select(t => t)).Cast<Item>().ToList();
case 3:
return (db.Tasks
.Where(t =>
(t.Title.Contains(searchTerms[0])
|| t.Description.Contains(searchTerms[0]))
&&
(t.Title.Contains(searchTerms[1])
|| t.Description.Contains(searchTerms[1]))
&&
(t.Title.Contains(searchTerms[2])
|| t.Description.Contains(searchTerms[2]))
)
.Select(t => t)).Cast<Item>().ToList();
default:
return null;
}
}
}
Ed, I've run into a similiar situation. The code is below. The important line of code is where I set the memberList variable. See if this fits your situation. Sorry if the formatting didn't come out to well.
Randy
// Get all the members that have an ActiveDirectorySecurityId matching one in the list.
IEnumerable<Member> members = database.Members
.Where(member => activeDirectoryIds.Contains(member.ActiveDirectorySecurityId))
.Select(member => member);
// This is necessary to avoid getting a "Queries with local collections are not supported"
//error in the next query.
memberList = members.ToList<Member>();
// Now get all the roles associated with the members retrieved in the first step.
IEnumerable<Role> roles = from i in database.MemberRoles
where memberList.Contains(i.Member)
select i.Role;
Since you cannot join local sequence with linq table, the only way to translate the above query into SQL woluld be to create WHERE clause with as many LIKE conditions as there are elements in searchTerms list (concatenated with AND operators). Apparently linq doesn't do that automatically and throws an expception instead.
But it can be done manually by iterating through the sequence:
public override List<Item> SearchListWithSearchPhrase(string searchPhrase)
{
List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase);
using (var db = Datasource.GetContext())
{
IQueryable<Task> taskQuery = db.Tasks.AsQueryable();
foreach(var term in searchTerms)
{
taskQuery = taskQuery.Where(t=>t.Title.ToUpper().Contains(term.ToUpper()) && t.Description.ToUpper().Contains(term.ToUpper()))
}
return taskQuery.ToList();
}
}
Mind that the query is still executed by DBMS as a SQL statement. The only drawback is that searchTerms list shouldn't be to long - otherwise the produced SQL statement won'tbe efficient.

Categories

Resources