使用通用 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
是作为常量的现有值。
请注意,如果 val
为 null
,您可能会遇到问题,除非您还可以提供 属性 的 type(.PropertyType
在 PropertyInfo
) - 并将其提供给 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))
{
当然要进行适当的错误检查。
我有一种方法可以使用类型 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
是作为常量的现有值。
请注意,如果 val
为 null
,您可能会遇到问题,除非您还可以提供 属性 的 type(.PropertyType
在 PropertyInfo
) - 并将其提供给 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))
{
当然要进行适当的错误检查。