Calling a UDF with Entity SQL but without a FROM clause? - c#

Am trying to call a UDF which just takes a parameter and returns a scalar. The examples I've seen all use a From clause: http://blogs.microsoft.co.il/blogs/gilf/archive/2009/10/20/calling-user-defined-functions-udfs-in-entity-framework.aspx
But I only want to call the UDF, I don't want to "join" it with any entities using a from clause:
string fieldTag = "TagNameHere";
var sql = "SELECT XyzModel.Store.FieldNameToFormIdMap(#fieldTag)";
System.Data.Objects.ObjectQuery<int> query =
new System.Data.Objects.ObjectQuery<int>(sql, xyzEntitiesContext);
query.Parameters.Add(new System.Data.Objects.ObjectParameter("fieldTag", fieldTag));
I get the error: "The query syntax is not valid."
This is using .NET 3.5
I could use old style ADO.NET but I'd rather not have to manage another set of connection strings aside from those used for the entity framework.

Just remove SELECT from your query, like this:
var sql = "XyzModel.Store.FieldNameToFormIdMap(#fieldTag)";

Related

EF PostgreeSQL array_append

I want append a item to column with type int[].
i see PostgreSQL document with array_append can do this.
but how can we do this with EF Core? (i use Npgsql.EntityFrameworkCore.PostgreSQL)
This currently isn't supported (the list of supported array translations can be found here).
In the meantime, as a workaround you can use raw SQL queries to express your query directly in SQL, and possibly compose additional LINQ operators over that.
EDIT: opened https://github.com/npgsql/efcore.pg/issues/2026 to track, this should be doable for 6.0.
raw SQL Query:
var sql= $"UPDATE public.\"TableName\" SET \"ArrayColumnName\" = array_append(\"ArrayColumnName\", {value}) WHERE ... ;";//fill ... with your condition
var result =await _dbContext.Database.ExecuteSqlRawAsync(sql) ;//return int

Select query linq to sql function [duplicate]

Is it possible to use custom method In query for example:
var result = from u in context.MyTable where MyMethod(u) == 10 select u;
As Pranay explains, you cannot have a custom (C#) method as part of the LINQ to SQL query, because LINQ to SQL wouldn't be able to look at the expression tree of the method and so it cannot translate it to SQL.
One option that you have is to write your function in SQL and store it as a SQL function on the SQL Server (possibly, you could also use SQL CLR, but I have not tried that). Then you can add the function to your DataContext type and LINQ to SQL will translate it to calls to the function on SQL server. Something like:
var result = from u in context.MyTable
where context.MyMethod(u) == 10 select u;
The problem, of course, is that you'll need to write the function in SQL (I think SQL CLR could also work - not sure about the performance and other possible complications though)
I also wrote an article (some time ago) that shows how to do this when you write the "method" as an expression tree way (as a value of type Expression<Func<...>>), which is possible, because in this case, the code is compiled as an expression tree. However, there is some postprocessing that has to be done and you can still write just a single expression that can be easily inlined in the LINQ query.
Check this full article : What is and what isn't possible with linq
Following is not possible
// function used in filter
static bool MyFunc(Nwind.Product p)
{
return p.ProductName.StartsWith("B");
}
// query that uses MyFunc
var q =
from p in db.Products
where MyPriceFunc(p.UnitPrice) > 30m
select p
It compiles with no errors, but when you execute it LINQ to SQL throws an exception saying: "Static method System.Boolean MyTest(LINQTest.Nwind.Product) has no supported translation to SQL."
The exception is actually thrown when you try to fetch results from q (for example using the foreach statement), because LINQ to SQL attempts to convert the expression trees to T-SQL only when the results are needed and the query must be executed.
To fix the example you can simply copy the code that checks whether product name starts with "B" to the where clause of the query and it would work fine.
Yes, but if you are using Linq-to-Sql - your method has to have special code to handle to SQL conversion.

Include with FromSqlRaw and stored procedure in EF Core 3.1

So here's the deal - I am currently using EF Core 3.1 and let's say I have an entity:
public class Entity
{
public int Id { get; set; }
public int AnotherEntityId { get; set; }
public virtual AnotherEntity AnotherEntity { get; set; }
}
When I access the DbSet<Entity> Entities normal way, I include AnotherEntity like:
_context.Entities.Include(e => e.AnotherEntity)
and this works. Why wouldn't it, right? Then I go with:
_context.Entities.FromSqlRaw("SELECT * FROM Entities").Include(e => e.AnotherEntity)
and this also works. Both return me the same collection of objects joined with AnotherEntity. Then I use a stored procedure which consists of the same query SELECT * FROM Entities named spGetEntities:
_context.Entities.FromSqlRaw("spGetEntities")
guess what? This also works. It gives me the same output but without joined AnotherEntity, obviously. However if I try to add the Include like this:
_context.Entities.FromSqlRaw("spGetEntities").Include(e => e.AnotherEntity)
I am getting:
FromSqlRaw or FromSqlInterpolated was called with non-composable SQL
and with a query composing over it. Consider calling AsEnumerable
after the FromSqlRaw or FromSqlInterpolated method to perform the
composition on the client side.
Even though the output of _context.Entities.FromSqlRaw("SELECT * FROM Entities") and _context.Entities.FromSqlRaw("spGetEntities") is identical.
I couldn't find a proof that I can or I can not do this with EF Core 3.1 but if someone could give me any hint of possibility of this approach it would be nice.
Also if there is another way to get joined entities using stored procedure I would probably accept it as the solution of my issue.
Shortly, you can't do that (at least for SqlServer). The explanation is contained in EF Core documentation - Raw SQL Queries - Composing with LINQ:
Composing with LINQ requires your raw SQL query to be composable since EF Core will treat the supplied SQL as a subquery. SQL queries that can be composed on begin with the SELECT keyword. Further, SQL passed shouldn't contain any characters or options that aren't valid on a subquery, such as:
A trailing semicolon
On SQL Server, a trailing query-level hint (for example, OPTION (HASH JOIN))
On SQL Server, an ORDER BY clause that isn't used with OFFSET 0 OR TOP 100 PERCENT in the SELECT clause
SQL Server doesn't allow composing over stored procedure calls, so any attempt to apply additional query operators to such a call will result in invalid SQL. Use AsEnumerable or AsAsyncEnumerable method right after FromSqlRaw or FromSqlInterpolated methods to make sure that EF Core doesn't try to compose over a stored procedure.
Additionally, since Include / ThenInclude require EF Core IQueryable<>, AsEnumerable / AsAsyncEnumerable etc. is not an option. You really need composable SQL, hence stored procedures are no option.
Instead of stored procedures though, you can use Table-Valued Functions (TVF) or database views because they are composable (select * from TVF(params) or select * from db_view) .
The solution for me was to add .AsEnumerable() before performing any operation on the result of the raw sql query.
this fails
var results = ExecuteMyRawSqlQuery();
results.Select(r=> r.MyColumn).ToList();
this works
var results = ExecuteMyRawSqlQuery();
results.AsEnumerable().Select(r=> r.MyColumn).ToList();
In my case I was converting working EF FromSql() with a stored procedure 2.1 code to 3.1.
Like so:
ctx.Ledger_Accounts.FromSql("AccountSums #from, #until, #administrationId",
new SqlParameter("from", from),
new SqlParameter("until", until),
new SqlParameter("administrationId", administrationId));
Where AccountSums is a SP.
The only thing I had to do was use FromSqlRaw() and add IgnoreQueryFilters() to get it working again. Like so:
ctx.Ledger_Accounts.FromSqlRaw("AccountSums #from, #until, #administrationId",
new SqlParameter("from", from),
new SqlParameter("until", until),
new SqlParameter("administrationId", administrationId)).IgnoreQueryFilters();
This is mentioned in the comments, but I missed that at first so including this here.

Confused about setting up parameters for an SQL Query

I'm using Dapper ORM to query my database. I've familiarized myself with the query syntax, but I'm having issues with the parameters.
Here is the query that works:
orders = ctx.Query<OrderView>(myQuery, new { Seller = 104386, StatusID = 2, query = "people"});
This maps the parameters in myQuery (#Seller, #StatusID, #query) to these values and it works correctly.
However, in my program, I generate parameters on the fly and store them in an List<ObjectParameter>, so each parameter has a name and a value. However, I can't get this to work correctly in the query. Here is an example of what doesn't work:
orders = ctx.Query<OrderView>(myQuery, parameters.toArray());
I've also tried converting that to List<SqlParameter> but that doesn't work either. Does anyone know how I can replicate the working query with my parameters list?
ObjectParameter is an EF thing (System.Data.Entity.dll) and is not used in dapper. You want DynamicParameters, inside the dapper assembly / namespace:
var args = new DynamicParameters();
...
args.Add(name, value); // there are more complex usages, note
...
connection.Query<OrderView>(myQuery, args);
If you want to take more control - for example you really want to use ObjectParameter - that's fine too: you just write your own type that implements SqlMapper.IDynamicParameters, and pass that to dapper.

generate sql query from entity framework

I am trying to generate sql query using .ToTraceString() in entity framework 3,5;
but I am getting the following error
Cannot convert type 'System.Collections.Generic.List to 'System.Data.Objects.ObjectQuery'
I have the following code
I need to generate sql query
customers= ent.Customer
.Include("UserType")
.Include("User")
.Where(c => c.Enabled == true)
.ToList<Customer>();
How to get sql query generated by entity framework. I don't have sql profiler.
Many thanks
Try not to call ToList() prior calling ToTraceString() because when you do you have list returned which doesn't have ToTraceString() method

Categories