Search code examples
c#asp.netstored-proceduresasp.net-identitydapper

ASP .Net Identity, Dapper & stored procedures Best Practices


I am currently following this guide to learn Dapper micro ORM and ASP.NET identity. I thought to mix it up a bit by implementing a generic repository pattern that will help me do basic CRUD operations.

Here are my classes:

User.cs

public class User : IUser
{
    public Guid UserId { get; set; }
    public string UserName { get; set; }
    public string PasswordHash { get; set; }
    public string SecurityStamp { get; set; }

    string IUser<string>.Id
    {
        get { return UserId.ToString(); }
    }
}

Repository.cs

public class Repository<T> : IRepository<T> where T : class
{
    public int ExecuteScalar(string query, object arguments)
    {
        var id = 0;

        using (var connection = ConnectionFactory.CreateConnection())
        {
            id = connection.ExecuteScalar<int>(query, arguments, commandType: CommandType.StoredProcedure);
        }

        return id;
    }
}

UserStore.cs

public class UserStore : IUserStore<User>, IUserLoginStore<User>, IUserPasswordStore<User>, IUserSecurityStampStore<User>
{
    private IRepository<User> repository;

    public UserStore(IRepository<User> repository)
    {
        this.repository = repository;
    }

    #region IUserStore
    public virtual Task CreateAsync(User user)
    {
        if (user == null)
            throw new ArgumentNullException("user");

        return Task.Factory.StartNew(() =>
        {
            user.UserId = Guid.NewGuid();
            repository.ExecuteScalar("sp_InsertUser",
                new { UserId = user.UserId, UserName = user.UserName, Password = user.PasswordHash,
                    SecurityStamp = user.SecurityStamp });
        });
    }
}

I didn't provide the other methods and classes just to make my post shorter: connection factory that will create SQL connection also the methods part of the interfaces in UserStore.

I have the following questions:

  1. Is there a way to pass the User object as it is to the ExecuteScalar method? Instead of typing in all the parameters each time.
  2. Is it really best practice to create my CRUD operations as stored procedure? For example here sp_InsertUser.
  3. Would the async methods here cause problems?
  4. In case I wanted to write an SQL query and pass it to Dapper (add query to XML file and read it from there). I saw this guide also and I liked the fact that everything is generic and I can minimize my code and create something neat. Is it best practice to call string format and pass parameters?

    string.Format("UPDATE [{0}] SET {1} WHERE {2}", typeof(T).Name, sqlValuePairs, sqlIdPairs)

I really apologize for the long post, but I am really new to this and I am trying to learn those technologies. But the more I search for articles, the more confused I get.


Solution

    1. if the parameter names and property names are a complete match, you can just pass user; however, note that while dapper can try to check what parameters are needed for inline text, it cannot do this for stored procedures, so it tries to send everything
    2. is largely opinion and preference; there are tools like dapper.contrib and dapperextensions that can automate this
    3. you should prefer the actual async methods like ExecuteScalarAsync
    4. seems to be a statement, not a question

    For the edited 4:

    1. well, there are any number of ways that could lead to vulnerabilities; but if you are tightly in control of the inputs it could work; there are times when I've done similar; but note that this is pretty much what things like "dapper.contrib" bring to the table