apperextensions:添加"insert ..更新重复的键

本文关键字:更新 insert 添加 quot apperextensions | 更新日期: 2023-09-27 17:54:30

现在我正在使用Dapper + Dapper. extensions。是的,这很简单,也很棒。但我遇到了一个问题:戴珀。扩展只有插入命令和没有InsertUpdateOnDUplicateKey。我想添加这样的方法,但我不知道怎么做:

  1. 我想让这个方法像Insert
  2. 一样通用
  3. 我不能得到特定类型的属性缓存列表,因为我不想使用反射直接构建原始sql

可能的方式在这里叉它在github上,但我想使它在我的项目只。有人知道怎么扩展它吗?我理解这个特性("插入……update on duplicate key")仅在MySQL中支持。但是我找不到在DapperExtensions中添加此功能的扩展点。
更新:这是我的分叉https://github.com/MaximTkachenko/Dapper-Extensions/commits/master

apperextensions:添加"insert ..更新重复的键

这段代码在MySQL相关项目中给了我很大的帮助,我欠你一个人情。

我在MySQL和MS SQL上做了很多与数据库相关的开发。我还尝试在我的项目之间共享尽可能多的代码。

MS SQL没有"ON DUPLICATE KEY UPDATE"的直接等效,所以我以前在使用MS SQL时无法使用此扩展。

在迁移一个web应用程序(严重依赖于这个Dapper)时。扩展调整)从MySQL到MS SQL,我终于决定做点什么。

这段代码使用了"IF EXISTS => UPDATE ELSE INSERT"方法,基本上与MySQL上的"ON DUPLICATE KEY UPDATE"方法相同。

请注意:该代码片段假设您正在处理该方法之外的事务。或者,您可以在生成的sql字符串的开头添加"BEGIN TRAN",在末尾添加"COMMIT"。

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

实际上我关闭了我的拉请求并删除了我的叉子,因为:

  1. 我看到2014年创建了一些开放的拉取请求
  2. 我找到了一种方法我的代码在Dapper.Extensions.

我提醒我的问题:我想为Dapper.Extensions创建更多的通用查询。这意味着我需要访问实体的映射缓存,SqlGenerator等。这就是我的方法。我想添加能力,使INSERT ..更新MySQL的重复键。我为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实例,因为DapperImplementor实例从基类是私有的:(。所以现在我可以使用我的Db类从Dapper.Extension调用我自己的通用sql查询和本机查询。可以在这里找到使用Database类代替IDbConnection扩展的示例。