Почему этот код не выполняет хранимую процедуру и не вставляет данные?

У меня есть контроллер, указанный здесь, который должен вызывать метод Dapper, однако я не могу заставить его выполниться. Он не выдает никаких исключений и перенаправляется при попытке поймать, однако процедура, похоже, не выполняется, поскольку новые пользователи никогда не создаются.

Настройка соединения работает с помощью простых встроенных методов .Query, однако, как только я пытаюсь сделать это с помощью хранимой процедуры, происходит сбой.

Модель:

    public class User
    {
        public int UserID { get; set; }
        public string Username { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Boolean ActiveB { get; set; }
    }

Вид:

@model ScaleBase.Models.FullUser

@{
    ViewData["Title"] = "Create";
}

<h2>Create</h2>

<h4>User</h4>
<hr />
<div class = "row">
    <div class = "col-md-4">
        <form asp-action = "Create">
            <div asp-validation-summary = "ModelOnly" class = "text-danger"></div>
            <div class = "form-group">
                <label asp-for = "OrganisationID" class = "control-label"></label>
                <input asp-for = "OrganisationID" class = "form-control" />
                <span asp-validation-for = "OrganisationID" class = "text-danger"></span>
            </div>
            <div class = "form-group">
                <label asp-for = "ClientID" class = "control-label"></label>
                <input asp-for = "ClientID" class = "form-control" />
                <span asp-validation-for = "ClientID" class = "text-danger"></span>
            </div>
            <div class = "form-group">
                <label asp-for = "TeamID" class = "control-label"></label>
                <input asp-for = "TeamID" class = "form-control" />
                <span asp-validation-for = "TeamID" class = "text-danger"></span>
            </div>
            <div class = "form-group">
                <label asp-for = "Email" class = "control-label"></label>
                <input asp-for = "Email" class = "form-control" />
                <span asp-validation-for = "Email" class = "text-danger"></span>
            </div>
            <div class = "form-group">
                <label asp-for = "Username" class = "control-label"></label>
                <input asp-for = "Username" class = "form-control" />
                <span asp-validation-for = "Username" class = "text-danger"></span>
            </div>
            <div class = "form-group">
                <label asp-for = "Password" class = "control-label"></label>
                <input asp-for = "Password" type = "password" class = "form-control" />
                <span asp-validation-for = "Password" class = "text-danger"></span>
            </div>
            <div class = "form-group">
                <label asp-for = "FirstName" class = "control-label"></label>
                <input asp-for = "FirstName" class = "form-control" />
                <span asp-validation-for = "FirstName" class = "text-danger"></span>
            </div>
            <div class = "form-group">
                <label asp-for = "LastName" class = "control-label"></label>
                <input asp-for = "LastName" class = "form-control" />
                <span asp-validation-for = "LastName" class = "text-danger"></span>
            </div>            
            <div class = "form-group">
                <div class = "checkbox">
                    <label>
                        <input asp-for = "ActiveB" /> @Html.DisplayNameFor(model => model.ActiveB)
                    </label>
                </div>
            </div>
            <div class = "form-group">
                <input type = "submit" value = "Create" class = "btn btn-default" />
            </div>
        </form>
    </div>
</div>

Контроллер:

public ActionResult Create(IFormCollection collection)
{
        try
        {
            DynamicParameters parameters = new DynamicParameters();

            parameters.Add("@Organisation", collection["OrganisationID"]);
            parameters.Add("@ClientID1", collection["ClientID"]);
            parameters.Add("@Team", collection["TeamID"]);
            parameters.Add("@Email", collection["Email"]);
            parameters.Add("@UserName", collection["UserName"]);
            parameters.Add("@Password", collection["Password"]);
            parameters.Add("@FirstName", collection["FirstName"]);
            parameters.Add("@LastName", collection["LastName"]);

            var affectedRows = _dapperRepo.CreateUser(parameters);

            return RedirectToAction(nameof(Index));
        }
        catch (Exception)
        {
            throw;
        }
    }

Шикарный репозиторий:

    public async Task<User> CreateUser(DynamicParameters parameters)
    {
        using (IDbConnection conn = Connection)
        {            
            string sproc = "EXEC sproc_NewUser @Organisation, @Client1, @Team, @Email  @UserName, @Password, @FirstName, @LastName";
            conn.Open();
            var result = await conn.QueryAsync(sproc, parameters, commandType: CommandType.StoredProcedure);
            return result.FirstOrDefault();
        }   
    }

Хранимая процедура:

BEGIN TRY 
    BEGIN TRANSACTION NewUser
        DECLARE @salt UNIQUEIDENTIFIER = NEWID()

        INSERT INTO [dbo].[User] (Username, Email, FirstName, LastName, Password, Salt, Active) 
        VALUES (@UserName, @Email, @FirstName, @LastName, HASHBYTES('SHA2_512', @Password+CAST(@salt AS NVARCHAR(36))), @salt, 1)

        INSERT INTO [dbo].[UserOrganisations] (UserID, OrganisationID) 
        VALUES (IDENT_CURRENT('User'), @Organisation)

        INSERT INTO [dbo].[UserClients] (UserID, ClientID) 
        VALUES (IDENT_CURRENT('User'), @Client1)

        IF @Client2 IS NOT NULL
        BEGIN 
            INSERT INTO [dbo].[UserClients] (UserID, ClientID) 
            VALUES (IDENT_CURRENT('User'), @Client2)
        END

        IF @Client3 IS NOT NULL
        BEGIN 
            INSERT INTO [dbo].[UserClients] (UserID, ClientID) 
            VALUES (IDENT_CURRENT('User'), @Client3)
        END

        IF @Client4 IS NOT NULL
        BEGIN 
            INSERT INTO [dbo].[UserClients] (UserID, ClientID) 
            VALUES (IDENT_CURRENT('User'), @Client4)
        END

        IF @Client5 IS NOT NULL
        BEGIN 
            INSERT INTO [dbo].[UserClients] (UserID, ClientID) 
            VALUES (IDENT_CURRENT('User'), @Client5)
        END

        INSERT INTO [dbo].[UserTeams] (UserID, TeamID) 
        VALUES (IDENT_CURRENT('User'), @Team)

        INSERT INTO [dbo].[UserPermission] (UserID, HolidayCount, HolidayUsed, TemplateID, ConfigState1, ConfigState2, ConfigState3, ConfigState4, ConfigState5) 
        VALUES (IDENT_CURRENT('User'), @Holiday, 0, 1, 255, null, null, null, null)

        INSERT INTO [dbo].[UserTime] (UserID, Scale, StartTime, EndTime) 
        VALUES (IDENT_CURRENT('User'), 1, @StartTime, @EndTime)

        COMMIT TRANSACTION NewUser

        PRINT 'Success'

        SELECT 
            [UserID], [Username], [Email], [Firstname], [Lastname], [Active] 
        FROM 
            [User] 
        WHERE 
            [UserID] = IDENT_CURRENT('User')
END TRY
BEGIN CATCH
    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK TRANSACTION NewUser
        PRINT 'Failed'
    END 

    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage

        RETURN 'Error'
END CATCH

Какую систему БД вы используете? SQLS? Вы уверены, что БД, которую вы просматриваете, и БД, которую модифицирует ваш код, это одна и та же БД? Я блокирую себя каждый раз, когда редактирую dev db в коде и смотрю вживую, говоря: «Почему здесь нет моего значения?!?»

Caius Jard 27.05.2019 21:35

Да я тоже так делаю! К сожалению, это не так просто, у меня есть другие методы, использующие этот DbConnection, и все они работают нормально, по общему признанию, они являются встроенным SQL, работающим с методом Query. Но я не уверен, в чем разница?

Connor Willoughby 27.05.2019 21:37

SQL Server 2016

Connor Willoughby 27.05.2019 21:42

Кажется, у вас есть ClientID1 по сравнению с Client1, вы уверены, что передаете правильные значения?

Hans Kesting 27.05.2019 22:02

@HansKefing, я только что исправил это и перестроил, однако, похоже, это не решает проблему. Я также дважды проверил параметры от хранимой процедуры до FormCollection, однако похоже, что проблема все еще остается.

Connor Willoughby 27.05.2019 22:07

В стороне: вместо того, чтобы использовать Ident_Current() для многократного получения значений, на которые может повлиять любой сеанс или область действия, вам будет лучше захватить значение Scope_Identity() в переменной сразу после insert в dbo.User и использовать это значение после этого.

HABO 27.05.2019 22:17

@HABO, да, ты совершенно прав. Я должен добавить это, как только я заработаю!

Connor Willoughby 27.05.2019 22:20

А sproc отлично работает из SSMS?

Caius Jard 27.05.2019 22:27

@CaiusJard, это действительно так.

Connor Willoughby 27.05.2019 22:37

Предполагается, что хранимые процедуры возвращают целочисленные значения, но ваши возвращают строку. Если вы исправите его так, чтобы он возвращал 1 в конце попытки (успех) и возвращал 0 в конце th catch (неудача), что dapper сообщает о возвращаемом значении?

Caius Jard 28.05.2019 07:43

(Добавьте параметр типа Direction.ReturnValue — см. stackoverflow.com/questions/44161310/…)

Caius Jard 28.05.2019 07:48

Другая сторона мысли; возможно, сделать возвращаемое значение ошибки отличным от 0, так как это значение по умолчанию для С# - вы не хотели бы, чтобы dapper по умолчанию имел значение 0, даже не запуская sproc и вводя вас в заблуждение, что 0 возвращается из sproc

Caius Jard 28.05.2019 08:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
12
629
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

При использовании commandType: CommandType.StoredProcedure вам нужно указать только имя хранимой процедуры, поэтому вместо

string sproc = "EXEC sproc_NewUser @Organisation, @Client1, @Team, @Email  @UserName, @Password, @FirstName, @LastName";

Просто пиши

string sproc = "sproc_NewUser";

К сожалению, это не решает проблему. По сути, изменений нет, после отправки формы я все еще получаю перенаправление, но пользователь не создается.

Connor Willoughby 27.05.2019 21:23

Вы получаете какой-либо результат? Вы подтвердили, что ваша процедура не завершилась откатом транзакции и возвратом ошибки? Если ошибки не регистрируются в базе данных, вы можете удалить try...catch в хранимой процедуре и просто сделать это на стороне С#. Эмпирическое правило для исключений состоит в том, чтобы выбрасывать как можно раньше, но ловить только тогда, когда вы действительно можете что-то сделать с ошибкой (и что-то может быть повторной попыткой, журналом, отображением удобного для пользователя сообщения об ошибке и т. д.) - в зависимости от самого исключения).

Zohar Peled 28.05.2019 06:44

Вместо использования QueryAsync используйте ExecuteAsync, который вернет количество строк, измененных командой.

Также вы не ожидаете своего асинхронного вызова. Это плохая практика. Попробуй это:

var affectedRows = await _dapperRepo.CreateUser(parameters);

а также

var result = await conn.ExecuteAsync(sproc, parameters, commandType: CommandType.StoredProcedure);

Вы можете обратиться к этому: https://dapper-tutorial.net/async#executeasync

Только что построил это, и кажется, что это тоже не работает.

Connor Willoughby 27.05.2019 21:55
Ответ принят как подходящий

Укажите ВСЕ параметры, которые перечислены в вашей хранимой процедуре. @Client2, @ClientID3 ect вызовет ошибку необъявленных параметров.

Изменение реализации этого на .ExecuteAsync и выполнение в контроллере, похоже, решает эту проблему.

public Async Task<IActionResult> Create(IFormCollection collection)
{
        try
        {
            DynamicParameters parameters = new DynamicParameters();

            parameters.Add("@Organisation", collection["OrganisationID"]);
            parameters.Add("@ClientID1", collection["ClientID"]);
            parameters.Add("@Team", collection["TeamID"]);
            parameters.Add("@Email", collection["Email"]);
            parameters.Add("@UserName", collection["UserName"]);
            parameters.Add("@Password", collection["Password"]);
            parameters.Add("@FirstName", collection["FirstName"]);
            parameters.Add("@LastName", collection["LastName"]);

            var affectedRows = _dapperRepo.CreateUser(parameters);
            using (IDbConnection conn = Connection)
            {            
                string sproc = "EXEC sproc_NewUser @Organisation, @Client1, @Team, @Email  @UserName, @Password, @FirstName, @LastName";
                conn.Open();
                var result = await conn.QueryAsync(sproc, parameters, commandType: CommandType.StoredProcedure);
                var result2 = result.FirstOrDefault();
            }   
            return RedirectToAction(nameof(Index));
        }
        catch (Exception)
        {
            throw;
        }
    }

Другие вопросы по теме