Query By Entity (Example) - c#

I’m looking for a tool that will dynamically generate Linq to Entity queries from a given entity, a Query By Entity (Example), if you will. Given an entity and the object context it belongs to, the generator returns an ObectQuery or IQueryable that could be further modified or executed. Ideally, the query builder would not directly reference Entity Model, rather it would use the object context to build the query from the model. I imagine the code looking something like this:
QueryBuilder qb = new QueryBuilder(new EntitiesContext());
Customer c = new Customer();
qb.Add(c);
c.FirstName = "Jim";
var qry = qb.BuildQuery();
int total = qry.Count();
The underlying query would look something like this:
var query = from c in ctx.Customers
where c.FirstName == "Jim"
select c;
Does such a thing already exist somewhere? I can imagine coding something like this myself, but I would rather start using something that already exists.

I'm not exactly sure what your goals are, if you would post some more information on your context (an actual problem), but Dynamic Linq might help you constructing linq queries dynamically:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Wiebe

I don't really see the value in a QueryBuilder. If it's as simple as in your sample you could only use it to find exact matches. If you need more complex queries you'd probably be better of using Linq directly.
You know that you can chain your queries? That's probably not exactly what you are looking for but you could do
IQueryable<Customers> source=context.Customers;
if (...)
{
source = from c in source
where c.FirstName.StartsWith("Jim")
select c;
}
if (...)
{
source = from c in source
where contries.Contains(c.Country)
select c;
}
// ...

You can use dynamic Linq to build up your Linq expressions at run time - simply appending methods to an existing Linq query (before you execute it).
Having said that, the notion of a "QueryBuilder" class is, as Chris mentioned, of questionable merit.
Strictly speaking, Linq is itself a query builder, since it takes a Linq statement and constructs a "graph" which a query provider is then able to use to do the underlying work (compiling a SQL statement for Linq-To-SQL, or enumerating objects of Linq-For-Objects).
If you want to offer end-users the ability to build queries dynamically, consider creating a UI that builds the Linq statement by adding query methods like Chris pointed out. Remember that with a Linq query, you can add as many expressions as you like - the query only executes when the enumerator is tripped.
i.e. To select the oldest Customer under 65 named Jim:
Dim qry = From customer in DataContext.Customers
qry = qry.Where( Function (c as Customer) c.Name="Jim" )
qry = qry.Where( Function (c as Customer) c.Age < 65 )
qry = qry.OrderByDescending( Function (c as Customer) c.Age )
qry = qry.Take(1)
Dim oldJim as Customer = qry.FirstOrDefault()
The FirstOrDefault method, like using ToArray or For Each, trips the enumerator and thus processes the query.
Hope this helps!

Related

How to filter and order collection in C#?

Here is the code:
public ActionResult Index()
{
var crView = db.CRCases.ToList();
return View(crView);
}
Would like to filter like we have in sql statement using WHERE and ORDER BY.
Is there any good reference on how to apply it?
linq is your friend here.
you can do
crView = crView.Where(x => x.yourPredicate).OrderBy( y => y.OrderClause);
CRCases should be a DBSet<T>. You can use LinQ like this :
db.CRCases.Where(x => x.TheProperty =xxx).ToList()
You can use LINQ = Language Integrated Query.
This have two different notations:
Functional: you can change several function calls beginning on an IEnumerable.
db.CrCases.Where(predicate).OrderBy(sortExpression)
"SQL like": it really looks like an "unordered SQL".
from crCase in db.CrCases where condition orderby sortExpression select crCase
The condition and sort expression in the first notation require lambda expressions (you can see them in action here). In the second case the condition is expressed.
Most of the queries can be written either way.
You'll usually use var to store the query, and materialize it later.
var query = from crCase in db.CrCases
where condition orderby sortExpression
select crCase
CrCase firstCase = query.Firts();
List<CrCase> cases = query.ToList();
There are several flavors of LINQ. They all look equal on the surface, but are translated into another thing. For example:
LINQ to objects: it's the more general, an allows to make queries on collections. Tis is what you'll use for this particular case.
LINQ to SQL: this allows to write queries in C# which are transalated to SQL queries in the underlying database
LINQ to Entities: similar to the previous, but you make your queries to an Entity Framework model, and the queries are finally transalated to the DB
There are some others flavors of LINQ. And, in fact, you can create your own, but that's not easy.
Finally, you have to take into account that a LINQ query isn't executed until you materialize it. I.e. it's not executed until you try to get data from it (converting it ToList(), ToArray() or whatever, enumerating it, or accesing any of it's data First(), Sum(), Count()). Depending on the flavor, the executing can be running C# code (LINQ to objects) or run a SQL Query in a DB (LINQ to SQL) or whatever.
Here you have a great place to learn how to use LINQ:
LINQ 101

Linq To SQL: Sort Query by Arbitrary Property(Column) Name

I have a larger/more complex problem, but for simplicity sake, let us consider the following:
Let us say that I have table in the SQL DataBase called Product, having two columns, ID (int, primary key) and Name (varchar/string). I also have a simple LINQ DataContext.
I have a query constructed and handed to “my” function. Let us say it is something like: (though it may be a bit more complex)
IQueryable<Product> query = from p in db.Products select p;
Once my method gets this query, passed in as a parameter, it has to change the sort order e.g.
IQueryable<Product> sortedQuery = query.OrderBy(x => x.Name);
I would like to make this more generic i.e. to specify the field to sort on. Normally, I can do a switch statement that takes a string. However I would like to know if there is a way to pass the parameter directly. I intend to extend this to other Database tables, so these switch statements would get tedious.
I was trying something like:
IQueryable<Product> sortedQuery = query.OrderBy(x => (typeof(Product)).GetProperty(“Name”));
But this does not work. I also want to ensure that the LINQ to SQL is maintained i.e. the sort to be done on the SQL Server. Hence if I debug, I should get a SQL query from this LINQ query.
Thank you in advance for your help.
You could use Dynamic Linq for this purpose.
See here Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)
Then you can make calls like this:
var query = DBContext.Users.Where( "Age > 3" ).OrderBy( "Name asc" );
Try this out instead:
query.OrderBy(x => x.GetType().GetProperty(“Name”).GetValue(x, null));
You can't just grab the property. You need to grab the value off of that property, hence the call to GetValue.
It is not as easy as it seems. The LINQ to SQL engine parses the expression you pass to the OrderBy method in order to obtain the name of the property you are referencing, then uses this information to compose a plain SQL order by clause.
I guess that maybe it can be done by using reflection, anyway. Maybe you can get something useful from the accepted answer of this SO question.

LINQ: When to use Compiled Queries?

I'd like some expert advice on this. I've used compiled queries before, but for this particular case, i'm not sure whether it's appropriate.
It's a search form where the query changes and is dependent on what is being searched on.
static Func<DBContext, int, IQueryable<Foo>> Search = CompiledQuery.Compile(
(DBContext db, int ID) =>
db.Person
.Where(w => w.LocationID = ID)
.Select(s =>
new Foo
{
Name = s.PersonName,
Age = s.Age,
Location = s.LocationName,
Kin = s.Kin
}));
Now if someone fills in the search box, i want to extend the query by adding another Where statement to the query:
var query = Search(context, 123);
query = query.Where(w => w.Name.Contains(searchString));
So my question is, is it returning all the results where LocationID == 123, then checking the results for a searchString match? Or is it actually extending the compiled query?
If it's the former (which i suspect it is), should scrap the CompiledQuery and just create a method that extends the query then return it as a list?
Also, what are the best practices for CompiledQuery usage and is there a guideline of when they should be used?
Note: I'm using the above in an ASP.NET website with Linq to SQL. Not sure if that makes any difference.
Thanks
The problem is that the compiled query is set in stone; it knows what SQL it will run against the database. The lambda expression is lazy loaded however, and cannot modify the compile query as it is being run during run time. The bad news is that it will return all of the records from the database, but it will query those records in memory to further refine them.
If you want to compile the query then I would suggest writing two queries with different signatures.
As far as I know, it is good practice to compile your query once, that is the whole point of pre-compiled query(and that's why your pre-compiled query is static), it saves time to compile that query into SQL. If it extend that pre-compiled query, then it is compiling that query again, which you loose gains.
Query result on result (your query variable) is no longer LINQ to SQL.
Just include your additional condition in your compiled query.
DB.Person.Where(w => w.LocationID == ID
& (searchString=="" || w.Name.Contains(searchString)))
If i am right then you need some dynamic where clause in linq. So for that i would suggest go this way
IEnumerable list;
if(condition1)
{
list = Linq Statement;
}
if(condition2)
{
list = from f in list where con1=con && con2=con select f;
}
if(condition3)
{
list = from n in list con1=con && con2=con select f;
}
I hope you got my words.

Why would Entity Framework not be able to use ToString() in a LINQ statement?

This works in LINQ-to-SQL:
var customersTest = from c in db.Customers
select new
{
Id = c.Id,
Addresses = from a in db.Addresses where c.Id.ToString() ==
a.ReferenzId select a
};
foreach (var item in customersTest)
{
Console.WriteLine(item.Id);
}
But a similar example in Entity Framework gets an error message that says basically that it can't "translate it to SQL", here is the original error message in German:
"'LINQ to Entities' erkennt die
Methode 'System.String ToString()'
nicht, und diese Methode kann nicht in
einen Speicherausdruck übersetzt
werden."
Translation:
"'LINQ to Entities' does not recognize
Method 'System.String ToString()',
this method can not be translated into
a memory expression.
Can anyone shed any light on how we could get this kind of statement to work in Entity Framework or explain why it gets this error?
Simply put: LINQ to Entities doesn't know about the conversion from your ID type to a string.
What is the type of c.ID? Is there any reason why it's one type for ID, but another for ReferenzId? If at all possible, make them the same type, at which point you won't have a problem any more. I don't know if there are other ways of performing conversions in LINQ to Entities - there may be - but aligning the types would be cleaner.
By the way, this really looks like it's a join:
var query = from c in db.Customers
join a in db.Addresses on c.Id equals a.ReferenzId into addresses
select new { Id = c.Id, Addresses = addresses };
EDIT: To respond to your comment - ToString appears in IntelliSense because the compiler has no real idea what your query is going to mean or how it will be translated. It's perfectly valid C#, and can generate a valid expression tree - it's just that EF doesn't know how to convert that expression tree into SQL.
You could try using Convert.ToString(c.Id) instead of just calling c.Id.ToString()...
Updated answer:
If you followed the link I gave you at the beginning of my answer, this missing feature has meanwhile received 75 votes and is now (finally!) implemented by Microsoft in EF 6.1. To all who participated: Thank you for voting! Your voice was heard.
For example:
var query = from e in context.Employees where e.EmployeeID.ToString() == "1" select e;
will now be translated to:
DECLARE #p0 NVarChar(1000) = '1'
SELECT [t0].[EmployeeID], [t0].[LastName], [t0].[FirstName], [t0].[Title],
[t0].[TitleOfCourtesy], [t0].[BirthDate], [t0].[HireDate], [t0].[Address],[t0].[City],
[t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[HomePhone], [t0].[Extension],
[t0].[Photo], [t0].[Notes], [t0].[ReportsTo], [t0].[PhotoPath]
FROM [Employees] AS [t0]
WHERE (CONVERT(NVarChar,[t0].[EmployeeID])) = #p0
i.e. e.EmployeeID.ToString() translates to (CONVERT(NVarChar,[t0].[EmployeeID])).
Original answer:
It makes no sense to me why Linq2EF does not translate .ToString() into a proper SQL statement, as Linq2SQL does, only the Microsoft dev team knows the reason why they did not implement it yet. :-(
But you can raise the priority to implement it if you vote for this feature by following this link.
Luckily there are also 2 workarounds available, both of them I used recently in EF queries:
I) What helped me to get around this limitation was to change the query into a list, like so:
var customersList = (from c in db.Customers
select c).ToList(); // converts to IEnumerable<T> ...
var customersTest = (from c in customersList
select new {Id=c.ID.ToString()}); // ... which allows to use .ToString()
The statement .ToList() converts to IEnumerable<T>, where .ToString() is available. Note that, Depending on the requirements, you can use .AsEnumerable() as well, which has the advantage that deferred execution is supported which is better if you have multiple linq queries depending on each other or if you're using different parameter values (many thanks to Divega for this hint!).
Afterwards you can use this query as you wish , e.g.:
var customersTest2 = from c in customersTest
select new
{
Id = c.Id,
Addresses = from a in db.Addresses where c.Id == a.ReferenzId select a
};
Of course if you need you can add more properties to the objects of customersTest as required. You can also optimize the query above, I have only used 3 steps for readability of this example.
II) For simple conversions, and if you have to reuse the generated query in further subqueries (and it needs to remain IQueryable), use SqlFunctions from System.Data.Objects.SqlClient, they will be translated into SQL queries correctly.
Example 1: Date conversion (you have to use dateparts as below shown)
var customersTest = from c in db.Customers
select new {
strDate=SqlFunctions.DateName("dd", c.EndDate)
+"."+SqlFunctions.DateName("mm", c.EndDate)
+"."+SqlFunctions.DateName("yyyy", c.EndDate)
}
Example 2: Numeric to string conversion
var customersTest = from c in db.Customers
select new {
strID=SqlFunctions.StringConvert((double)c.ID)
}
This should help you out of most situations where conversions into strings are required.
Entity Framework 6.1 RTM which was just released now support .ToString()
LINQ to Entities as far as I understand it (for v1) is very primative. In otherwords it doesn't know how to take the extension method "ToString()" and generate the SQL for it.
In LINQ to SQL, it executes the extension method "ToString()" before generating the SQL. The difference is that LINQ to Entities uses IQueryable instead of IEnumerable.
BUT, from what I remember casting should work (because casting is a data type and SQL knows about CAST()).
So
c.Id.ToString() should really be (string)c.Id
(also, make sure it is (string) and not (String)).
One of the downfalls I would say about using Lambda (in Entity Framework) to generate the SQL expression instead of pure LINQ.
Keep in mind too, that using CAST on the left side of the equals sign in SQL is a bit ill performing :-)

Select clause containing non-EF method calls

I'm having trouble building an Entity Framework LINQ query whose select clause contains method calls to non-EF objects.
The code below is part of an app used to transform data from one DBMS into a different schema on another DBMS. In the code below, Role is my custom class unrelated to the DBMS, and the other classes are all generated by Entity Framework from my DB schema:
// set up ObjectContext's for Old and new DB schemas
var New = new NewModel.NewEntities();
var Old = new OldModel.OldEntities();
// cache all Role names and IDs in the new-schema roles table into a dictionary
var newRoles = New.roles.ToDictionary(row => row.rolename, row => row.roleid);
// create a list or Role objects where Name is name in the old DB, while
// ID is the ID corresponding to that name in the new DB
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles.ToList();
But calling ToList gives me this NotSupportedException:
LINQ to Entities does not recognize
the method 'Int32
get_Item(System.String)' method, and
this method cannot be translated into
a store expression
Sounds like LINQ-to-Entities is barfing on my call to pull the value out of the dictionary given the name as a key. I admittedly don't understand enough about EF to know why this is a problem.
I'm using devart's dotConnect for PostgreSQL entity framework provider, although I assume at this point that this is not a DBMS-specific issue.
I know I can make it work by splitting up my query into two queries, like this:
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r;
var roles2 = from r in roles.AsEnumerable()
select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles2.ToList();
But I was wondering if there was a more elegant and/or more efficient way to solve this problem, ideally without splitting it in two queries.
Anyway, my question is two parts:
First, can I transform this LINQ query into something that Entity Framework will accept, ideally without splitting into two pieces?
Second, I'd also love to understand a little about EF so I can understand why EF can't layer my custom .NET code on top of the DB access. My DBMS has no idea how to call a method on a Dictionary class, but why can't EF simply make those Dictionary method calls after it's already pulled data from the DB? Sure, if I wanted to compose multiple EF queries together and put custom .NET code in the middle, I'd expect that to fail, but in this case the .NET code is only at the end, so why is this a problem for EF? I assume the answer is something like "that feature didn't make it into EF 1.0" but I am looking for a bit more explanation about why this is hard enough to justify leaving it out of EF 1.0.
The problem is that in using Linq's delayed execution, you really have to decide where you want the processing and what data you want to traverse the pipe to your client application. In the first instance, Linq resolves the expression and pulls all of the role data as a precursor to
New.roles.ToDictionary(row => row.rolename, row => row.roleid);
At that point, the data moves from the DB into the client and is transformed into your dictionary. So far, so good.
The problem is that your second Linq expression is asking Linq to do the transform on the second DB using the dictionary on the DB to do so. In other words, it is trying to figure out a way to pass the entire dictionary structure to the DB so that it can select the correct ID value as part of the delayed execution of the query. I suspect that it would resolve just fine if you altered the second half to
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r.RoleName;
var list = roles.ToDictionary(roleName => roleName, newRoles[roleName]);
That way, it resolves your select on the DB (selecting just the rolename) as a precursor to processing the ToDictionary call (which it should do on the client as you'd expect). This is essentially exactly what you are doing in your second example because AsEnumerable is pulling the data to the client before using it in the ToList call. You could as easily change it to something like
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r;
var list = roles.AsEnumerable().Select(r => new Role { Name = r.RoleName, ID = newRoles[r.RoleName] });
and it'd work out the same. The call to AsEnumerable() resolves the query, pulling the data to the client for use in the Select that follows it.
Note that I haven't tested this, but as far as I understand Entity Framework, that's my best explanation for what's going on under the hood.
Jacob is totally right.
You can not transform the desired query without splitting it in two parts, because Entity Framework is unable to translate the get_Item call into the SQL query.
The only way is to write the LINQ to Entities query and then write a LINQ to Objects query to its result, just as Jacob advised.
The problem is Entity-Framework-specific one, it does not arise from our implementation of the Entity Framework support.

Categories