3

我需要使用单个 linq 查询过滤 linq 中实体的子元素。这可能吗?

假设我有两个相关的表。诗句和诗句翻译。LINQ to SQL 创建的实体是这样的,我有一个 Verse 对象,其中包含一个子对象,该子对象是 VerseTranslation 的集合。

现在,如果我有以下 linq 查询

var res = from v in dc.Verses
                  where v.id = 1
                  select v;

我得到一个 id 为 1 的 Verses 集合,每个 verse 对象包含来自 VerseTranslations 的所有子对象。

我还想做的是过滤 Verse Translations 的子列表。

到目前为止,我能够想出的唯一方法是使用新的匿名类型或其他方式。如下

var res= from v in dc.Verses
                   select new myType
                   {
                       VerseId = v.VerseId,
                       VText = v.Text,
                       VerseTranslations = (from trans in v.VerseTranslations
                                               where languageId==trans.LanguageId
                                               select trans
                   };

上面的代码有效,但我必须为它声明一个新类。有没有办法以这样的方式做到这一点,即子表的过滤可以合并到第一个 linq 查询中,这样就不必声明新的类。

问候, MAC

4

4 回答 4

9

多亏了设拉子的指导,我终于让它工作了。

        DataLoadOptions options = new DataLoadOptions();
        options.AssociateWith<Verse>(item => item.VerseTranslation.Where(t => languageId.Contains(t.LanguageId)));

        dc.LoadOptions = options;

        var res = from s in dc.Verse
                   select s;

这不需要投影或使用新的扩展类。

感谢所有输入的人。

于 2009-08-04T22:55:43.940 回答
0

如果这是来自数据库,您可以运行您的第一条语句。

然后使用 Where 子句执行 VerseTranslations 的加载或包含。

http://msdn.microsoft.com/en-us/library/bb896249.aspx

您的模型中是否有 Verse 和 VerseTranslations 之间的关系。在这种情况下,这可能会起作用:

var res= from v in 
dc.Verses.Include("VerseTranslations").Where(o => languageId==o.LanguageId)
select v;
于 2009-08-04T10:17:43.477 回答
0

过滤对象的封闭集合,

var res = dc.Verses
            .Update(v => v.VerseTranslations 
                      =  v.VerseTranslations
                          .Where(n => n.LanguageId == languageId));

通过使用来自HookedOnLinq的扩展方法“更新”

public static class UpdateExtensions {

    public delegate void Func<TArg0>(TArg0 element);

    /// <summary>
    /// Executes an Update statement block on all elements in an IEnumerable<T> sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="update">The update statement to execute for each element.</param>
    /// <returns>The numer of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update) {
        if (source == null) throw new ArgumentNullException("source");
        if (update == null) throw new ArgumentNullException("update");
        if (typeof(TSource).IsValueType) 
            throw new NotSupportedException("value type elements are not supported by update.");

        int count = 0;
        foreach(TSource element in source) {
            update(element);
            count++;
        }
        return count;
    }
}
于 2009-08-04T10:29:35.090 回答
0

有没有办法以这样的方式做到这一点,以便可以将子表的过滤合并到第一个 linq 查询中,从而不必声明新的类?

从技术上讲,答案是否定的。如果您尝试返回的数据多于单个实体对象(Verse、VerseTranslation)可以容纳的数据,则需要某种对象来“投影”到其中。但是,您可以myType通过使用匿名类型来明确声明:

var res = from v in dc.Verses
          select new
          {
              Verse = v,
              Translations = (from trans in v.VerseTranslations
                              where languageId==trans.LanguageId
                              select trans).ToList()
          };

var first = res.First();
Console.WriteLine("Verse {0} has {1} translation(s) in language {2}.",
    first.Verse.VerseId, first.Translations.Count, languageId);

编译器将为您生成一个具有适当类型的 Verse 和 Translations 属性的类。只要您不需要按名称引用类型(例如,从命名方法返回),您几乎可以将这些对象用于任何事情。因此,虽然您在技术上没有“声明”一个类型,但您仍在使用将根据您的规范生成的新类型。

至于使用单个 LINQ 查询,这完全取决于您希望数据的结构方式。对我来说,您的原始查询似乎最有意义:将每个查询Verse与过滤的翻译列表配对。如果您希望每种语言只有一个翻译,您可以使用SingleOrDefault(或FirstOrDefault) 来展平您的子查询,或者只使用SelectMany这样的:

var res= from v in dc.Verses
         from t in v.VerseTranslations.DefaultIfEmpty()
         where t == null || languageId == t.LanguageId
         select new { Verse = v, Translation = t };

如果一个 Verse 有多个翻译,这将为每个 Verse/Translation 对返回一个“行”。我使用DefaultIfEmpty()左连接来确保我们能得到所有的诗句,即使它们缺少翻译。

于 2009-08-04T12:53:46.463 回答