Dapper로 삽입 및 업데이트 수행


195

Dapper 사용에 관심이 있지만 쿼리 및 실행 만 지원한다는 것을 알 수 있습니다. Dapper에 객체 삽입 및 업데이트 방법이 포함되어 있지 않습니다.

프로젝트 (대부분의 프로젝트)가 삽입 및 업데이트를 수행해야한다면 dapper와 함께 삽입 및 업데이트를 수행하는 가장 좋은 방법은 무엇입니까?

바람직하게는 매개 변수 작성 등의 ADO.NET 방법에 의존 할 필요가 없습니다.

이 시점에서 내가 취할 수있는 가장 좋은 대답은 삽입 및 업데이트에 LinqToSQL을 사용하는 것입니다. 더 나은 답변이 있습니까?


3
내가 사용하는 Dapper.NET 자체 에서이 Contrib 확장이 있습니다. github.com/StackExchange/dapper-dot-net/tree/master/…
Rajiv

답변:


201

우리는 몇 가지 헬퍼를 빌드하면서 API를 결정하고 이것이 핵심인지 아닌지를 결정하고 있습니다. 진행 방법 은 https://code.google.com/archive/p/dapper-dot-net/issues/6 를 참조하십시오 .

그 동안 다음을 수행 할 수 있습니다.

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

내 블로그 게시물 참조 : 성가신 삽입 문제

최신 정보

주석에서 지적했듯이 이제 Dapper.Contrib 프로젝트에서 다음 IDbConnection확장 방법 의 형태로 사용 가능한 몇 가지 확장이 있습니다 .

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

4
안녕하세요 샘, 구글과의 SO 응답을 발견하고 코드의 마지막 줄이 단어를 포함해야한다 궁금 해서요 set등을 cnn.Execute("update Table SET val = @val where Id = @id", new {val, id = 1});하거나 말끔의 특정은? 나는
dapper를 처음 접했고

1
@ JPHellemons 나는 이것을 시도했고 var updateCat = connection.Execute("UPDATE tCategories SET sCategory = @val WHERE iCategoryID = @id", new { val = "dapper test", id = 23 });효과가있었습니다. SET를 사용하지 않으면 sCategory 근처에서 SQLException 구문 오류가 발생합니다.
Pricey


3
@RosdiKasim Dapper 사용의 목적을 무효화하지 않습니까? SQL을 사용하고 싶습니다. 이것은 그것을 추상화합니다. 내가 무엇을 놓치고 있습니까?
johnny

2
@johnny 그것은 단지 도우미 클래스입니다 ... 어떤 사람들은 가능한 한 간결하게 코드를 원합니다 ... 원치 않으면 그것을 사용할 필요가 없습니다.
Rosdi Kasim 2012 년

68

Dapper를 사용하여 CRUD 작업을 수행하는 것은 쉬운 작업입니다. CRUD 작업에 도움이되는 아래 예제를 언급했습니다.

C RUD 코드 :

방법 # 1 : 이 방법은 다른 엔터티에서 값을 삽입 할 때 사용됩니다.

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

방법 # 2 : 이 방법은 엔티티 속성의 이름이 SQL 열과 동일한 경우에 사용됩니다. 따라서 ORM 인 Dapper는 엔티티 특성을 일치하는 SQL 열과 맵핑합니다.

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

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

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

26

당신은 그런 식으로 할 수 있습니다 :

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

36
using-statement예외가 발생하더라도 연결이 닫히도록 사용해야합니다 .
Tim Schmelter

12
당신은 ... 대신 익명 형식을 사용하는 직접 customerEntity를 전달할 수
토마스 레베

@ThomasLevesque 그게 무슨 뜻입니까? 당신이 의미하는 작은 코드 예제를 제공 할 수 있습니까?
iaacp

4
@iaacp, 나는 다음을 의미한다 :sqlConnection.Execute(sqlQuery, customerEntity);
Thomas Levesque

1
@ThomasLevesque 같은 패턴으로도 업데이트 할 수 있습니까? 즉,sqlConnection.Execute(sqlQuery, customerEntity);

16

Dapper.Contrib를 사용하면 다음 과 같이 간단합니다.

삽입 목록 :

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

단일 삽입 :

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

업데이트 목록 :

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

싱글 업데이트 :

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

출처 : https://github.com/StackExchange/Dapper/tree/master/Dapper.Contrib


1
위의 방법을 사용하여 단일 객체를 삽입하면 새 ID 번호를 다시 가져 와서 모델에 다시 넣을 수 있습니다 ...하지만 객체 목록을 삽입하기 위해 어떻게해야합니까-목록의 객체에는 신원 필드. 목록을 반복 한 다음 한 번에 하나씩 삽입하여 매번 새 ID를 가져와야합니까?
Harag

1
@harag 만약 당신이 다른 곳에서 새로운 ID를 필요로한다면 그렇게해야 할 것 같습니다. Entity Framework는 삽입과 관련된 문제없이 클래스와 같은 참조 유형을 처리하지만 Dapper.Contrib가 각도에 따라 작동하는 방식을 모르겠습니다.
Ogglas

5
@ 오 글라스, 감사합니다. 객체를 하나만 삽입하는 경우 "connection.Insert (myObject)"는 "myObject"의 "[key]"속성을 업데이트하지만 동일한 객체를 사용하여 5 개의 객체 목록을 삽입하는 경우 "connection.Insert (myObjectList)"다음 [keys] 속성이 업데이트되지 않으므로 목록의 각 항목을 수동으로 수행하고 한 번에 하나씩 삽입해야합니다.
Harag

1
에서 conn.Update(yourClass)일부 속성이있는 경우 이다 널 (null) , 다음 NULL로 필드를 업데이트 ? 작동하지 않는. 필드를 NULL로 업데이트하십시오 . Not partials updates
Kiquenet

5

또한 저장 프로 시저 및 모든 것을 쉽게 관리 할 수있는 일반적인 방법으로 dapper를 사용할 수 있습니다.

연결을 정의하십시오.

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

실제로 필요한 Dapper 메소드를 정의하기위한 인터페이스를 작성하십시오.

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

인터페이스를 구현하십시오.

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

    }

이제 필요에 따라 모델에서 전화를 걸 수 있습니다.

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

매개 변수도 전달할 수 있습니다.

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

        }

이제 컨트롤러에서 전화하십시오.

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

코드 반복을 방지하고 보안을 제공하기를 바랍니다.


1

당신은 이것을 시도 할 수 있습니다 :

 string sql = "UPDATE Customer SET City = @City WHERE CustomerId = @CustomerId";             
 conn.Execute(sql, customerEntity);

0

쿼리 작업에 타사 라이브러리를 사용하는 대신 직접 쿼리를 작성하는 것이 좋습니다. 다른 타사 패키지를 사용하면 dapper 즉 유연성있는 쿼리 작성의 주요 이점이 없어지기 때문입니다.

이제 전체 개체에 대해 Insert 또는 Update 쿼리를 작성하는 데 문제가 있습니다. 이를 위해 아래처럼 간단하게 도우미를 만들 수 있습니다.

InsertQueryBuilder :

 public static string InsertQueryBuilder(IEnumerable < string > fields) {


  StringBuilder columns = new StringBuilder();
  StringBuilder values = new StringBuilder();


  foreach(string columnName in fields) {
   columns.Append($ "{columnName}, ");
   values.Append($ "@{columnName}, ");

  }
  string insertQuery = $ "({ columns.ToString().TrimEnd(',', ' ')}) VALUES ({ values.ToString().TrimEnd(',', ' ')}) ";

  return insertQuery;
 }

이제 삽입 할 열 이름을 전달하면 아래와 같이 전체 쿼리가 자동으로 생성됩니다.

List < string > columns = new List < string > {
 "UserName",
 "City"
}
//QueryBuilder is the class having the InsertQueryBuilder()
string insertQueryValues = QueryBuilderUtil.InsertQueryBuilder(columns);

string insertQuery = $ "INSERT INTO UserDetails {insertQueryValues} RETURNING UserId";

Guid insertedId = await _connection.ExecuteScalarAsync < Guid > (insertQuery, userObj);

TableName 매개 변수를 전달하여 전체 INSERT 문을 리턴하도록 함수를 수정할 수도 있습니다.

클래스 특성 이름이 데이터베이스의 필드 이름과 일치하는지 확인하십시오. 그런 다음 전체 obj (이 경우 userObj와 같은)를 전달하면 값이 자동으로 매핑됩니다.

같은 방식으로 UPDATE 쿼리에 대한 도우미 함수를 가질 수도 있습니다.

  public static string UpdateQueryBuilder(List < string > fields) {
   StringBuilder updateQueryBuilder = new StringBuilder();

   foreach(string columnName in fields) {
    updateQueryBuilder.AppendFormat("{0}=@{0}, ", columnName);
   }
   return updateQueryBuilder.ToString().TrimEnd(',', ' ');
  }

그리고 그것을 다음과 같이 사용하십시오 :

List < string > columns = new List < string > {
 "UserName",
 "City"
}
//QueryBuilder is the class having the UpdateQueryBuilder()
string updateQueryValues = QueryBuilderUtil.UpdateQueryBuilder(columns);

string updateQuery =  $"UPDATE UserDetails SET {updateQueryValues} WHERE UserId=@UserId";

await _connection.ExecuteAsync(updateQuery, userObj);

이러한 도우미 함수에서도 삽입 또는 업데이트하려는 필드의 이름을 전달해야하지만 최소한 쿼리를 완전히 제어 할 수 있으며 필요할 때 다른 WHERE 절을 포함 할 수도 있습니다.

이 도우미 기능을 통해 다음 코드 줄을 저장합니다.

쿼리 삽입의 경우 :

 $ "INSERT INTO UserDetails (UserName,City) VALUES (@UserName,@City) RETURNING UserId";

업데이트 쿼리의 경우 :

$"UPDATE UserDetails SET UserName=@UserName, City=@City WHERE UserId=@UserId";

몇 줄의 코드 차이가있는 것처럼 보이지만 10 개가 넘는 필드가있는 테이블에서 삽입 또는 업데이트 작업을 수행 할 때 차이를 느낄 수 있습니다.

nameof 연산자를 사용하여 오타를 피하기 위해 함수에서 필드 이름을 전달할 수 있습니다.

대신에:

List < string > columns = new List < string > {
 "UserName",
 "City"
}

당신은 쓸 수 있습니다:

List < string > columns = new List < string > {
nameof(UserEntity.UserName),
nameof(UserEntity.City),
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.