#c# #entity-framework-core
#c# #entity-framework-core
Вопрос:
MapToStoredProcedures
В EF Core этого нет, что очень жаль, поскольку позволяет Add
методу скрывать, используется ли хранимая процедура или нет.
Я просмотрел EF Core 3.1 и 5 и не могу найти рекомендуемую замену. Итак, если у меня есть приведенный ниже код, как / где мне настроить и вызвать хранимую процедуру insert или выбрать хранимую процедуру для нее?
public class DatabaseModel : DbContext
{
public virtual DbSet<Office> Offices { get; set; }
public DatabaseModel(DbContextOptions<DatabaseModel> options) : base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder) {}
}
Спасибо за любую помощь.
Комментарии:
1. В настоящее время это не поддерживается
2. Отслеживается на github github.com/dotnet/efcore/issues/245
Ответ №1:
MapToStoredProcedures
не поддерживается в EF CORE. Вы можете выполнить хранимую процедуру с FromSqlRaw
помощью метода.
var result = ctx.ParameterDetails.FromSqlRaw("EXEC dbo.get_nextparam @UserId={0}", userId).ToList();
Комментарии:
1. какова цель ctx?
2. Это EF core DbContext
Ответ №2:
ПРИМЕЧАНИЕ: В НАСТОЯЩЕЕ ВРЕМЯ ЭТО НЕ РЕАЛИЗУЕТ МАССОВЫЕ ВСТАВКИ, КОТОРЫЕ ВЫПОЛНЯЕТ SQL SERVER, ДЛЯ РАБОТЫ НЕСКОЛЬКИХ С ОДНИМ И ТЕМ ЖЕ ИМЕНЕМ ТАБЛИЦЫ ВАМ ПОТРЕБУЕТСЯ РЕАЛИЗОВАТЬ AppendBulkInsertOperation
Вы могли бы создать реализацию, которая переопределяет Microsoft.EntityFrameworkCore.Update.IUpdateSqlGenerator
и выполняет a ReplaceService
для нее при регистрации. Я успешно переопределил операцию вставки в Sql Server.
В моем случае прямой вызов хранимой процедуры невозможен, потому что я пытаюсь обновить проект с EF 6 до EF Core с более минимальными изменениями.
Регистрация DbContext:
services.AddDbContext<ExampleDbContext>((sp, options) =>
{
options
.UseSqlServer(connectionString)
.ReplaceService<IUpdateSqlGenerator, MapToProcedureUpdateSqlGenerator>();
});
MapToProcedureUpdateSqlGenerator
using Microsoft.EntityFrameworkCore.SqlServer.Update.Internal;
using Microsoft.EntityFrameworkCore.Update;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EFCoreMapToStoredProcedures
{
// careful with this implementation as it gives warning
// This is an internal API that supports the Entity Framework Core infrastructure
// and not subject to the same compatibility standards as public APIs.
public class MapToProcedureUpdateSqlGenerator : SqlServerUpdateSqlGenerator
{
public MapToProcedureUpdateSqlGenerator(UpdateSqlGeneratorDependencies dependencies) : base(dependencies)
{
}
public override ResultSetMapping AppendInsertOperation(StringBuilder commandStringBuilder, ModificationCommand command, int commandPosition)
{
if (command == null) throw new ArgumentNullException(nameof(command));
if (commandStringBuilder == null) throw new ArgumentNullException(nameof(commandStringBuilder));
if (_tableInsertProcs.TryGetValue(command.TableName, out string procName))
{
var name = command.TableName;
var schema = command.Schema;
var operations = command.ColumnModifications;
var writeOperations = operations.Where(o => o.IsWrite).ToList();
AppendExecCommandHeader(commandStringBuilder, procName, schema, writeOperations);
if (operations.Any(_ => _.IsRead))
{
return ResultSetMapping.LastInResultSet;
}
return ResultSetMapping.NoResultSet;
}
else
{
return base.AppendInsertOperation(commandStringBuilder, command, commandPosition);
}
}
/// <summary>
/// Appends a SQL fragment for excuting a stored procedure
/// </summary>
/// <param name="commandStringBuilder"> The builder to which the SQL should be appended. </param>
/// <param name="name"> The name of the procedure. </param>
/// <param name="schema"> The table schema, or <see langword="null" /> to use the default schema. </param>
/// <param name="operations"> The operations representing the data to be inserted. </param>
protected virtual void AppendExecCommandHeader(
StringBuilder commandStringBuilder,
string name,
string schema,
IReadOnlyList<ColumnModification> operations)
{
if (commandStringBuilder == null) throw new ArgumentNullException(nameof(commandStringBuilder));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("required", nameof(name));
if (operations == null) throw new ArgumentNullException(nameof(operations));
commandStringBuilder.Append("EXEC ");
SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, name, schema);
if (operations.Count > 0)
{
commandStringBuilder
.AppendJoin(
operations,
(this, name, schema),
(sb, o, p) =>
{
if (o.IsWrite)
{
var (g, n, s) = p;
if (!o.UseCurrentValueParameter)
{
throw new NotSupportedException("literals not supported");
}
else
{
g.SqlGenerationHelper.GenerateParameterNamePlaceholder(sb, o.ColumnName);
commandStringBuilder.Append(" = ");
g.SqlGenerationHelper.GenerateParameterNamePlaceholder(sb, o.ParameterName);
}
}
else
{
sb.Append("DEFAULT");
}
});
commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator);
}
}
// todo make configurable with dependencies
private readonly Dictionary<string, string> _tableInsertProcs = new Dictionary<string, string>()
{
["OrderItems"] = "InsertOrderItem"
};
}
}