-2

I have a controller listed here which should be calling the Dapper method, however I cannot get it to execute. It's not throwing any exceptions and it is redirecting under the try catch however the procedure doesn't seem to be running as no new users are ever created.

The connection set up works with simple inline .Query methods however as soon as I attempt to do this with a stored procedure, it fails.

Model:

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

View:

@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>

Controller:

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

Dapper repo:

    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();
        }   
    }

Stored procedure:

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
Connor Willoughby
  • 86
  • 1
  • 1
  • 10
  • What db system are you using? SQLS? Are you certain the db you're looking in and the db your code is modifying, is the same db? I lock myself every time I'm editing the dev db in the code and looking in live, going "why isn't my value here?!?" – Caius Jard May 27 '19 at 19:35
  • Yeah I do the same too! Unfortunately its not a simple as that, i have other methods using this DbConnection and they all work fine, admittedly they're inline SQL running off the Query method. But i'm not sure what the difference would be anyway? – Connor Willoughby May 27 '19 at 19:37
  • SQL Server 2016 – Connor Willoughby May 27 '19 at 19:42
  • You seem to have ClientID1 versus Client1, are you sure you are passing the correct values? – Hans Kesting May 27 '19 at 20:02
  • @HansKesting, I have just corrected this and rebuilt however it doesn't seem to resolve the issue. I also double checked the parameters from the stored procedure up to the FormCollection however it appears as though the issue still remains. – Connor Willoughby May 27 '19 at 20:07
  • Aside: Rather than using [`Ident_Current()`](https://learn.microsoft.com/en-us/sql/t-sql/functions/ident-current-transact-sql?view=sql-server-2017) to repeatedly get values that may be affected by any session or scope you would be better served by capturing the value of [`Scope_Identity()`](https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?view=sql-server-2017) in a variable immediately after the `insert` into `dbo.User` and using that value thereafter. – HABO May 27 '19 at 20:17
  • @HABO, Yeah, youre completely right. I'll have to add that in once I get this working! – Connor Willoughby May 27 '19 at 20:20
  • And the sproc works perfectly from SSMS? – Caius Jard May 27 '19 at 20:27
  • @CaiusJard, it does indeed. – Connor Willoughby May 27 '19 at 20:37
  • Stored procedures are supposed to return integer values, but yours returns a string. If you fix it up so it returns 1 at the end of the try (success) and returns 0 at the end of th catch (fail) what does dapper report the return value to be? – Caius Jard May 28 '19 at 05:43
  • (Add a parameter of type Direction.ReturnValue - see https://stackoverflow.com/questions/44161310/how-do-i-use-dapper-to-get-the-return-value-of-stored-proc) – Caius Jard May 28 '19 at 05:48
  • Another side thought; maybe make the fail return value something other than 0, as it's c#'s default - you wouldn't want dapper to be defaulting 0, failing to even run the sproc and misleading you that 0 was coming back from the sproc – Caius Jard May 28 '19 at 06:12

3 Answers3

2

When using commandType: CommandType.StoredProcedure you only need to specify the stored procedure name - so instead of

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

Just write

string sproc = "sproc_NewUser";
Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • Unfortunately this doesn't seem to resolve the issue. Essentially there is no change, after form submission I still get the redirect however the user isnt created. – Connor Willoughby May 27 '19 at 19:23
  • Do you get any result? have you confirmed your procedure didn't end up rolling back the transaction and returning an error? Unless logging the errors in the database, you can remove the `try...catch` in the stored procedure and simply do that on the c# side. The rule of thumb for exceptions is to throw as early as possible, but only catch when you can actually do something about the error (and do something might be retry, log, show a user-friendly error message to the user, etc' - depending on the exception itself). – Zohar Peled May 28 '19 at 04:44
0

Instead of using QueryAsync use ExecuteAsync that will return the number of rows altered by the command.

Also you are not awaiting your async call. That is no good practice. Try this:

var affectedRows = await _dapperRepo.CreateUser(parameters);

and

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

You can refer to this: https://dapper-tutorial.net/async#executeasync

NicoRiff
  • 4,803
  • 3
  • 25
  • 54
0

Delclare ALL paramaters which are listed in your stored procedure. @Client2, @ClientID3 ect will cause an error of undeclared paramaters.

Changing the implementation of this to .ExecuteAsync and executing in the controller seem to resolve this issue.

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;
        }
    }
Connor Willoughby
  • 86
  • 1
  • 1
  • 10