I have an IQueryable object which is the result of view (using a data model in Visual Studio).
I want to apply an additional filter on the object, but I don't want it to go back to the database and run the query again with the additional where clause. The query takes several seconds to run so I'd like linq to simply filter the current list rather than regenerating the query.
How do I prevent linq to sql going back to the database when I execute a .where() function?
To answer the title of the question, you can generally use AsEnumerable to make subsequent bits execute locally:
var query = db.People
.Where(p => p.Age > 18) // In database
.AsEnumerable()
.Where(p => p.Name == "Jon") // In .NET
The call to AsEnumerable doesn't do anything other than change the static type from IQueryable<T> to IEnumerable<T> so that the extension methods in Enumerable get called instead of those in Queryable.
However, if you reuse a query that's already executed, it will still execute again in the database... so (to answer the body of the question) if you want to reuse a query's results, you need to convert them into a list first, e.g.
var results = db.People
.Where(p => p.Age > 18)
.ToList();
Then accessing results won't go back to the database (unless it needs to fetch child values, of course). So you can do:
var subquery = results.Where(p => p.Name == "Jon");
That will perform the filtering in process instead.
use ToList() after first execution and use the generated list in the rest of your code.
var products = (from p in Products where p.Name.Contains("k") select p).ToList(); // this will go to database and fill the products list.
var x = products.Where(p => p.ProductionDate > DateTime.Now); // this will run in memory.
Related
I have been struggling with an issue for a couple of days. I am sharing a Content Provider with two different apps (app A and app B). All the stuff regarding DB creation and Content Provider management is done by app A. App B just accesses it using the corresponding Authorities and a Content Provider Client.
ContentProviderClient myCPClient = this.miContext.getContentResolver().acquireContentProviderClient(this.miUri);
The problem comes up when trying to query the database in a more complex way, i.e. using some key words like GROUP BY, HAVING, etc. I need to get unique references according to one specific column (I want to use GROUP BY), and I have found out that there is no rawQuery() method for a ContentProviderClient, but a simplified query() method (compared to the one available in the class SQLiteDatabase, which allows to formulate proper MySQL commands).
I have checked this answer, but since my ContentProvider is accessed from a different app, I do not have any class like MyContentProvider.
To sum up, is there any way to make a proper query (like rawQuery()) to a ContentProvider which was generated by a different app?
I have finally got to a solution which is rather simple and sensible. I got a very good explanation about Content Providers and Content Resolvers. The latter is used to access the former, which means that they can not control what is in the provider, but get data from them. This means that you can not make a Content Provider Client to use a rawQuery() if it is not implemented (override) in the query() method of the corresponding ContentProvider.
To work around my problem, I have used a flag in my provider client and modify my content provider to read it so I can make use of GROUP BY. I just wanted to get unique references from the database according to a particular column.
Here it is the solution, which is not a very clean one, but it works quite well.
For the ContentProviderClient,
ContentProviderClient myCPClient = this.miContext.getContentResolver().acquireContentProviderClient(this.miUri);
//I declare some variables for the query
//'selection' will get all the rows whose "_id" is greater than 0, i.e. all the rows
String selection = BaseDatosParam.Tabla._ID + ">?";
String[] selectionArgs = {"0"};
//'groupBy' is not formatted in any particular way. I just need it to contain the pattern "GROUP BY"
String groupBy = "GROUP BY" + BaseDatosParam.Tabla.REF;
//the last field of the query corresponds to 'sortOrder', but I
Cursor c = myCPClient.query(Uri.parse(miUri.toString()),
projection, selection, selectionArgs, groupBy);
In the ContentProvider,
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{
String where = selection;
String groupBy = null;
SQLiteDatabase db = this.miBDManager.getWritableDatabase();
//We just check out whether 'sortOrder' includes the pattern "GROUP BY", otherwise that field will remain null
Pattern myPat = Pattern.compile("GROUP BY");
Matcher myMat = myPat.matcher(sortOrder);
if (myMat.find())
groupBy = myMat.replaceFirst("");
Cursor c = db.query(BaseDatosParam.Tabla.NOMBRE_TABLA, projection, where, selectionArgs, groupBy, null, null);
c.setNotificationUri(this.getContext().getContentResolver(), uri);
return c;
}
Regards,
I have the following query
#initial_matches = Listing.find_by_sql(["SELECT * FROM listings WHERE industry = ?", current_user.industry])
Is there a way I can run another SQL query on the selection from the above query using a each do? I want to run geokit calculations to eliminate certain listings that are outside of a specified distance...
Your question is slightly confusing. Do you want to use each..do (ruby) to do the filtering. Or do you want to use a sql query. Here is how you can let the ruby process do the filtering
refined list = #initial_matches.map { |listing|
listing.out_of_bounds? ? nil : listing
}.comact
If you wanted to use sql you could simply add additional sql (maybe a sub-select) it into your Listing.find_by_sql call.
If you want to do as you say in your comment.
WHERE location1.distance_from(location2, :units=>:miles)
You are mixing ruby (location1.distance_from(location2, :units=>:miles)) and sql (WHERE X > 50). This is difficult, but not impossible.
However, if you have to do the distance calculation in ruby already, why not do the filtering there as well. So in the spirit of my first example.
listing2 = some_location_to_filter_by
#refined_list = #initial_matches.map { |listing|
listing.distance_from(listing2) > 50 ? nil : listing
}.compact
This will iterate over all listings, keeping only those that are further than 50 from some predetermined listing.
EDIT: If this logic is done in the controller you need to assign to #refined_list instead of refined_list since only controller instance variables (as opposed to local ones) are accessible to the view.
In short, no. This is because after the initial query, you are not left with a relational table or view, you are left with an array of activerecord objects. So any processing to be done after the initial query has to be in the format of ruby and activerecord, not sql.
I have the code below but it give me an error at
.Where(p => Regex.Replace(p.Phone, rgPattern, "") == Regex.Replace(phone.Trim(), rgPattern, "")
string rgPattern = #"[\\\/:\*\?""<>|()-]";
var members = from m in Context.Members select m;
if (!String.IsNullOrEmpty(phone))
members = members.Where(p => Regex.Replace(p.Phone, rgPattern, "") == Regex.Replace(phone.Trim(), rgPattern, ""));
I know the LINQ commnand is not executed until I run:
members.OrderBy(orderBy).Skip(startRow).Take(maxRows).ToList();
Any idea in how to clean the value before comparing?
This is probably a problem with your data architecture. This is why we keep the data clean in the tables all of the time -- so you don't have to do evil things like this.
You have a couple of options here:
Clean up your actual data and add protections on the data layer to keep your phone numbers pure.
Create a view or computed column in your data which cleans up the phone number on the data layer, then map to that clean phone number and query off of that column instead.
Call ToList() on a subset of your rows before running your regex-based query and then use Linq to Objects to run the regex against those rows. This may be prohibitive if you can't narrow down your rows enough before using Linq to Objects.
I am trying to return a list of results, where the joined table has multiple values, but I only want one image value for each listing value.
The SQL is like this:
SELECT
Title, Comments, ThumbNailPath, thumbheight, thumbwidth
FROM
Listings as l INNER JOIN
Images as i ON l.id = i.ListingId
WHERE
(i.ImageSlot = 1)
My repository class looks something like this: (db.GetListings() is the stored procedure method I added to the methods section of the .dbml file)
private ListingModelDataContext db = new ListingModelDataContext();
public IQueryable FindAllListings()
{
//return all listings and first associated thumbnail
return db.GetListings();
}
When I try calling from stored procedure I get error
'System.Data.Linq.ISingleResult' to 'System.Linq.IQueryable'. An
explicit conversion exists (are you
missing a cast?)
I am looking for guidance on how I might either call that sql statement from a stored procedure or simply structure the linq to return that value.
My preference is to know how to write the LINQ statement. To clarify, I have a one to many relationship on the images table. (multiple images per listing) I only want to return the first image though on a search results page. So, for each listing return image value where imageSlot = 1.
I should get a list of rows showing the listing values joined to the image values. I get the correct results in SQL but not sure how to write it in linq or how to call the sproc correctly.
Thanks in advance and sorry if this is hard to read.
Are you trying to do soemthing like :
var result = (stored proc).Single()
or
var result = (stored proc).SingleOrDefault()
?? This would break if there are indeed multiple elements coming back - instead use the .First() or .FirstOrDefault() methods:
var result = (stored proc).FirstOfDefault();
returns the first entry from the stored proc, or null if the stored proc returns no values at all.
Marc
I am working on a database access layer project and decided to use Linq to SQL for it. One of the things I wanted to do was provide an API that can take Linq Expressions as arguments to retrieve data. For instance, I wrote this API now that looks like this:
public Result GetResults(System.Linq.Expressions.Expression<Func<Result, bool>> predicate)
{
Result result = db.Results.SingleOrDefault(predicate);
return result;
}
You can then use this API to query the database for a Result row that satisfies certain conditions, for example:
Result result = Provider.GetResults(r => r.ID == 11);
This works very well. I am able to get the one row I want based on my conditions.
Next step was taking this to be able to get multiple objects back from the database.
The way I got it to work was like this:
public List<Result> GetResults(System.Linq.Expressions.Expression<Func<Result, bool>> predicate)
{
List<Result> results = db.Results.Select(r => r).Where(r => r.ID == 11).ToList<Result>();
return results;
}
As you can see, I call a Select with r => r, this gives me back everything and then I use a Where to filter to what I need.
It works... but something tells me that I am doing it really ugly. I could be wrong, but doesn't this pull EVERYTHING out of the Results table then filters it? or does it put together the correct SQL statement that does filter at the database level?
Anyway... I would highly appreciate some guidance on how I can accomplish this task. How do I write an API that takes a Linq Expression as an argument and returns a set of objects from the database based on that expression.
Thanks!
The Select(r=>r) does nothing (except change from Table<T> to IQueryable<T> - but nothing useful). And I assume you intended to pass predicate to the Where?
Indeed, this doesn't pull everything out and filter it - the appropriate WHERE (TSQL) clause is generated. This is possible because of "deferred execution" and "composability" - meaning: it doesn't actually execute anything until you start iterating the data (in the ToList()) - until then you are simply shaping the query.
You can see this by doing somthing like:
db.Log = Console.Out;
and look at the TSQL. Or run a TSQL trace. To make it prettier, simplify it to:
return db.Results.Where(predicate).ToList();
I know you said you wanted to pass a predicate and return a List but have you considered returning an IQueryable
Then you could call:
GetResults().Where(r => r.SomeProperty == "SomeValue")
.OrderBy(r => r.SomeOtherProperty)
.Skip(10)
.Take(10); //etc.
With your current API design you would return all records and then have to get 10, where as the above would return only the 10 you needed...
Just some thoughts...