1

我正在尝试编写一个通用方法,该方法将加载具有特定 ID 的特定类型的记录。这是一种有效的方法:

    public abstract class LinqedTable<T> where T : LinqableTable {
    public static T Get(long ID) {
        DataContext context = LinqUtils.GetDataContext<T>();
        var q = from obj in context.GetTable<T>()
                        where obj.ID == ID
                        select obj;
        return q.Single<T>();
    }
}

public abstract class LinqableTable {
    public abstract long ID { get; set; }
}

您可以忽略对LinqUtils.GetDataContext<T>();的调用 这是一个实用功能,我必须处理我的程序中有多个数据上下文的事实。关键是现在我可以将我的任何类声明为 的子类LinqableTable,并且只需调用LinqedTable<MyType>.Get(ID).

然而,这有一些限制。首先,它强制我的所有表都有一个类型为 I 的身份字段long,名为ID。其次,因为我使用的是抽象方法,所以我不得不去 O/R 设计器,将系统中每个 ID 字段的继承属性更改为“覆盖”。

我想要更多的灵活性。所以很自然地,我尝试了反思,并得出以下结论:

    public abstract class LinqedTable<T> where T : LinqableTable {
    public static T Get(long ID) {
        DataContext context = LinqUtils.GetDataContext<T>();
        var q = from obj in context.GetTable<T>()
                        where obj.IDValue == ID
                        select obj;
        return q.Single<T>();
    }
}

public abstract class LinqableTable {
    internal long IDValue {
        get { return (long)IDProperty.GetValue(this, null); }
        set { IDProperty.SetValue(this, value, null); }
    }
    internal PropertyInfo IDProperty {
        get { return this.GetType().GetProperty(IDPropertyName); }
    }
    internal protected virtual string IDPropertyName {
        get { return "ID"; }
    }
}

从理论上讲,这允许我覆盖 ID 列名称,转换为long任何整数数据类型都可以,并且我不需要将我的所有 ID 列定义为overrides.

Linq 不喜欢这样。在调用q.Single<T>();我得到一个运行时错误:

The member 'EISS.Utils.LinqableTable.IDValue' has no supported translation to SQL.

好的,今天我了解到 Linq 在后端做了某种魔术;它不会实例化obj,只是读取IDValue属性。所以一定有一些属性需要在IDValue属性上设置,让 Linq 做它的事情。

但是什么?

4

3 回答 3

3

Linq to SQL 尝试将您的 linq-query 转换为 SQL,但它不知道如何将您的属性转换为数据库中的列名。

一个很好的解释可以在这里找到: simple linq to sql has no supported translation to SQL

但如何解决,是另一回事。我已经成功使用了这个线程的方法:

http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/df9dba6e-4615-478d-9d8a-9fd80c941ea2/

或者您可以使用 scott guthrie 在此处提到的动态查询:

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

于 2009-04-26T17:10:22.893 回答
1

阅读了这些帖子:使用 LINQ to SQL 和 C# 的通用数据访问LINQ-to-SQL:通用主键函数使用类型调用通用方法

我和我的同事提出了以下摘要:

我们将以下方法添加到我们的数据上下文中(在部分类中)。

public T GetInstanceByPrimaryKey<T>(object primaryKeyValue) where T : class
{
    var table = this.GetTable<T>();
    var mapping = this.Mapping.GetTable(typeof(T));
    var pkfield = mapping.RowType.DataMembers.SingleOrDefault(d => d.IsPrimaryKey);

    if (pkfield == null)
        throw new Exception(String.Format("Table {0} does not contain a Primary Key field", mapping.TableName));

    var param = Expression.Parameter(typeof(T), "e");

    var predicate =
        Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Property(param, pkfield.Name), Expression.Constant(primaryKeyValue)), param);

    return table.SingleOrDefault(predicate);
}

然后,我们需要从类型名称和主键值中实例化:

string name = "LinqObjectName";
int primaryKey = 123;

var dc = new YourDataContext();

Type dcType = dc.GetType();
Type type = dcType.Assembly.GetType(String.Format("{0}.{1}", dcType.Namespace, name));

MethodInfo methodInfoOfMethodToExcute = dc.GetType().GetMethod("GetInstanceByPrimaryKey");
MethodInfo methodInfoOfTypeToGet = methodInfoOfMethodToExcute.MakeGenericMethod(name);
var instance = methodInfoOfTypeToGet.Invoke(dc, new object[] { primaryKey });

return instance;

希望这可以帮助!

于 2009-07-16T15:42:04.283 回答
-1

由于引用 LINQ-to-SQL IQueryable 的 LINQ 语句被转换为 SQL 查询,因此您将不得不使用 AsEnumerable 扩展(这反过来会导致读取数据库中的所有项目)并在其上执行与反射相关的工作可数的。
编辑
根据需要这里是一个澄清
正如评论中指定的那样,我的意思是这样的:

 (from obj in context.GetTable<T>() select obj).AsEnumerable().Where(x => x.IDValue == ID)

与在 IQueryable 上执行的查询不同,后者可以完美地转换为 SQL,例如


context.GetTable().Where(x => x.Text == "Hello")
它被转换为类似于

SELECT * FROM TABLE_MAPPED_TO_TYPE_T WHERE Text = 'Hello'

针对 IEnumerable 执行的查询(在您的情况下)将通过获取表的所有条目然后按代码应用指定的过滤器来执行。

于 2009-04-26T17:05:18.990 回答