Realizar inserciones y actualizaciones con Dapper

194

Estoy interesado en usar Dapper, pero por lo que puedo decir, solo admite Query and Execute. No veo que Dapper incluya una forma de Insertar y Actualizar objetos.

Dado que nuestro proyecto (¿la mayoría de los proyectos?) Necesita hacer inserciones y actualizaciones, ¿cuál es la mejor práctica para hacer Inserciones y Actualizaciones junto con dapper?

Preferiblemente no tendríamos que recurrir al método ADO.NET de construcción de parámetros, etc.

La mejor respuesta que puedo encontrar en este momento es usar LinqToSQL para inserciones y actualizaciones. ¿Hay una mejor respuesta?

Slaggg
fuente
3
Existe esta extensión de Contrib del propio Dapper.NET que uso. github.com/StackExchange/dapper-dot-net/tree/master/…
Rajiv

Respuestas:

201

Estamos buscando construir algunos ayudantes, todavía decidimos sobre las API y si esto va en el núcleo o no. Consulte: https://code.google.com/archive/p/dapper-dot-net/issues/6 para ver el progreso.

Mientras tanto, puedes hacer lo siguiente

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

etcétera

Ver también mi blog: ese molesto problema de INSERT

Actualizar

Como se señaló en los comentarios, ahora hay varias extensiones disponibles en el proyecto Dapper.Contrib en forma de estos IDbConnectionmétodos de extensión:

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 Azafrán
fuente
44
Hola Sam, encontré tu respuesta SO con google y me preguntaba si la última línea de código debería incluir la palabra setcomo cnn.Execute("update Table SET val = @val where Id = @id", new {val, id = 1});o es específica. Soy nuevo en Dapper y estaba buscando un ejemplo de actualización :)
JP Hellemons
1
@JPHellemons Intenté esto var updateCat = connection.Execute("UPDATE tCategories SET sCategory = @val WHERE iCategoryID = @id", new { val = "dapper test", id = 23 });y funcionó. Sin el uso de SET obtengo un error de sintaxis SQLException cerca de sCategory.
Caro
3
Avance rápido hasta diciembre de 2015: github.com/StackExchange/dapper-dot-net/tree/master/…
Rosdi Kasim
3
@RosdiKasim ¿Esto no vence el propósito de usar Dapper? Quiero usar SQL Esto lo resume. ¿Qué me estoy perdiendo?
Johnny
2
@johnny Es solo clase auxiliar ... algunas personas quieren que su código sea lo más breve posible ... no tiene que usarlo si no lo desea.
Rosdi Kasim
67

Realizar operaciones CRUD con Dapper es una tarea fácil. He mencionado los siguientes ejemplos que deberían ayudarlo en las operaciones CRUD.

Código para C RUD:

Método n. ° 1: este método se utiliza cuando inserta 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 se usa cuando las propiedades de su entidad tienen los mismos nombres que las columnas SQL. Entonces, Dapper es un ORM que asigna propiedades de entidad con las columnas SQL coincidentes.

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
fuente
26

puedes hacerlo de esta manera:

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
fuente
36
Debe usar using-statementpara que la conexión se cierre incluso en caso de una excepción.
Tim Schmelter
12
simplemente podría pasar customerEntity directamente en lugar de usar un tipo anónimo ...
Thomas Levesque
@ThomasLevesque ¿Qué quieres decir con eso? ¿Puedes proporcionar un pequeño código de ejemplo de lo que quieres decir?
iaacp
44
@iaacp, quiero decir que:sqlConnection.Execute(sqlQuery, customerEntity);
Thomas Levesque
1
@ThomasLevesque, ¿podemos actualizar también con el mismo patrón? es decir,sqlConnection.Execute(sqlQuery, customerEntity);
Shankar
16

Usando Dapper.Contrib es tan simple como esto:

Insertar lista:

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

Insertar single:

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

Lista de actualizacion:

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

Actualizar solo:

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

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

Ogglas
fuente
1
Usando lo anterior para insertar un solo objeto, puede recuperar el nuevo número de identidad y volver a colocarlo en su modelo ... Pero, ¿cómo lo hace para insertar una lista de objetos? Los objetos en la lista no tienen el campo de identidad ¿Tiene que recorrer la lista y luego insertarlos uno a la vez, obteniendo el nuevo ID cada vez?
Harag
1
@harag Si necesita la nueva ID en otro lugar, supongo que debe hacerlo así. Entity Framework maneja los tipos de referencia, como las clases, sin problemas con las inserciones, pero no sé cómo funciona Dapper.Contrib si ese fuera su ángulo.
Ogglas
55
@Ogglas, gracias. He notado que "connection.Insert (myObject)" actualizará la propiedad "[clave]" del "myObject" si solo estoy insertando un objeto, pero si estoy insertando una lista de 5 objetos usando el mismo "connection.Insert (myObjectList)", entonces ninguna de las propiedades [claves] se actualiza, por lo que tengo que hacer manualmente cada elemento de la lista e insertarlos uno por uno.
Harag
1
En conn.Update(yourClass)caso de algunas propiedades son nulos , a continuación, actualizar los campos en NULL ? No funciona. Actualizar campo a NULL . Not partials updates
Kiquenet
5

También puede usar apuesto con un procedimiento almacenado y una forma genérica por la cual todo es fácilmente manejable.

Define tu conexión:

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

Cree una interfaz para definir los métodos Dapper que realmente necesita:

 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 la interfaz:

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

    }

Ahora puede llamar desde el modelo según lo necesite:

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

También puede pasar parámetros también:

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

        }

Ahora llame desde sus controladores:

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

Espero que evite la repetición de su código y brinde seguridad;

Majedur Rahaman
fuente
1

Puedes probar esto:

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