如何在 Linq 中过滤子集合

发布于 2024-07-30 00:44:21 字数 993 浏览 5 评论 0原文

我需要使用单个 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 中的所有子对象。

我还想做的是过滤诗歌翻译的子列表。

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

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 查询中,这样就不必声明新的类。

问候, 苹果

I need to filter the child elements of an entity in linq using a single linq query. Is this possible?

Suppose I have two related tables. Verses and VerseTranslations. The entity created by LINQ to SQL is such that i have a Verse object that contains a child object that is a collection of VerseTranslation.

Now if i have the follow linq query

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

I get a collection of Verses whose id is 1 and each verse object contains all the child objects from VerseTranslations.

What I also want to do is filter that child list of Verse Translations.

So far the only way i have been able to come up with is by using a new Type Anonymous or otherwise. As follows

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
                   };

The above code works, but i had to declare a new class for it. Is there no way to do it in such a manner such that the filtering on the child table can be incorporated in the first linq query so that no new classes have to be declared.

Regards,
MAC

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

烟酉 2024-08-06 00:44:24

这样的情况难道就没有办法了吗?
方式使得过滤
子表可以合并到
第一个 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 ,例如this:

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 };

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

Is there no way to do it in such a
manner such that the filtering on the
child table can be incorporated in the
first linq query so that no new
classes have to be declared?

Technically, the answer is no. If you're trying to return more data than a single entity object (Verse, VerseTranslation) can hold, you'll need some sort of object to "project" into. However, you can get around explicitly declaring myType by using an anonymous type:

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);

The compiler will generate a class with appropriately-typed Verse and Translations properties for you. You can use these objects for just about anything as long as you don't need to refer to the type by name (to return from a named method, for example). So while you're not technically "declaring" a type, you're still using a new type that will be generated per your specification.

As far as using a single LINQ query, it all depends how you want the data structured. To me it seems like your original query makes the most sense: pair each Verse with a filtered list of translations. If you expect only a single translation per language, you could use SingleOrDefault (or FirstOrDefault) to flatten your subquery, or just use a SelectMany like this:

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 };

If a Verse has multiple translations, this will return a "row" for each Verse/Translation pair. I use DefaultIfEmpty() as a left join to make sure we will get all Verses even if they're missing a translation.

凉墨 2024-08-06 00:44:23

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

然后使用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;

If this is coming from a database, you could run your first statement.

Then do a Load or Include of VerseTranslations with a Where clause.

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

Do you have a relationship in your model between Verse and VerseTranslations. In that case this might work:

var res= from v in 
dc.Verses.Include("VerseTranslations").Where(o => languageId==o.LanguageId)
select v;
落花浅忆 2024-08-06 00:44:23

感谢 Shiraz 的指点,我终于成功了。

        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;

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

感谢所有提供意见的人。

So i finally got it to work thanks to the pointers given by Shiraz.

        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;

This does not require projection or using new extension classes.

Thanks for all your input people.

最佳男配角 2024-08-06 00:44:23

过滤对象的封闭集合

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

通过使用 HookedOnLinq 中的扩展方法“Update”

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;
    }
}

Filtering on the enclosed collection of the object,

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

By using extension method "Update" from 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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文