Impossible to use stored procedures related to entities that inherits another one
See original GitHub issueThere is no way what so ever to get any dataset from a stored procedure that returns a type that inherits from another one:
var result = this.Context.Set<MyEntity>().FromSqlRaw($"EXECUTE MyStoredProcedure").AsEnumerable().ToList();
Whatever I do, it will always compose the call of my stored procedure: I suppose it tries to compose because it feels the need to ensure to get the right type. But it is obviously wrong:
SELECT
[p].blabla1,
[p].blabla2
FROM (
EXECUTE MyStoredProcedure
) AS [p]
WHERE ([p].[Discriminator] = N'MyEntity')
Is there a way to have the code executed AS IS (no sort
, no any where
clause that would disturb the order of the dataset…), and blindly map to the columns that are setup in the entity setup?
Prior EF core 3.0, wrapping the sql statement within a sql_execute
did the trick. But this doesn’t work anymore as it tries to compose anyway. This new behavior is more than understandable, as I was even initially surprised that preventing composition in such a way was working. I perfectly get the point of composition, and it is a fantastic feature to combine custom code with Linq operators. But it happens (in some specific cases) that we actually simply need EF mapping feature.
I understand that amending existing freshly neat and clear methods would jeopardize stability and clarity of its behavior, so I suppose it is very reasonable to assume that FromSqlRaw
, FromSqlInterpolated
or FromSql
will always be composed as they are meant to be combined with Linq operations.
For everything else, you could simply expose another method that permits to build a collection of entities from a DataTable, a DataReader, a free sql statement (or anything that you believe is better) based on EF mapping specifications as this feature must already already exist somewhere within EF core. No confusion as the distinction would be crystal clear, and everybody (EF team and developers) would be satisfied.
Note
I created an interceptor to proceed with a simple “uncompose” for statements that have been composed whereas they shouldn’t.
using System.Data.Common;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore.Diagnostics;
namespace FundProcess.Pms.DataAccess
{
public class InappropriateCompositionRemoverInterceptor : DbCommandInterceptor
{
private static readonly string[] _regexString = new[]{
@".*\sfrom\s+\(\s*(?<query>execute\s+.+)\s*\)\s+as\s+.*",
@".*\sfrom\s+\(\s*(?<query>.+\s+option\s+\(.+\))\s*\)\s+as\s+.*"
};
private static Regex[] _regexes;
public InappropriateCompositionRemoverInterceptor()
{
_regexes = _regexString.Select(i => new Regex(i, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline | RegexOptions.IgnoreCase)).ToArray();
}
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
foreach (var regex in _regexes)
{
command.CommandText = ProcessRegex(command.CommandText, regex);
}
return result;
}
private string ProcessRegex(string input, Regex regex)
{
var match = regex.Match(input);
if (match.Success)
input = match.Groups["query"].Value;
return input;
}
}
}
The problem is that EF core doesn’t map column using their name but by using their position in the column list… So I’m still stuck as I don’t have any idea where EF takes this specific order from. So I still have no way to get entities from a procedure, in a multitenant context, with an entity type that inherits another one.
Further technical details
EF Core version: 3.0 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET Core 3.0 Operating system: ubuntu 19.04 IDE: Visual Studio Code
Issue Analytics
- State:
- Created 4 years ago
- Reactions:3
- Comments:13 (6 by maintainers)
@stas-lucky - If your entity has a global query filter then you need to call IgnoreQueryFilters. Either your stored procedure returns data which is already filtered for your purposes (in which case you are good to go), or data from sproc requires additional filtering, since purpose of global query filters is to apply filters on database side, in latter case you need to ignore query filters and apply filters yourself on client.
This issue does not happen when using global query filter. I have updated title and OP to reflect that.
I’m not sure what is the deal here, but personally, I am unable to run that command at all.
There are no calls made on the database, and I always get the following error:
I’m on EF Core 3.1.1. I spent about 3h trying everything with this .NET core 3.1.1 and nothing, I can’t make a store procedure work at all.