Dapper ile Ekleme ve Güncelleme Yapma


195

Ben Dapper kullanmakla ilgileniyorum - ama ne söyleyebilirim sadece Sorgu ve Yürütmeyi destekler. Dapper'ın nesneleri Ekleme ve Güncelleme yöntemini içerdiğini görmüyorum.

Projemizin (çoğu proje?) Kesici uçlar ve güncellemeler yapması gerektiği göz önüne alındığında, dapper'ın yanında Eklemeler ve Güncellemeler yapmak için en iyi uygulama nedir?

Tercihen, parametre oluşturma vb. İçin ADO.NET yöntemine başvurmamız gerekmez.

Bu noktada karşılaşabileceğim en iyi cevap, ekler ve güncellemeler için LinqToSQL kullanmaktır. Daha iyi bir cevap var mı?


3
Kullandığım Dapper.NET'in kendisinden bu Katkıda Bulunan uzantı var. github.com/StackExchange/dapper-dot-net/tree/master/…
Rajiv

Yanıtlar:


201

Birkaç yardımcı oluşturmaya, hala API'lara karar vermeye ve bunun temelde olup olmayacağına bakıyoruz. İlerleme için bkz . Https://code.google.com/archive/p/dapper-dot-net/issues/6 .

Bu arada aşağıdakileri yapabilirsiniz

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

ve benzeri

Ayrıca blog gönderime bakın: Bu sinir bozucu INSERT sorunu

Güncelleme

Yorumlarda belirtildiği gibi, Dapper.Contrib projesinde şu IDbConnectionuzantı yöntemleri şeklinde birkaç uzantı bulunmaktadır :

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
Merhaba Sam, google ile SO cevabını buldum ve kodunun son satırı kelimesini içermelidir acaba setolarak cnn.Execute("update Table SET val = @val where Id = @id", new {val, id = 1});veya bu şık özgüdür? Dapper'da yeniyim ve bir güncelleme örneği arıyordum :)
JP Hellemons

1
@JPHellemons Bunu denedim var updateCat = connection.Execute("UPDATE tCategories SET sCategory = @val WHERE iCategoryID = @id", new { val = "dapper test", id = 23 });ve işe yaradı. SET kullanımı olmadan sCategory yakınında bir SQLException sözdizimi hatası alıyorum.
Pahalı

3
Aralık 2015'e kadar hızlı ilerleyin: github.com/StackExchange/dapper-dot-net/tree/master/…
Rosdi Kasim

3
@RosdiKasim Bu Dapper'ı kullanma amacını bozmaz mı? SQL kullanmak istiyorum. Bu onu soyutlar. Neyi kaçırıyorum?
johnny

2
@johnny Bu sadece yardımcı bir sınıf ... bazı insanlar kodlarını olabildiğince özlü istiyorlar ... istemiyorsanız kullanmak zorunda değilsiniz.
Rosdi Kasim

68

Dapper kullanarak CRUD işlemlerini gerçekleştirmek kolay bir iştir. CRUD operasyonlarında size yardımcı olacak aşağıdaki örneklerden bahsetmiştim.

C RUD kodu :

Yöntem # 1: Bu yöntem, farklı varlıklardan değerler eklerken kullanılır.

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

Yöntem # 2: Bu yöntem, varlık özellikleriniz SQL sütunlarıyla aynı ada sahip olduğunda kullanılır. Bu nedenle, Dapper bir ORM olmak, varlık özelliklerini eşleşen SQL sütunlarıyla eşler.

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 kodu :

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 için kod :

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 kodu :

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

bunu şu şekilde yapabilirsiniz:

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İstisna durumunda bile bağlantının kapanması için kullanmalısınız .
Tim Schmelter

12
anonim bir tür kullanmak yerine doğrudan customerEntity'yi geçebilirsiniz ...
Thomas Levesque

@ThomasLevesque Bununla ne demek istiyorsun? Ne demek istediğinize dair küçük bir kod örneği verebilir misiniz?
iaacp

4
@iaacp, demek istediğim:sqlConnection.Execute(sqlQuery, customerEntity);
Thomas Levesque

1
@ThomasLevesque aynı modeli kullanarak güncelleme yapabilir miyiz? yani,sqlConnection.Execute(sqlQuery, customerEntity);
Shankar

16

Dapper.Contrib kullanmak bu kadar basit:

Liste ekle:

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

Tek ekle:

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

Güncelleme listesi:

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

Tek güncelleme:

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

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


1
Tek bir nesne eklemek için yukarıdakileri kullanarak yeni kimlik numarasını geri alabilir ve modelinize geri koyabilirsiniz ... Ancak bir nesne listesi eklemek için bunu nasıl yaparsınız - listedeki nesneler kimlik alanı. Listeyi yinelemeniz ve her seferinde bir tane eklemeniz ve her seferinde yeni kimliği almanız gerekiyor mu?
Harag

1
@harag Eğer başka bir yerde yeni bir kimliğe ihtiyacınız varsa bunu böyle yapmak zorunda sanırım. Entity Framework ekler ile bir sorun olmadan sınıflar gibi başvuru türlerini işler, ancak sizin açınız buysa Dapper.Contrib bununla nasıl çalıştığını bilmiyorum.
Ogglas

5
@Ogglas, teşekkürler. "Bağlantı.Insert (myObject)", sadece bir nesne ekliyorsam "myObject" in "[key]" özelliğini güncelleyeceğini fark ettim, ancak aynı nesneyi kullanarak 5 nesnenin bir listesini ekliyorsam "connection.Insert (myObjectList)" sonra [keys] özelliklerinden hiçbiri güncellenmez, bu yüzden listedeki her öğeyi el ile yapmak ve her seferinde bir tane eklemek zorundayım.
Harag

1
Gelen conn.Update(yourClass)bazı özellikler ise şunlardır boş , ardından NULL'A alanları GÜNCELLEME ? Çalışmıyor. Alanı NULL olarak güncelleyin . Not partials updates
Kiquenet

5

Veritabanını saklı bir prosedürle ve her şeyin kolayca yönetilebileceği genel bir şekilde kullanabilirsiniz.

Bağlantınızı tanımlayı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();
    }
}

Aslında ihtiyacınız olan Dapper yöntemlerini tanımlamak için bir arayüz oluşturun:

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

Arayüzü uygulayın:

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

    }

Artık ihtiyaç duyduğunuzda modelden arayabilirsiniz:

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

Ayrıca parametreleri de iletebilirsiniz:

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

        }

Şimdi kontrolörlerinizden arayın:

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

Umarım kod tekrarını önler ve güvenlik sağlar;


1

Bunu deneyebilirsiniz:

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

0

Sorgu işlemleri için herhangi bir üçüncü taraf kitaplığı kullanmak yerine, kendi kendinize sorgu yazmayı öneririm. Çünkü başka herhangi bir üçüncü taraf paketi kullanmak, dapper kullanmanın temel avantajını yani sorgu yazma esnekliğini ortadan kaldıracaktır.

Şimdi, nesnenin tamamı için Ekle veya Güncelle sorgusu yazma ile ilgili bir sorun var. Bunun için sadece aşağıdaki gibi yardımcılar oluşturulabilir:

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

Şimdi, eklenecek sütunların adını geçirerek, aşağıdaki gibi tüm sorgu otomatik olarak oluşturulacaktır:

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 parametresini ileterek INSERT deyiminin tamamını döndürecek işlevi de değiştirebilirsiniz.

Class özellik adlarının veritabanındaki alan adlarıyla eşleştiğinden emin olun. O zaman sadece tüm nesneyi (bizim durumumuzda userObj gibi) geçebilirsiniz ve değerler otomatik olarak eşlenir.

Aynı şekilde, UPDATE sorgusu için de yardımcı işleve sahip olabilirsiniz:

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

Ve şöyle kullanın:

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

Bu yardımcı işlevlerde de, eklemek veya güncellemek istediğiniz alanların adını iletmeniz gerekir, ancak en azından sorgu üzerinde tam kontrole sahip olursunuz ve gerektiğinde farklı WHERE cümleleri de ekleyebilirsiniz.

Bu yardımcı işlevler aracılığıyla aşağıdaki kod satırlarını kaydedersiniz:

Sorgu Ekle için:

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

Güncelleme Sorgusu için:

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

Birkaç kod satırının farkı var gibi görünüyor, ancak 10'dan fazla alana sahip bir tabloyla ekleme veya güncelleme işlemi söz konusu olduğunda, fark hissedilebilir.

Yazım hatalarını önlemek için işlevdeki alan adını iletmek için nameof operatörünü kullanabilirsiniz.

Onun yerine:

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

Yazabilirsin:

List < string > columns = new List < string > {
nameof(UserEntity.UserName),
nameof(UserEntity.City),
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.