How to share one LINQ query between two methods? - c#

is there any way to share one LINQ query between two methods? I have quite long LINQ query that gets search results from database and I need to use this query to get results (some kind of list<>) -first method - and to get its count (int) - second method -. I don't want to copy this query in two separate methods and I can't return custom class object containing search results and records count (returned by this query). So what I want to do is to get LINQ query definition(or something like this?) but no the results set that I can use in other methods. Maybe there is another good way to do that. thanks for yout help ;)
the code look like this:
public ??? GetSearchResultsQuery(SearchRequest search_request)
{
var queryGetSearchResults = ....
return queryGetSearchResults;
}
public int GetSearchResultsCount(SearchRequest search_request)
{
return GetSearchResultsQuery(search_request).Count();
}
public List<SearchResults> GetSearchResults(SearchRequest search_request)
{
return GetSearchResultsQuery(search_request).Skip(search_request.startRowIndex).Take(search_request.maximumRows).ToList();
}

public IQueryable<SearchedForType> GetSearchResultsQuery(SearchRequest search_request)
{
var queryGetSearchResults = context.SearchedForTypes.Where(x => x == search_request.X);
... build up your search query ...
return queryGetSearchResults;
}

Related

Using variables to build a LinQ query?

I don't think is possible but wanted to ask to make sure. I am currently debugging some software someone else wrote and its a bit unfinished.
One part of the software is a search function which searches by different fields in the database and the person who wrote the software wrote a great big case statement with 21 cases in it 1 for each field the user may want to search by.
Is it possible to reduce this down using a case statement within the Linq or a variable I can set with a case statement before the Linq statement?
Example of 1 of the Linq queries: (Only the Where is changing in each query)
var list = (from data in dc.MemberDetails
where data.JoinDate.ToString() == searchField
select new
{
data.MemberID,
data.FirstName,
data.Surname,
data.Street,
data.City,
data.County,
data.Postcode,
data.MembershipCategory,
data.Paid,
data.ToPay
}
).ToList();
Update / Edit:
This is what comes before the case statement:
string searchField = txt1stSearchTerm.Text;
string searchColumn = cmbFirstColumn.Text;
switch (cmbFirstColumn.SelectedIndex + 1)
{
The cases are then done by the index of the combo box which holds the list of field names.
Given that where takes a predicate, you can pass any method or function which takes MemberDetail as a parameter and returns a boolean, then migrate the switch statement inside.
private bool IsMatch(MemberDetail detail)
{
// The comparison goes here.
}
var list = (from data in dc.MemberDetails
where data => this.IsMatch(data)
select new
{
data.MemberID,
data.FirstName,
data.Surname,
data.Street,
data.City,
data.County,
data.Postcode,
data.MembershipCategory,
data.Paid,
data.ToPay
}
).ToList();
Note that:
You may look for a more object-oriented way to do the comparison, rather than using a huge switch block.
An anonymous type with ten properties that you use in your select is kinda weird. Can't you return an instance of MemberDetail? Or an instance of its base class?
How are the different where statements handled, are they mutually excluside or do they all limit the query somehow?
Here is how you can have one or more filters for a same query and materialized after all filters have been applied.
var query = (from data in dc.MemberDetails
select ....);
if (!String.IsNullOrEmpty(searchField))
query = query.Where(pr => pr.JoinDate.ToString() == searchField);
if (!String.IsNullOrEmpty(otherField))
query = query.Where(....);
return query.ToList();

LINQ to SQL where clause verifying string contains list element

I'm using a view returning Domains according to an id. The Domains column can be 'Geography' or can be stuffed domains 'Geography,History'. (In any way, the data returned is a VARCHAR)
In my C# code, I have a list containing main domains:
private static List<string> _mainDomains = new List<string>()
{
"Geography",
"Mathematics",
"English"
};
I want to filter my LINQ query in order to return only data related to one or many main Domain:
expression = i => _mainDomains.Any(s => i.Domains.Contains(s));
var results = (from v_lq in context.my_view
select v_lq).Where(expression)
The problem is I can't use the Any key word, nor the Exists keyword, since they aren't available in SQL. I've seen many solutions using the Contains keyword, but it doesn't fit to my problem.
What should I do?
You can use contains:
where i.Domains.Any(s => _mainDomains.Contains<string>(s.xxx))
Notice that the generic arguments are required (even if Resharper might tell you they are not). They are required to select Enumerable.Contains, not List.Contains. The latter one is not translatable (which I consider an error in the L2S product design).
(I might not have gotten the query exactly right for your data model. Please just adapt it to your needs).
I figured it out. Since I can't use the Any keyword, I used this function:
public static bool ContainsAny(this string databaseString, List<string> stringList)
{
if (databaseString == null)
{
return false;
}
foreach (string s in stringList)
{
if (databaseString.Contains(s))
{
return true;
}
}
return false;
}
So then I can use this expression in my Where clause:
expression = i => i.Domains.ContainsAny(_mainDomains);
Update:
According to usr, the query would return all the values and execute the where clause server side. A better solution would be to use a different approach (and not use stuffed/comma-separated values)

LINQ translates when not part of a property

I have a query like this:
var q = db.GetTable<Person>().Where(x => x.Employer.CEO != null);
This made up query will return the ID of the CEO of the company a given person works for. This works find and dandy, but if I do something like this, it get the failed to translate to SQL error:
public class Person
{
public bool HasCEO
{
get
{
return this.Employer.CEO != null;
}
}
}
I want to be able to do this and wrap the longer expression within a property so that I don't have to repeat a nested table get:
var q = db.GetTable<Person>().Where(x => x.HasCEO);
How do I create LINQ properties to achieve my desired result?
I'm using C# 4.0 if that matters.
You can't. LINQ to SQL works by examining the expression tree of the query, and does not delve into the implementation of properties to determine whether a row meets your criteria.
What you can do is create a view in SQL server that dynamically calculates this property, and query that instead of the table.

SQL "not in" syntax for Entity Framework 4.1

I have a simple issue with Entity Framework syntax for the "not in" SQL equivalent. Essentially, I want to convert the following SQL syntax into Entity Framework syntax:
select ID
from dbo.List
where ID not in (list of IDs)
Here is a method that I use for looking up a single record:
public static List GetLists(int id)
{
using (dbInstance db = new dbInstance())
{
return db.Lists.Where(m => m.ID == id);
}
}
Here is a pseudo-method that I want to use for this:
public static List<List> GetLists(List<int> listIDs)
{
using (dbInstance db = new dbInstance())
{
return db.Lists.Where(**** What Goes Here ****).ToList();
}
}
Can anyone give me pointers as to what goes in the Where clause area? I read some forums about this and saw mention of using .Contains() or .Any(), but none of the examples were a close enough fit.
Give this a go...
public static List<List> GetLists(List<int> listIDs)
{
using (dbInstance db = new dbInstance())
{
// Use this one to return List where IS NOT IN the provided listIDs
return db.Lists.Where(x => !listIDs.Contains(x.ID)).ToList();
// Or use this one to return List where IS IN the provided listIDs
return db.Lists.Where(x => listIDs.Contains(x.ID)).ToList();
}
}
These will turn into approximately the following database queries:
SELECT [Extent1].*
FROM [dbo].[List] AS [Extent1]
WHERE NOT ([Extent1].[ID] IN (<your,list,of,ids>))
or
SELECT [Extent1].*
FROM [dbo].[List] AS [Extent1]
WHERE [Extent1].[ID] IN (<your,list,of,ids>)
respectively.
This one requires you to think backwards a little bit. Instead of asking if the value is not in some list of ids, you have to ask of some list of id's does not contain the value. Like this
int[] list = new int[] {1,2,3}
Result = (from x in dbo.List where list.Contains(x.id) == false select x);
Try this for starters ...
m => !listIDs.Contains(m.ID)
This might be a way to do what you want:
// From the method you provided, with changes...
public static List GetLists(int[] ids) // Could be List<int> or other =)
{
using (dbInstance db = new dbInstance())
{
return db.Lists.Where(m => !ids.Contains(m.ID));
}
}
However I've found that doing so might raise error on some scenarios, specially when the list is too big and connection is somewhat slow.
Remember to check everything else BEFORE so this filter might have less values to check.
Also remember that Linq does not populate the variable when you build your filter/query (at least not by default). If you're going to iterate for each record, remember to call a ToList() or ToArray() method before, unless each record has 500MB or more...

Db4o StartsWith and ignore case

The following query takes a while to return:
db.Query<Person>(x => x.StartsWith("Chr", StringComparison.CurrentCultureIgnoreCase))
is there a way to get this working correctly? ie faster?
Maybe you ran into a limitation of db4o’s query-optimization. Normally Native Queries and LINQ-Queries are translated into a low level SODA-query. When this optimization fails, db4o instantiates the objects in the database in order to execute the query. As you can imagine this can be quite slow.
The best current solution is to use a SODA directly for this case. For example a class with one property:
public class SimpleObject
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
The native query like this:
var result = db.Query<SimpleObject>(x => x.Name.StartsWith ("Chr",StringComparison.CurrentCultureIgnoreCase));
Can be represented by this SODA-Query:
IQuery query = db.Query();
query.Constrain(typeof (SimpleObject)); // restrict to a certain class
query.Descend("name").Constrain("Chr").StartsWith(false); // the field 'name' starts with 'chr', case-insensitive
foreach (var s in query.Execute())
{
//
}
I hope future versions of the Query-Optimizer support this case directly.
adding and index on the column you're comparing would probably help.
http://developer.db4o.com/Documentation/Reference/db4o-7.4/net35/tutorial/docs/Indexes.html#outline219

Categories