使用通用 Insert 方法时,Linq To SQL 抛出不支持转换为 SQL 异常

Linq To SQL throws has no supported translation to SQL Exception when using generic Insert method

我有一种方法可以使用类型 T 将实体插入到 table 中。但是我想添加一个功能来检查要添加的实体是否存在于 table 中,基于匹配器 属性(例如姓名)。当我执行代码时,它会在检查行上抛出 'has no supported translation to SQL' 。我的代码部分如下。我该如何解决这个问题?

    public static InsertStatus Add<T>(T ent, string matcherProp) where T : class
    {
        System.Data.Linq.Table<T> t = otdc.GetTable<T>();

        //Exception on this line
        if (t.Any(item => object.Equals(GetPropValue(item, matcherProp), GetPropValue(ent, matcherProp))))
            return InsertStatus.AlreadyExists;

        try
        {
            t.InsertOnSubmit(ent);
            otdc.SubmitChanges();
            return InsertStatus.Successfull;
        }
        catch
        {
            return InsertStatus.UnknownError;
        }
    }
    public static object GetPropValue(object src, string propName)
    {
        return src.GetType().GetProperty(propName).GetValue(src, null);
    }

您需要在运行时创建一个表达式树。幸运的是,这对您来说并不难;它将是 类似于:

var p = Expression.Parameter(typeof(T), "p");
var val = GetPropValue(ent, matcherProp);
var test = Expression.Lambda<Func<T, bool>>(
    Expression.Equal(
        Expression.PropertyOrField(p, matcherProp),
        Expression.Constant(val)
    ), p);

if (t.Any(test))
    return InsertStatus.AlreadyExists;

这样做是为以下内容构建逻辑树:

p => p.{matcherProp} == {val}

其中 matcherProp 是要测试的成员的名称,val 是作为常量的现有值。

请注意,如果 valnull,您可能会遇到问题,除非您还可以提供 属性 的 type.PropertyTypePropertyInfo) - 并将其提供给 Expression.Constant


编辑:另一种方法是提供 ent 作为常量:

var p = Expression.Parameter(typeof(T), "p");
var test = Expression.Lambda<Func<T, bool>>(
    Expression.Equal(
        Expression.PropertyOrField(p, matcherProp),
        Expression.PropertyOrField(Expression.Constant(ent), matcherProp),
    ), p);

这更类似于:

p => p.{matcherProp} == ent.{matcherProp}

其中 ent 在 lambda 中的行为很像 "captured variable"。

与其将 matcherProp 设为字符串,不如考虑将其设为 Expression<Func<T, P>>,这样您就可以将其调用为:Add(myEntity, e => e.Name).

那么你需要类似的东西

public static InsertStatus Add<T>(T ent, Expression<Func<T,P>> keySelector) where P : class
{
    System.Data.Linq.Table<T> t = otdc.GetTable<T>();

    var memberAccess = (keySelector as LambdaExpression)?.Body as MemberExpression;
    ParameterExpression paramExpr = Expression.Parameter(typeof(T), "e");
    Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T,bool>>(
            Expression.Equal(memberAccess.Update(Expression.Constant(ent)), memberAccess.Update(paramExpr)), paramExpr);

    if (t.Any(predicate))
    {

当然要进行适当的错误检查。