Ядро EF — что такое замена MapToStoredProcedures в EF Core 3.1 или 5

#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"
        };
    }
}