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...
Related
I have a function that creates a query object for me based on a number of conditions:
compileSearchCriteria(search){
var conditions = {
published: true
};
conditions["publishedDate"] = {$lte: new Date(search.searchDate)};
<more code here>
return conditions;
},
Dates are stored as ISODates in MongoDB. The resulting query looks like this:
{"published":true,"publishedDate":{"$lte":"2016-03-27T16:23:12.000Z"}}
and returns nothing.
db.trips.find({"published":true,"publishedDate":{"$lt":ISODate("2016-03-27T15:48:41.866Z")}})
returns desired results. Sending the query direct works as well:
DealsCollection.find({published:true, publishedDate:{$lt: new Date(this.props.searchDate)}}).fetch()
How can I create the query object so that I get proper results?
Update
It should work and it does work. The problem seems to be with mini-mongo implementation in Meteor. I separated the calls to see where the disconnect is. The server returned data using the original query. But querying resulting documents from the mini-mongo collection produced no results, even though I can see returned documents in it. That is a different issue. Thank you for pointers.
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.
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'm working my way through the MVC Storefront code and trying to follow the path of a repository, a service and a model that is a poco, outside of the dbml/data context. It's pretty easy to follow actually, until I started writing tests and things failed in a way I just don't understand.
In my case, the primary key is a uniqueidentifier instead of an int field. The repository returns an IQueryable:
public IQueryable<Restaurant> All()
{
return from r in _context.Restaurants select new Restaurant(r.Id)
{
Name = r.Name
};
}
In this case, Restaurant is a Models.Restaurant of course, and not _context.Restaurants.Restaurant.
Filtering in the service class (or in repository unit tests) against All(), this works just as expected:
var results = Repository.All().Where(r => r.Name == "BW<3").ToList();
This works just fine, has one Model.Restaurant. Now, if I try the same things with the pkid:
var results = Repository.All().Where(r => r.Id == new Guid("088ec7f4-63e8-4e3a-902f-fc6240df0a4b")).ToList();
If fails with:
The member 'BurningPlate.Models.Restaurant.Id' has no supported translation to SQL.
If seen some similiar posts where people say it's because r => r.Id is [Model.Restaurants] is a class level the linq2sql layer isn't aware of. To me, that means the the first version shouldn't work either. Of course, if my pk is an int, it works just fine.
What's really going on here? Lord knows, it's not very intuitive to have one work and one not work. What am I misunderstanding?
I think the problem here is due to using a constructor overload, and expecting the query to fill it in. When you do a projection like this, you have to put all the things you want to be in the projection query in the actual projection itself. Otherwise Linq won't include that in the SQL query.
So, rewrite your bits like so:
return from r in _context.Restaurants select new Restaurant()
{
Id=r.Id,
Name = r.Name
};
This should fix it up.
Not having actually typed this code out, have you tried
var results = Repository.All().Where(r => r.Id.Equals(new Guid("088ec7f4-63e8-4e3a-902f-fc6240df0a4b")).ToList()
Ninja
this probably has to do with fact that you trying to instantiate the guid in the query, and I think that LINQ to SQL is trying to convert that to actual SQL code before the object is created.
Try instantiating before the query and not on the query.
I am a bit of a newbie when it comes to Linq to SQL but I hope you can help out. I've written the following Linq to SQL statement with Extension Methods:
Cedb.ClassEvents.Where(c => c.ClassID == 1).Select(c => c).Single()
Where Cedb is the Datacontext, ClassEvents is a table (for classes and events being held at a facility) and ClassID is a unique integer key.
This query runs fine in LinqPad (without Cedb). When it returns, it says that the return type is "ClassEvent". In Intellisense in Visual studio, it tells me that the return type of this query is ClassEvent (created in my data model). However, when I try to place the results in a variable:
var classEvent = Cedc.ClassEvents.Where(c.ClassID == 1).Select(c => c).Single();
then I get an error: InvalidCastException: Specified cast is not valid. The same thing happens if I use the "ClassEvent" class in place of the var. I'm new to this but this one seems like a true slam dunk rather than a bug. Is there something about the Single method that I don't know that is leading to the error? Any help would be appreciated!
Slace - and any other interested parties. The cause of the "Invalid Cast Exception" error was a change in the underlying data model. A smallint field had been changed to bit. Thus, when the system tried to map the query results onto the "ClassEvent" data structure, the conflict between the model (which had not been updated) and the data table emerged.
Nonetheless, I do appreciate the answer!
You don't need to do both a Select and a Single, in fact, you don't even need the Where, you can get away with (see http://msdn.microsoft.com/en-us/library/bb535118.aspx):
var classEvent = Cedc.ClassEvents.Single(c => c.ClassID == 1);
I'd also recommend against using Single unless you're 100% sure that the Func<T, bool> will always return a value, as if it doesn't return a value you will have an exception thrown. Better is using SingleOrDefault and doing a null check before interaction with the object (http://msdn.microsoft.com/en-us/library/bb549274.aspx)