How to handle stored procedure that returns different outputs in Entity Framwork.
For example , on sp, there is if condition and else condition. if condition return integer and else condition return datatable. How to handle this using entity framework in vs.
Please advice.
Starting from EF 4.1 this is possible.
The only requirement is to know what the SP is going to return in each case.
In my example I use
DbContext.Database.SqlQuery<TElement>(string sql, params object[] parameters)
This generic method takes as a generic parameter the type you would like to use for materialization. It also takes SQL expression you would like to execute as a parameter, along with it's parameters as param'd array.
Simplified SP (no input parameters) usage is:
var res = ctx.Database.SqlQuery<MyResultType1>("dbo.MyStoredProcedure");
foreach (var r in res)
{
System.Console.Out.WriteLine(
"col1:{0}; col2:{1}; col3={2}",
r.Col1,
r.Col2,
r.Col3);
}
So, you can do following:
IEnumerable res
if(...your logic...)
{
res = ctx.Database.SqlQuery<MyResultType1>(...your SP call...);
}
else
{
res = ctx.Database.SqlQuery<MyResultType2>(...your SP call...);
}
This way you are going to fill your collection with SP output resultset the way you want.
Click update model from database select your stored procedure and make sure it procedure has been added in Function Imports(Model Browser)
You can change return values from Edit function import window
and then just execute db.myProcedure();
eg:
just drag and drop your stored procedure and then
db.procedure(element1, element2);
db.SubmitChanges();
Ef4 does not support stored procedure with multiple returns of different types. We can do it either by direct calling of Sp via sql codes or can do the schema in linq.
Else we needs to use EF 4.1 and above.
Related
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.
I need to return info from database with T-SQL and I'm using dapper.
I've written this code:
SELECT *
FROM UserActivetionCode UA
WHERE UA.Username=#Username
AND UA.ActivetionCode= #ActivetionCode
AND UA.ExpireTime > GETDATE()
and I'm using this code:
var find = await roleConnection.QueryFirstAsync(command, new {
#ActivetionCode = ActivetionCode,
#Username = Username });
but it shows me this error:
Sequence contains no elements
This error means that the query didn't return any results.
If you don't want to get it, use QueryFirstOrDefaultAsync.
This will result with default(T) (T being whatever datatype the query should return) if the query returned no rows.
However, QueryFirst implies a known order in the resultset, which your query does not provide (as it doesn't have an order by clause) and in any case, I suspect that the combination of Username and ActivetionCode should probably be unique. In that case, I would go with QuerySingleOrDefault, which will throw an exception if the query returned more than one result.
I want to call a stored procedure in bulk with Dapper. Each call will return a different set of results, so I was using QueryMultiple. If I create the parameters like this:
var parameters = new DynamicParameters();
// The stored proc accepts a single integer, but I want to pass multiple so each call had a new value.
parameters.Add("LookupID", lookupIds);
var resultSet = connection.QueryMultiple("SPName", parameters, commandType: System.Data.CommandType.StoredProcedure);
I get an error that the stored procedure has too many arguments specified. So how else can I accomplish this?
QueryMultiple is about the results, not the inputs. To do what you want, you need to call the SP lots of times. Which you can do either via a simple foreach loop yourself, or many (not all) of the dapper methods will allow a convenience method by passing in an IEnumerable<T> as the outermost object, for example:
int[] ids = ...
var args = ids.Select(id => new { id, val = "ABC" });
db.Execute("whatever", args);
Which will execute the SQL once per item passing in #id and #val for each. In the case of the async API this can also be combined with MARS to remove the cost of latency between calls, but this is opt-in because MARS is not a default option.
Alternatively, you could write a new wrapper SP that takes a single parameter and splits it (the new string_split would be ideal), and do the loop at the server.
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".
I'm trying to call a stored procedure in Entity Framework 4 application and so far getting strange results. Stored procedure takes IN and OUT parameters, and returns resultset. I mapped stored procedure and created a complex type that represents a row in returned resultset. I call it
using (MyObjectContext ctx = new MyObjectContext())
{
ObjectParameter out1 = new ObjectParameter("out1", typeof(String));
ObjectParameter out2 = new ObjectParameter("out2", typeof(String));
var res = ctx.my_proc(1,2, out1,out2);
}
The problem is that unless I call res.ToList() (or enumerate through res, or call any methods that accesses underlying collection), values of out1 and out2 are null.
How do I fix it?
Thanks
You cannot fix it. It is a gotcha. Result set must be materialized or thrown away before output parameters are accessible because these output parameters are transferred in the last result set so internal data reader must first read the returning result set with main data and then access the next result set to read output parameters. Btw. it is the same if you use ADO.NET directly - that is not a feature of Entity framework itself but the way how SQL server returns output parameters and the way how DataReader consumes result sets in sequential order.
when u call procedure or query that returns a table of rows
EF does real execution when user calls ToList or enumerate the return value