You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SqlBulkTools泛型方法动态指定MatchTargetOn匹配列的实现问询

解决方案

根因说明

SqlBulkTools的MatchTargetOn方法要求传入Expression<Func<T, object>>类型的属性选择表达式,框架内部会直接解析表达式树提取属性名生成SQL匹配条件。你之前直接传入x => x.GetMatchOn()的方法调用表达式,框架无法解析为具体的表字段,因此抛出异常。

方案1:基于现有IModel接口动态构建表达式树

你只需要在泛型方法中,根据GetMatchOn返回的PropertyInfo手动构造符合要求的属性选择表达式即可,修改后的泛型方法代码如下:

public void InsertOrUpdateTableMatchOnString<T>(string tableName, List<T> models) where T : class, IModel, new()
{
    BulkOperations bulk = new BulkOperations();

    using (TransactionScope trans = new TransactionScope())
    {
        using (SqlConnection conn = new SqlConnection(MyConString))
        {
            // 1. 获取当前模型的匹配属性
            var modelInstance = new T();
            PropertyInfo matchProp = modelInstance.GetMatchOn();

            // 2. 动态构建属性选择表达式 x => x.匹配属性
            ParameterExpression paramExpr = Expression.Parameter(typeof(T), "x");
            MemberExpression propAccessExpr = Expression.Property(paramExpr, matchProp);
            // 转换为object类型匹配方法入参要求
            UnaryExpression convertExpr = Expression.Convert(propAccessExpr, typeof(object));
            Expression<Func<T, object>> matchLambda = Expression.Lambda<Func<T, object>>(convertExpr, paramExpr);

            // 3. 构造批量操作配置
            var bulkConfig = bulk.Setup<T>()
                .ForCollection(models)
                .WithTable(tableName)
                .AddAllColumns()
                .BulkInsertOrUpdate()
                .SetIdentityColumn(x => x.Id)
                .MatchTargetOn(x => x.AgreementId);

            // 4. 传入动态构建的匹配表达式
            bulkConfig.MatchTargetOn(matchLambda);

            // 提交操作
            bulkConfig.Commit(conn);
        }

        trans.Complete();
    }
}

注意:需要给泛型约束加上new(),保证T有无参构造函数可以实例化调用GetMatchOn方法。如果你的模型不允许有无参构造,可以把GetMatchOn改为IModel的静态抽象方法(C# 11+支持),避免实例化开销。

方案2:更简洁的特性标记方案(推荐)

你可以去掉IModel里的GetMatchOn方法,改用特性标记匹配列,代码更简洁易维护:

  1. 首先定义匹配列特性:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class MatchTargetColumnAttribute : Attribute
{
}
  1. 修改模型,给对应匹配列加标记:
public class Company : IModel
{
    public long? Id { get; set; }
    public int AgreementId { get; set; }
    // 给匹配列加标记
    [MatchTargetColumn]
    public string Name { get; set; }
    // 其余属性省略
}
  1. 修改IModel接口,移除GetMatchOn定义:
public interface IModel
{
    long? Id { get; set; }
    int AgreementId { get; set; }
}
  1. 泛型方法直接通过反射获取带标记的属性:
public void InsertOrUpdateTableMatchOnString<T>(string tableName, List<T> models) where T : class, IModel
{
    BulkOperations bulk = new BulkOperations();

    using (TransactionScope trans = new TransactionScope())
    {
        using (SqlConnection conn = new SqlConnection(MyConString))
        {
            // 直接反射查找带匹配标记的属性
            PropertyInfo matchProp = typeof(T).GetProperties()
                .FirstOrDefault(p => p.GetCustomAttribute<MatchTargetColumnAttribute>() != null);
            if (matchProp == null)
            {
                throw new InvalidOperationException($"模型{typeof(T).Name}未指定匹配列,请给对应属性添加[MatchTargetColumn]标记");
            }

            // 后续构建表达式、调用方法的逻辑和方案1完全一致
            ParameterExpression paramExpr = Expression.Parameter(typeof(T), "x");
            MemberExpression propAccessExpr = Expression.Property(paramExpr, matchProp);
            UnaryExpression convertExpr = Expression.Convert(propAccessExpr, typeof(object));
            Expression<Func<T, object>> matchLambda = Expression.Lambda<Func<T, object>>(convertExpr, paramExpr);

            var bulkConfig = bulk.Setup<T>()
                .ForCollection(models)
                .WithTable(tableName)
                .AddAllColumns()
                .BulkInsertOrUpdate()
                .SetIdentityColumn(x => x.Id)
                .MatchTargetOn(x => x.AgreementId);

            bulkConfig.MatchTargetOn(matchLambda);
            bulkConfig.Commit(conn);
        }

        trans.Complete();
    }
}

内容的提问来源于stack exchange,提问作者8nette

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 01:42:00