Executando inserções e atualizações com o Dapper

194

Estou interessado em usar o Dapper - mas, pelo que sei, ele suporta apenas a Consulta e Execução. Não vejo que o Dapper inclua uma maneira de inserir e atualizar objetos.

Dado que nosso projeto (a maioria dos projetos?) Precisa fazer inserções e atualizações, qual é a melhor prática para inserir e atualizar ao lado do dapper?

De preferência, não teríamos que recorrer ao método ADO.NET de criação de parâmetros, etc.

A melhor resposta que posso encontrar neste momento é usar o LinqToSQL para inserções e atualizações. Existe uma resposta melhor?

Slaggg
fonte
3
Existe essa extensão Contrib do próprio Dapper.NET que eu uso. github.com/StackExchange/dapper-dot-net/tree/master/…
Rajiv

Respostas:

201

Estamos pensando em criar alguns auxiliares, ainda decidindo sobre APIs e se isso é essencial ou não. Consulte: https://code.google.com/archive/p/dapper-dot-net/issues/6 para obter progresso.

Enquanto isso, você pode fazer o seguinte

val = "my value";
cnn.Execute("insert into Table(val) values (@val)", new {val});

cnn.Execute("update Table set val = @val where Id = @id", new {val, id = 1});

etcetera

Veja também no meu blog: Esse problema irritante do INSERT

Atualizar

Conforme apontado nos comentários, agora existem várias extensões disponíveis no projeto Dapper.Contrib na forma dos seguintes IDbConnectionmétodos de extensão:

T Get<T>(id);
IEnumerable<T> GetAll<T>();
int Insert<T>(T obj);
int Insert<T>(Enumerable<T> list);
bool Update<T>(T obj);
bool Update<T>(Enumerable<T> list);
bool Delete<T>(T obj);
bool Delete<T>(Enumerable<T> list);
bool DeleteAll<T>();
Sam Saffron
fonte
4
Oi Sam, encontrei sua resposta SO com o google e eu queria saber se a última linha de código deve incluir a palavra setcomo cnn.Execute("update Table SET val = @val where Id = @id", new {val, id = 1});ou isso é específico dapper? Eu sou novo para dapper e estava procurando um exemplo de atualização :)
JP Hellemons
1
@JPHellemons Eu tentei isso var updateCat = connection.Execute("UPDATE tCategories SET sCategory = @val WHERE iCategoryID = @id", new { val = "dapper test", id = 23 });e funcionou. Sem o uso de SET, recebo um erro de sintaxe SQLException perto de sCategory.
Caro,
3
Avanço rápido para dezembro de 2015: github.com/StackExchange/dapper-dot-net/tree/master/…
Rosdi Kasim
3
@RosdiKasim Isso não derrota o propósito de usar o Dapper? Eu quero usar SQL. Isso abstrai. o que estou perdendo?
Janny
2
@ johnny É apenas uma classe auxiliar ... algumas pessoas querem seu código o mais sucinto possível ... você não precisa usá-lo se não quiser.
Rosdi Kasim
67

Executar operações CRUD usando o Dapper é uma tarefa fácil. Mencionei os exemplos abaixo que devem ajudá-lo em operações CRUD.

Código para C RUD:

Método 1: Este método é usado quando você está inserindo valores de diferentes entidades.

using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDbConnection"].ConnectionString))
{
    string insertQuery = @"INSERT INTO [dbo].[Customer]([FirstName], [LastName], [State], [City], [IsActive], [CreatedOn]) VALUES (@FirstName, @LastName, @State, @City, @IsActive, @CreatedOn)";

    var result = db.Execute(insertQuery, new
    {
        customerModel.FirstName,
        customerModel.LastName,
        StateModel.State,
        CityModel.City,
        isActive,
        CreatedOn = DateTime.Now
    });
}

Método 2: Este método é usado quando as propriedades da sua entidade têm os mesmos nomes que as colunas SQL. Portanto, Dapper sendo um ORM mapeia as propriedades da entidade com as colunas SQL correspondentes.

using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDbConnection"].ConnectionString))
{
    string insertQuery = @"INSERT INTO [dbo].[Customer]([FirstName], [LastName], [State], [City], [IsActive], [CreatedOn]) VALUES (@FirstName, @LastName, @State, @City, @IsActive, @CreatedOn)";

    var result = db.Execute(insertQuery, customerViewModel);
}

Código para C R UD:

using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDbConnection"].ConnectionString))
{
    string selectQuery = @"SELECT * FROM [dbo].[Customer] WHERE FirstName = @FirstName";

    var result = db.Query(selectQuery, new
    {
        customerModel.FirstName
    });
}

Código para CR U D:

using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDbConnection"].ConnectionString))
{
    string updateQuery = @"UPDATE [dbo].[Customer] SET IsActive = @IsActive WHERE FirstName = @FirstName AND LastName = @LastName";

    var result = db.Execute(updateQuery, new
    {
        isActive,
        customerModel.FirstName,
        customerModel.LastName
    });
}

Código para CRU D :

using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDbConnection"].ConnectionString))
{
    string deleteQuery = @"DELETE FROM [dbo].[Customer] WHERE FirstName = @FirstName AND LastName = @LastName";

    var result = db.Execute(deleteQuery, new
    {
        customerModel.FirstName,
        customerModel.LastName
    });
}
Reyan Chougle
fonte
26

você pode fazer isso da seguinte maneira:

sqlConnection.Open();

string sqlQuery = "INSERT INTO [dbo].[Customer]([FirstName],[LastName],[Address],[City]) VALUES (@FirstName,@LastName,@Address,@City)";
sqlConnection.Execute(sqlQuery,
    new
    {
        customerEntity.FirstName,
        customerEntity.LastName,
        customerEntity.Address,
        customerEntity.City
    });

sqlConnection.Close();
Dana Addler
fonte
36
Você deve usar using-statementpara que a conexão seja fechada mesmo em caso de exceção.
precisa saber é o seguinte
12
você poderia simplesmente passar customerEntity diretamente em vez de usar um tipo anônimo ...
Thomas Levesque
@ThomasLevesque O que você quer dizer com isso? Você pode fornecer um pequeno exemplo de código do que você quer dizer?
23413 Iaacp
4
@iaacp, quero dizer que:sqlConnection.Execute(sqlQuery, customerEntity);
Thomas Levesque
1
@ThomasLevesque podemos atualizar também usando o mesmo padrão? isto é,sqlConnection.Execute(sqlQuery, customerEntity);
Shankar
16

Usando Dapper.Contrib é tão simples como isto:

Inserir lista:

public int Insert(IEnumerable<YourClass> yourClass)
{
    using (SqlConnection conn = new SqlConnection(ConnectionString))
    {
        conn.Open();
        return conn.Insert(yourClass) ;
    }
}

Inserir simples:

public int Insert(YourClass yourClass)
{
    using (SqlConnection conn = new SqlConnection(ConnectionString))
    {
        conn.Open();
        return conn.Insert(yourClass) ;
    }
}

Atualizar lista:

public bool Update(IEnumerable<YourClass> yourClass)
{
    using (SqlConnection conn = new SqlConnection(ConnectionString))
    {
        conn.Open();
        return conn.Update(yourClass) ;
    }
}

Atualização única:

public bool Update(YourClass yourClass)
{
    using (SqlConnection conn = new SqlConnection(ConnectionString))
    {
        conn.Open();
        return conn.Update(yourClass) ;
    }
}

Fonte: https://github.com/StackExchange/Dapper/tree/master/Dapper.Contrib

Ogglas
fonte
1
Usando o acima para inserir um único objeto, você pode recuperar o novo número de identidade e colocá-lo novamente em seu modelo ... Mas como você faz isso para inserir uma lista de objetos - os objetos na lista não têm o campo de identidade. Você precisa percorrer a lista e depois inseri-los um de cada vez, obtendo o novo ID a cada vez?
Harag
1
@harag Se você precisar do novo ID em outro lugar, acho que você deve fazê-lo dessa maneira. O Entity Framework lida com tipos de referência, como classes, sem problemas com inserções, mas não sei como o Dapper.Contrib funciona com isso se esse for o seu ângulo.
Ogglas
5
@ Douglas, obrigado. Eu notei que "connection.Insert (myObject)" atualizará a propriedade "[key]" do "myObject" se eu apenas estiver inserindo um objeto, mas se estiver inserindo uma lista de 5 objetos usando o mesmo "connection.Insert (myObjectList)", então nenhuma das propriedades [keys] é atualizada, então eu tenho que fazer manualmente para cada item da lista e inseri-los um por vez.
Harag
1
Em conn.Update(yourClass)se algumas propriedades são nulos , em seguida, atualizar os campos para NULL ? Não está funcionando. Atualize o campo para NULL . Not partials updates
Kiquenet
5

Você também pode usar o dapper com um procedimento armazenado e uma maneira genérica pela qual tudo é facilmente gerenciado.

Defina sua conexão:

public class Connection: IDisposable
{
    private static SqlConnectionStringBuilder ConnectionString(string dbName)
    {
        return new SqlConnectionStringBuilder
            {
                ApplicationName = "Apllication Name",
                DataSource = @"Your source",
                IntegratedSecurity = false,
                InitialCatalog = Database Name,
                Password = "Your Password",
                PersistSecurityInfo = false,
                UserID = "User Id",
                Pooling = true
            };
    }

    protected static IDbConnection LiveConnection(string dbName)
    {
        var connection = OpenConnection(ConnectionString(dbName));
        connection.Open();
        return connection;
    }

    private static IDbConnection OpenConnection(DbConnectionStringBuilder connectionString)
    {
        return new SqlConnection(connectionString.ConnectionString);
    }

    protected static bool CloseConnection(IDbConnection connection)
    {
        if (connection.State != ConnectionState.Closed)
        {
            connection.Close();
            // connection.Dispose();
        }
        return true;
    }

    private static void ClearPool()
    {
        SqlConnection.ClearAllPools();
    }

    public void Dispose()
    {
        ClearPool();
    }
}

Crie uma interface para definir os métodos Dapper dos quais você realmente precisa:

 public interface IDatabaseHub
    {
   long Execute<TModel>(string storedProcedureName, TModel model, string dbName);

        /// <summary>
        /// This method is used to execute the stored procedures with parameter.This is the generic version of the method.
        /// </summary>
        /// <param name="storedProcedureName">This is the type of POCO class that will be returned. For more info, refer to https://msdn.microsoft.com/en-us/library/vstudio/dd456872(v=vs.100).aspx. </param>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="model">The model object containing all the values that passes as Stored Procedure's parameter.</param>
        /// <returns>Returns how many rows have been affected.</returns>
        Task<long> ExecuteAsync<TModel>(string storedProcedureName, TModel model, string dbName);

        /// <summary>
        /// This method is used to execute the stored procedures with parameter. This is the generic version of the method.
        /// </summary>
        /// <param name="storedProcedureName">Stored Procedure's name. Expected to be a Verbatim String, e.g. @"[Schema].[Stored-Procedure-Name]"</param>
        /// <param name="parameters">Parameter required for executing Stored Procedure.</param>        
        /// <returns>Returns how many rows have been affected.</returns>         
        long Execute(string storedProcedureName, DynamicParameters parameters, string dbName);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="storedProcedureName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        Task<long> ExecuteAsync(string storedProcedureName, DynamicParameters parameters, string dbName);
}

Implemente a interface:

     public class DatabaseHub : Connection, IDatabaseHub
        {

 /// <summary>
        /// This function is used for validating if the Stored Procedure's name is correct.
        /// </summary>
        /// <param name="storedProcedureName">Stored Procedure's name. Expected to be a Verbatim String, e.g. @"[Schema].[Stored-Procedure-Name]"</param>
        /// <returns>Returns true if name is not empty and matches naming patter, otherwise returns false.</returns>

        private static bool IsStoredProcedureNameCorrect(string storedProcedureName)
        {
            if (string.IsNullOrEmpty(storedProcedureName))
            {
                return false;
            }

            if (storedProcedureName.StartsWith("[") && storedProcedureName.EndsWith("]"))
            {
                return Regex.IsMatch(storedProcedureName,
                    @"^[\[]{1}[A-Za-z0-9_]+[\]]{1}[\.]{1}[\[]{1}[A-Za-z0-9_]+[\]]{1}$");
            }
            return Regex.IsMatch(storedProcedureName, @"^[A-Za-z0-9]+[\.]{1}[A-Za-z0-9]+$");
        }

     /// <summary>
            /// This method is used to execute the stored procedures without parameter.
            /// </summary>
            /// <param name="storedProcedureName">Stored Procedure's name. Expected to be a Verbatim String, e.g. @"[Schema].[Stored-Procedure-Name]"</param>
            /// <param name="model">The model object containing all the values that passes as Stored Procedure's parameter.</param>
            /// <typeparam name="TModel">This is the type of POCO class that will be returned. For more info, refer to https://msdn.microsoft.com/en-us/library/vstudio/dd456872(v=vs.100).aspx. </typeparam>
            /// <returns>Returns how many rows have been affected.</returns>

            public long Execute<TModel>(string storedProcedureName, TModel model, string dbName)
            {
                if (!IsStoredProcedureNameCorrect(storedProcedureName))
                {
                    return 0;
                }

                using (var connection = LiveConnection(dbName))
                {
                    try
                    {
                        return connection.Execute(
                            sql: storedProcedureName,
                            param: model,
                            commandTimeout: null,
                            commandType: CommandType.StoredProcedure
                            );

                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        CloseConnection(connection);
                    }
                }
            }

            public async Task<long> ExecuteAsync<TModel>(string storedProcedureName, TModel model, string dbName)
            {
                if (!IsStoredProcedureNameCorrect(storedProcedureName))
                {
                    return 0;
                }

                using (var connection = LiveConnection(dbName))
                {
                    try
                    {
                        return await connection.ExecuteAsync(
                            sql: storedProcedureName,
                            param: model,
                            commandTimeout: null,
                            commandType: CommandType.StoredProcedure
                            );

                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        CloseConnection(connection);
                    }
                }
            }

            /// <summary>
            /// This method is used to execute the stored procedures with parameter. This is the generic version of the method.
            /// </summary>
            /// <param name="storedProcedureName">Stored Procedure's name. Expected to be a Verbatim String, e.g. @"[Schema].[Stored-Procedure-Name]"</param>
            /// <param name="parameters">Parameter required for executing Stored Procedure.</param>        
            /// <returns>Returns how many rows have been affected.</returns>

            public long Execute(string storedProcedureName, DynamicParameters parameters, string dbName)
            {
                if (!IsStoredProcedureNameCorrect(storedProcedureName))
                {
                    return 0;
                }

                using (var connection = LiveConnection(dbName))
                {
                    try
                    {
                        return connection.Execute(
                            sql: storedProcedureName,
                            param: parameters,
                            commandTimeout: null,
                            commandType: CommandType.StoredProcedure
                            );
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        CloseConnection(connection);
                    }
                }
            }



            public async Task<long> ExecuteAsync(string storedProcedureName, DynamicParameters parameters, string dbName)
            {
                if (!IsStoredProcedureNameCorrect(storedProcedureName))
                {
                    return 0;
                }

                using (var connection = LiveConnection(dbName))
                {
                    try
                    {
                        return await connection.ExecuteAsync(
                            sql: storedProcedureName,
                            param: parameters,
                            commandTimeout: null,
                            commandType: CommandType.StoredProcedure
                            );

                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        CloseConnection(connection);
                    }
                }
            }

    }

Agora você pode ligar do modelo conforme sua necessidade:

public class DeviceDriverModel : Base
    {
 public class DeviceDriverSaveUpdate
        {
            public string DeviceVehicleId { get; set; }
            public string DeviceId { get; set; }
            public string DriverId { get; set; }
            public string PhoneNo { get; set; }
            public bool IsActive { get; set; }
            public string UserId { get; set; }
            public string HostIP { get; set; }
        }


        public Task<long> DeviceDriver_SaveUpdate(DeviceDriverSaveUpdate obj)
        {

            return DatabaseHub.ExecuteAsync(
                    storedProcedureName: "[dbo].[sp_SaveUpdate_DeviceDriver]", model: obj, dbName: AMSDB);//Database name defined in Base Class.
        }
}

Você também pode passar parâmetros:

public Task<long> DeleteFuelPriceEntryByID(string FuelPriceId, string UserId)
        {


            var parameters = new DynamicParameters();
            parameters.Add(name: "@FuelPriceId", value: FuelPriceId, dbType: DbType.Int32, direction: ParameterDirection.Input);
            parameters.Add(name: "@UserId", value: UserId, dbType: DbType.String, direction: ParameterDirection.Input);

            return DatabaseHub.ExecuteAsync(
                    storedProcedureName: @"[dbo].[sp_Delete_FuelPriceEntryByID]", parameters: parameters, dbName: AMSDB);

        }

Agora ligue de seus controladores:

var queryData = new DeviceDriverModel().DeviceInfo_Save(obj);

Espero que isso impeça a repetição do código e forneça segurança;

Majedur Rahaman
fonte
1

Você pode tentar isso:

 string sql = "UPDATE Customer SET City = @City WHERE CustomerId = @CustomerId";             
 conn.Execute(sql, customerEntity);
Luiz Fernando Corrêa Leite
fonte