2

我有两张表研究和系列。系列是 FK'd back to Studies,因此一项研究包含可变数量的系列。每个系列项目都有一个Deleted列,表明它已从数据库中逻辑删除。

我正在尝试Deleted在 Study 类中实现一个属性,该属性仅在删除所有包含的系列时才返回 true。我正在使用 O/R Designer 生成的类,因此我将以下内容添加到 Study 类型的用户可修改部分类中:

public bool Deleted
{
    get
    {
        var nonDeletedSeries = from s in Series
                               where !s.Deleted
                               select s;
        return nonDeletedSeries.Count() == 0;
    }
    set
    {
        foreach (var series in Series)
        {
            series.Deleted = value;
        }
    }
}

这给出了一个异常“成员 'PiccoloDatabase.Study.Deleted' 没有支持的 SQL 转换。” 当执行这个调用 get 的简单查询时:

IQueryable<Study> dataQuery = dbCtxt.Studies;
dataQuery = dataQuery.Where((s) => !s.Deleted);
foreach (var study in dataQuery)
{
   ...
}

基于此http://www.foliotek.com/devblog/using-custom-properties-inside-linq-to-sql-queries/,我尝试了以下方法:

static Expression<Func<Study, bool>> DeletedExpr = t => false;
public bool Deleted
{
    get
    {
        var nameFunc = DeletedExpr.Compile();
        return nameFunc(this);
    }
    set
    {  ... same as before
    }
}

运行不支持对 SQL 的转换的查询时,我得到相同的异常。( lambda 表达式的逻辑还无关紧要 - 只是试图通过异常。)

我是否缺少一些基本属性或允许转换为 SQL 的东西?我已经阅读了关于这个异常的大部分帖子,但似乎没有什么完全适合我的情况。

4

2 回答 2

1

我相信 LINQ-to-SQL 的重点是您的实体是为您映射的,并且必须在数据库中具有相关性。您似乎正在尝试混合使用 LINQ-to-Objects 和 LINQ-to-SQL。

如果 Series 表在数据库中有一个 Deleted 字段,而 Study 表没有,但您想将逻辑 Study.Deleted 转换为 SQL,那么扩展将是一种方法。

public static class StudyExtensions
{
    public static IQueryable<study> AllDeleted(this IQueryable<study> studies)
    {
        return studies.Where(study => !study.series.Any(series => !series.deleted));
    }
}

class Program
{
    public static void Main()
    {
        DBDataContext db = new DBDataContext();
        db.Log = Console.Out;

        var deletedStudies = 
            from study in db.studies.AllDeleted()
            select study;

        foreach (var study in deletedStudies)
        {
            Console.WriteLine(study.name);
        }
    }
}

这会将您的“已删除研究”表达式映射到 SQL:

SELECT t0.study_id, t0.name
FROM study AS t0
WHERE NOT EXISTS(
    SELECT NULL AS EMPTY
    FROM series AS t1
    WHERE (NOT (t1.deleted = 1)) AND (t1.fk_study_id = t0.study_id)
)

或者,您可以构建实际表达式并将它们注入到您的查询中,但这是一种矫枉过正的做法。

但是,如果 Series 和 Study 在数据库中都没有 Deleted 字段,而只是在内存中,那么您需要先将查询转换为 IEnumerable,然后才能访问 Deleted 属性。但是,这样做会在应用谓词之前将记录传输到内存中,并且可能会很昂贵。IE

var deletedStudies = 
    from study in db.studies.ToList()
    where study.Deleted
    select study;

foreach (var study in deletedStudies)
{
    Console.WriteLine(study.name);
}
于 2012-07-26T17:21:51.983 回答
0

当您进行查询时,您将希望使用静态定义Expression的,而不是属性。

实际上,而不是:

dataQuery = dataQuery.Where((s) => !s.Deleted);

每当您进行 Linq to SQL 查询时,您将希望使用:

dataQuery = dataQuery.Where(DeletedExpr);

请注意,这将要求您可以DeletedExpr从中查看dataQuery,因此您需要将其移出您的类,或者将其公开(即 make 它public,在这种情况下,您将通过类定义访问它:)Series.DeletedExpr

此外,anExpression的局限性在于它不能具有函数体。所以,DeletedExpr可能看起来像:

public static Expression<Func<Study, bool>> DeletedExpr = s => s.Series.Any(se => se.Deleted);

该属性只是为了方便而添加的,因此您也可以将其用作代码对象的一部分而无需复制代码,即

var s = new Study();
if (s.Deleted)
    ...
于 2014-03-12T21:11:37.077 回答