With Entity Framework 4.3 and Linq, I want to match a search string against certain properties of contained objects and also on the properties of child objects. This turns out to be a rather complex query though, and I'm not sure how to do it. For instance, one property is an integer and I'm not allowed to call .ToString() in a Linq query.
In order to give you an idea of what I'm trying to do, consider this example code:
var dbVersions = from ver in db.Versions
where ver.Name.Contains(search) ||
ver.Children.Any(c=>c.Id.ToString().Contains(search))
select ver;
How should I implement this search? Perhaps via a stored procedure?
The database server is SQL Server 2012.
If you want to use LINQ the framework internally will do the proper optimizations and from my experience the results are quite OK.
If you don't want to use an stored procedure and stick to LINQ keeping it all in your class code use:
SqlFunctions.StringConvert((double)c.Id)
for converting your int to an string. Note that there is no overload for int, so you need to cast it to double or decimal.
In your situation I would suggest using a stored procedure.
If you end up passing one search term which will be used against multiple columns, then your better off writing a stored proc. I once tried something similar to what you're doing, and the end result was really messy LINQ statements that left me feeling dirty :)
Good reference:
x.ToString() is not supported by the entity framework!
Here's an exmaple of how to use stored procs with EF:
http://blogs.msdn.com/b/bindeshv/archive/2008/11/20/using-stored-procedures-in-entity-framework.aspx
Remember to add the stored proc when you "Update Model from database".
Related
I need to know whether I can get a clue or not.
I am trying to build an Expression Tree that is evaluated via Entity Framework 6 (LINQ to Entities) via ODP.NET managed driver (current Version) to the appropriate Oracle statement in order to perform filtering. This works perfectly for string <> nvarchar2 datatypes. The output for a Contains search is something like:
Select *
[...]
FROM "MYSCHEMA"."MYTABLE" "Extent1"
WHERE ("Extent1"."ANVARCHAR2COLUM" LIKE '%givenStrValueToFind%')
Now I want to get the same result for a DateTime <> Timestamp(6) since this line of sql statement is valid for an oracle query and returns all Dates which contains '08':
select * from "MYSCHEMA"."MYTABLE" where ATIMESTAMP6COLUMN like '%08%';
Since I am new to Expression Trees, I first need to know (after googling alot and tried this an that), whether this is possible before I dig deeper into. And if so, how could this be accomplished best? Since there is no Contains Method defined for DateTime and DateTime? Maybe providing Extension methods? And I dont want to execude queries directly against database.
Any hints would be nice to 'unhook' the given database datatype perhaps...
Thx.
For EF 6 you can use :
ctx.MYTABLE.Where(x=>x.ATIMESTAMP6COLUMN.ToString().Contains("08")).ToList();
which translates to
SELECT
[Extent1].[some property name] AS [some property name],
[Extent1].[ATIMESTAMP6COLUMN] AS [ATIMESTAMP6COLUMN],
.
.
.
FROM [dbo].[MYTABLE] AS [Extent1]
WHERE CAST( [Extent1].[ATIMESTAMP6COLUMN] AS nvarchar(max)) LIKE N'%08%'
As far as what I know all versions of EF does not support string functions but EF 6.x supports this kind of functions(The EF that I tested with is EF 6.1.3, the test is done with sql server localdb as DBMS).
I don't know if I understood. Anyway, you don't need to build an expression tree to avoid DB load in memory. LINQ build an expression tree from an expression, EF translates the expression tree to a SQL statement (using the EF Provider) then the query is run on the DB.
In your case the LINQ Query should be something like this
myContext.MYTABLE.Where(e => e.ATIMESTAMP6COLUMN.Contains("08").ToList();
EDIT
Conversions:
You can use .ToString(). It should be a canonical function (function available on every EF Provider).
Other DateTime related functions:
You could have a look to other canonical functions (there are some about dates that allow to retrieve date parts) and also to non canonical functions (functions implemented only in EF Provider for oracle).
References:
Canonical functions https://msdn.microsoft.com/en-us/library/vstudio/bb738626(v=vs.100).aspx
Actually I can't find non canonical functions for Oracle.
If you need it the best thing is that you ask another question.
I have an app using Entity Framework. I want to add a tree view listing products, grouped by their categories. I have an old SQL query that will grab all of the products and categories and arrange them into parent nodes and children. I am trying to translate it into LINQ that uses the EF. But the SQL has a WITH sub-query that I am not familiar with using. I have tried using Linqer and LinqPad to sort it out, but they choke on the WITH clause and I am not sure how to fix it. Is this sort of thing possible in LINQ?
Here is the query:
declare #id int
set #id=0
WITH ChildIDs(id,parentid,type,ChildLevel) AS
(
SELECT id,parentid,type,0 AS ChildLevel
FROM dbo.brooks_product
WHERE id = #id
UNION ALL
SELECT e.id,e.parentid,e.type,ChildLevel + 1
FROM dbo.brooks_product AS e
INNER JOIN ChildIDs AS d
ON e.parentid = d.id
WHERE showitem='yes' AND tribflag=1
)
SELECT ID,parentid,type,ChildLevel
FROM ChildIDs
WHERE type in('product','productchild','productgroup','menu')
ORDER BY ChildLevel, type
OPTION (MAXRECURSION 10);
When I run the query, I get data that looks like this (a few thousand rows, truncated here):
ID.....parentid.....type.....ChildLevel
35429..0............menu.....1
49205..0............menu.....1
49206..49205........menu.....2
169999.49206........product..3
160531.169999.......productchild..4
and so on.
The WITH block is a Common Table Expression, and in this case is used to create a recursive query.
This will be VERY difficult in Linq as Linq doesn't play well with recursion. If you need all of the data on one result set that a Stored Procedure would be easier. Another option is to do the recursion in C# (not in Linq but a recursive function) and do multiple round-trips. The performance will not be as good but if you result set is small it may not make much difference (and you will get a better object model).
You may be able to solve this using LINQ to Entities, but it is non-trivial and I suspect it will be very time consuming.
In situations like this, you may prefer to build a SQL View or Table-Valued Function that returns the results for which you're looking. Then import that View or Table-Valued Function into your EF model and you can pull data directly from it using LINQ.
Querying the View in LINQ is no different than querying a table.
To get data from a Table-Valued Function in LINQ, you pass the function's parameters in after the name of the function, like so:
var query = from tvf in _db.MyTableValuedFunction(parameters)
select tvf;
EDIT
As suggested by #thepirat000, Table-Valued Function support is not available in Entity Framework versions prior to version 5. In order to use this functionality, EF must be running with .NET 4.5 or higher.
At the end of the day, I could not get this to work. I ended up writing out a SQL query dynamically and sending that straight to the database. It works fine, and I am not relying on any direct user input so there is no chance of SQL injection. But it seems so old school! For the rest of my program I am using EF and LINQ.
Thanks for the replies!
Suppose I have a collection (of arbitrary size) of IQueryable<MyEntity> (all for the same MyEntity type). Each individual query has successfully been dynamically built to encapsulate various pieces of business logic into a form that can be evaluated in a single database trip. Is there any way I can now have all these IQueryables executed in a single round-trip to the database?
For example (simplified; my actual queries are more complex!), if I had
ObjectContext context = ...;
var myQueries = new[] {
context.Widgets.Where(w => w.Price > 500),
context.Widgets.Where(w => w.Colour == 5),
context.Widgets.Where(w => w.Supplier.Name.StartsWith("Foo"))
};
I would like to have EF perform the translation of each query (which it can do indivudually), then in one database visit, execute
SELECT * FROM Widget WHERE Price > 500
SELECT * FROM Widget WHERE Colour = 5
SELECT W.* FROM Widget
INNER JOIN SUpplier ON Widget.SupplierId = Supplier.Id
WHERE Supplier.Name LIKE 'Foo%'
then convert each result set into an IEnumerable<Widget>, updating the ObjectContext in the usual way.
I've seen various posts about dealing with multiple result sets from a stored procedure, but this is slightly different (not least because I don't know at compile time how many results sets there are going to be). Is there an easy way, or do I have to use something along the lines of Does the Entity Framework support the ability to have a single stored procedure that returns multiple result sets??
No. EF deosn't have query batching (future queries). One queryable is one database roundtrip. As a workaround you can try to play with it and for example use:
string sql = ((ObjectQuery<Widget>)context.Widgets.Where(...)).ToTraceString();
to get SQL of the query and build your own custom command from all SQLs to be executed. After that you can use similar approach as with stored procedures to translate results.
Unless you really need to have each query executed separately you can also union them to single query:
context.Widgets.Where(...).Union(context.Widgets.Where(...));
This will result in UNION. If you need just UNION ALL you can use Concat method instead.
It might be late answer, hopefully it would help some one else with the same issue.
There is Entity Framework Extended Library on NuGet which provides the future queries feature (among others). I played a bit with it and it looks promising.
You can find more information here.
I'm looking for best way to write a query with LINQ or an expression tree to return a dynamic result according to dynamic input. For example, consider this pseudocode:
CREATE PROCEDURE Test
#Input NVARCHAR(50)
AS
BEGIN
DECLARE #Query NVARCHAR(100);
SET #Query=N'SELECT ' + #Input + ' FROM MyTable'
EXEC #Query
END
What is best way to:
Code this using LINQ or an expression tree
Call this as a stored procedure using LINQ to SQL
EDIT 1)
Consider every dynamic Query does not include SELECT statement.for example I recently write a dynamic PIVOTquery.So I can't use Dynamic LINQ
Dynamic Linq? Probably not
I was going to suggest Dynamic Linq but as your "Edit" states that won't do for you.
In that case I'd say the answer to your question is "There is no best way".
Is Linq the best choice for this?
Your question is asking for a Linq, specifically a Linq to Sql, implementation of how to achieve your dynamic queries.
But consider what Linq to Sql is for. The main benefits that jump to mind of Linq to SQL are:
Strongly typed queries (catch errors at compile time)
Strongly typed results. Meaning nice pre-written classes (in Linq to SQL's case, pre generated)
Now, if you're doing "Select" statements and always returning an actual mapped table, then there's no reason you can't use the dynamic Linq library.
But if you're returning arbitrary non mapped results, there's no point in using LINQ, indeed it won't let you.
DataContext.ExecuteQuery<T>(string query)
The DataContext class does have an ExecuteQuery<T> method, but you unfortunately you cannot return specify dynamic as the T :-(
Linq to SQL and stored procedures
Lastly, LINQ to SQL can map to stored procedures. So you could have a stored procedure that take a query as an argument, like your example. However, I believe it's got the same limitations of ExecuteQuery in terms of the types in can result, though don't hold me to that.
I'd have a look at that as your final option, and explore the sproc in the Linq to SQL designer returning either anonymous, dynamic, or a mapped type.
So, in my last post I was asking how to build a dynamic search filter using LINQ and EF4 (See Here) and finally came up with the solution of building the expression as a string and parse it to an expression using the Dynamic LINQ library.
I that solved the problem. I was able to generate a Expression<Func<TSource, out bool>> and pass it to the Where() method of the DbSet. I am also trying to do this using MySql as a database behind EF4.
The problem came when I tried to apply string operations to integers, like searching a database record which consecutive number starts with 1234.
My initial expression was something like: record.ConsecutiveNumber.ToString().StartsWith("1234"). Sadly, as expected, things were not that easy as EF4 fails to query the DbSet with exception:
"LINQ to Entities does not recognize
the method 'System.String ToString()'
method, and this method cannot be
translated into a store expression."
After some Google search I found that this is a common problem. But C'MON! Is there a way to perform a search function that can search records with a consecutive number starting by "1234"?
How pros implement search features with EF4? This is with a single property filter. What if I wanna add multiple filters? God, my head hurts... :/
Thanks!
EDIT:
Thought #1: What about a stored procedure? What about calling a MySql stored procedure from Linq? Am I aiming way too high?
You can use the SqlFunctions.StringConvert method. It requires a double (or decimal) so you'll have to cast your int ConsecutiveNumber.
Replace:
record.ConsecutiveNumber.ToString().StartsWith("1234")
With:
SqlFunctions.StringConvert((double)record.ConsecutiveNumber).StartsWith("1234")
Have you looked at the Dynamic LinQ Library:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
And for your question
How to use "contains" or "like" in a dynamic linq query?
Previously I have gotten the code for this lib and just taken a look inside, it is pretty easy to follow.
This would be my thought process on getting it to work. Hopefully it points you in the right direction.
According to other posts SqlFunctions.StringConvert((double)record.ConsecutiveNumber) works for Sql Server.
Problem with converting int to string in Linq to entities
And here is relevant information on linq conversions.
Linq int to string
And here is an answer hinting at writing your own sql function for stringconvert
Using a SQL Function in Entity Framework Select
If SqlFunctions.StringConvert doesn't work for you I'd suggest looking at figuring out how to do it in Sql and then writing your own [EdmFunction()] attribute based method.
I haven't got a clue if this will work over Linq to EF or not but presuming that they mapped the Math operations, this might solve your need:
record.ConsecutiveNumber / Math.Pow(10, Math.Truncate(Math.Log10(record.ConsecutiveNumber) - 3)) == 1234
This is basically dividing the number by a power of 10 just big enough to leave the first 4 digits.
I know this is very hacky and inefficient even if it works, but there you go. :)
Any method calls in a LINQ to Entities query that are not explicitly mapped to a canonical function will result in a runtime NotSupportedException exception being thrown.
Check mapping canonical function here:
http://msdn.microsoft.com/en-us/library/bb738681.aspx
In this case, you can use Math function. (I don't think code first can use in product project at that time)