DapperExtensions : "삽입 ... 중복 키에 업데이트"추가

c# dapper dapper-extensions orm

문제

이제 Dapper + Dapper.Extensions를 사용하고 있습니다. 그리고 네, 쉽고 굉장합니다. 그러나 나는이 문제에 직면 : Dapper.Extensions는 명령하지 InsertUpdateOnDUplicateKey를 삽입했다. 나는 그런 방법을 추가하고 싶지만 나는 그것을 할 좋은 방법을 보지 못한다.

  1. Insert와 같이이 메서드를 일반화하고 싶습니다.
  2. 원시 SQL을 빌드하기 위해 리플렉션을 직접 사용하고 싶지 않기 때문에 특정 유형의 캐시 된 목록을 가져올 수 없습니다.

가능한 방법은 github에 포크 여기지만, 내 프로젝트에만 그것을 만들고 싶어. 아무도 그것을 연장하는 방법을 알고 있습니까? 나는이 기능 ( "duplicate key에 insert ... update")이 MySQL에서만 지원된다는 것을 알고있다. 그러나 DapperExtensions에서이 기능을 외부에 추가하기 위해 확장 점을 찾을 수 없습니다.
업데이트 : 이것은 내 포크입니다 https://github.com/MaximTkachenko/Dapper-Extensions/commits/master

수락 된 답변

사실 나는 끌어 오기 요구를 닫고 내 포크를 제거하기 때문에 :

  1. 2014 년에 생성 된 일부 공개 요청을 봅니다.
  2. Dapper.Extensions에 코드를 삽입하는 방법을 발견했습니다.

나는 나의 문제를 상기시킨다 : Dapper.Extensions에 대한 더 일반적인 쿼리를 만들고 싶다. 그것은 엔티티, SqlGenerator 등을위한 매핑 캐시에 대한 액세스 권한이 필요하다는 것을 의미합니다. 그래서 여기에 내 방식이 있습니다. MySQL에 대해 INSERT ... DUPLICATE KEY업데이트 하는 기능을 추가하고 싶습니다. ISqlGenerator에 대한 확장 메서드를 만들었습니다.

   public static class SqlGeneratorExt
    {
        public static string InsertUpdateOnDuplicateKey(this ISqlGenerator generator, IClassMapper classMap)
        {
            var columns = classMap.Properties.Where(p => !(p.Ignored || p.IsReadOnly || p.KeyType == KeyType.Identity));
            if (!columns.Any())
            {
                throw new ArgumentException("No columns were mapped.");
            }

            var columnNames = columns.Select(p => generator.GetColumnName(classMap, p, false));
            var parameters = columns.Select(p => generator.Configuration.Dialect.ParameterPrefix + p.Name);
            var valuesSetters = columns.Select(p => string.Format("{0}=VALUES({1})", generator.GetColumnName(classMap, p, false), p.Name));

            string sql = string.Format("INSERT INTO {0} ({1}) VALUES ({2}) ON DUPLICATE KEY UPDATE {3}",
                                       generator.GetTableName(classMap),
                                       columnNames.AppendStrings(),
                                       parameters.AppendStrings(),
                                       valuesSetters.AppendStrings());

            return sql;
        }
    }

IDapperImplementor에 대한 하나의 확장 메소드

public static class DapperImplementorExt
{
    public static void InsertUpdateOnDuplicateKey<T>(this IDapperImplementor implementor, IDbConnection connection, IEnumerable<T> entities, int? commandTimeout = null) where T : class
    {
        IClassMapper classMap = implementor.SqlGenerator.Configuration.GetMap<T>();
        var properties = classMap.Properties.Where(p => p.KeyType != KeyType.NotAKey);
        string emptyGuidString = Guid.Empty.ToString();

        foreach (var e in entities)
        {
            foreach (var column in properties)
            {
                if (column.KeyType == KeyType.Guid)
                {
                    object value = column.PropertyInfo.GetValue(e, null);
                    string stringValue = value.ToString();
                    if (!string.IsNullOrEmpty(stringValue) && stringValue != emptyGuidString)
                    {
                        continue;
                    }

                    Guid comb = implementor.SqlGenerator.Configuration.GetNextGuid();
                    column.PropertyInfo.SetValue(e, comb, null);
                }
            }
        }

        string sql = implementor.SqlGenerator.InsertUpdateOnDuplicateKey(classMap);

        connection.Execute(sql, entities, null, commandTimeout, CommandType.Text);
    }
}

이제 데이터베이스 클래스에서 파생 된 새로운 클래스를 만들어 내 자신의 SQL을 사용할 수 있습니다.

public class Db : Database
{
    private readonly IDapperImplementor _dapperIml;

    public Db(IDbConnection connection, ISqlGenerator sqlGenerator) : base(connection, sqlGenerator)
    {
        _dapperIml = new DapperImplementor(sqlGenerator);
    }

    public void InsertUpdateOnDuplicateKey<T>(IEnumerable<T> entities, int? commandTimeout) where T : class
    {
        _dapperIml.InsertUpdateOnDuplicateKey(Connection, entities, commandTimeout);
    }
}

예, 기본 클래스의 DapperImplementor 인스턴스가 private이기 때문에 다른 DapperImplementor 인스턴스를 만들어야합니다. (이제 Db 클래스를 사용하여 Dapper.Extension에서 제네릭 SQL 쿼리와 기본 쿼리를 호출 할 수 있습니다. IDbConnection 확장은 여기 에서 찾을 수 있습니다 .


인기 답변

이 코드는 MySQL 관련 프로젝트에서 엄청난 도움을주었습니다.

MySQL과 MS SQL 모두에서 데이터베이스 관련 개발을 많이합니다. 나는 또한 내 프로젝트간에 최대한 많은 코드를 공유하려고한다.

MS SQL에는 "ON DUPLICATE KEY UPDATE"에 대한 직접적인 기능이 없기 때문에 이전에 MS SQL을 사용하여이 확장을 사용할 수 없었습니다.

MySQL에서 MS SQL로 웹 응용 프로그램 (이 Dapper.Extensions 조정에 많이 의존하는)을 마이그레이션하는 동안 마침내 그것에 대해 뭔가를하기로 결정했습니다.

이 코드는 "IF EXISTS => UPDATE ELSE INSERT"접근법을 사용하는데, 기본적으로 MySQL의 "ON DUPLICATE KEY UPDATE"와 동일합니다.

참고 : 스 니펫은이 방법 외의 거래를 처리한다고 가정합니다. 또는 "BEGIN TRAN"을 처음에 추가하고 "COMMIT"을 생성 된 SQL 문자열 끝에 추가 할 수 있습니다.

public static class SqlGeneratorExt
{
    public static string InsertUpdateOnDuplicateKey(this ISqlGenerator generator, IClassMapper classMap, bool hasIdentityKeyWithValue = false)
    {
        var columns = classMap.Properties.Where(p => !(p.Ignored || p.IsReadOnly || (p.KeyType == KeyType.Identity && !hasIdentityKeyWithValue))).ToList();
        var keys = columns.Where(c => c.KeyType != KeyType.NotAKey).Select(p => $"{generator.GetColumnName(classMap, p, false)}=@{p.Name}");
        var nonkeycolumns = classMap.Properties.Where(p => !(p.Ignored || p.IsReadOnly) && p.KeyType == KeyType.NotAKey).ToList();
        if (!columns.Any())
        {
            throw new ArgumentException("No columns were mapped.");
        }
        var tablename = generator.GetTableName(classMap);
        var columnNames = columns.Select(p => generator.GetColumnName(classMap, p, false));
        var parameters = columns.Select(p => generator.Configuration.Dialect.ParameterPrefix + p.Name);
        var valuesSetters = nonkeycolumns.Select(p => $"{generator.GetColumnName(classMap, p, false)}=@{p.Name}").ToList();
        var where = keys.AppendStrings(seperator: " and ");
        var sqlbuilder = new StringBuilder();
        sqlbuilder.AppendLine($"IF EXISTS (select * from {tablename} WITH (UPDLOCK, HOLDLOCK) WHERE ({where})) ");
        sqlbuilder.AppendLine(valuesSetters.Any() ? $"UPDATE {tablename} SET {valuesSetters.AppendStrings()} WHERE ({where}) " : "SELECT 0 ");
        sqlbuilder.AppendLine($"ELSE INSERT INTO {tablename} ({columnNames.AppendStrings()}) VALUES ({parameters.AppendStrings()}) ");
        return sqlbuilder.ToString();
    }
}


아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow