有效的linq包含匿名类型

时间:2013-04-03 15:47:20

标签: c# linq entity-framework contains anonymous-types

我有一组像这样创建的匿名对象:

var srcCategories = srcSet.Categories.Select(c => new
{
     ApplicationId = c.IsGLobal ? (long?)null : c.App.Id,
     c.Name
});

请注意,此集合 not 来自我的数据上下文;它是从外部系统的输入生成的。我需要将两个 ApplicationIdName映射到我的数据库中的实体。到目前为止,这是我成功实现它的唯一方法:

var trgCategoryIds =
    (from c in core.Domain.Categories.AsEnumerable()
     let ci = new { c.ApplicationId, c.Name }
     where srcCategories.Contains(ci)
     select c.Id)
    .ToArray();

但这要求我先将整个Categories表拉入内存。我正在寻找一种更有效的方法,最好是在一个查询中。我已经尝试了以下所有选项,它们都不能转换为sql:

// Removed .AsEnumerable()
var trgCategoryIds =
    (from c in core.Domain.Categories 
     let ci = new { c.ApplicationId, c.Name }
     where srcCategories.Contains(ci)
     select c.Id)
     .ToArray();

// Use .Any() instead of .Contains()
var trgCategoryIds =
    (from c in core.Domain.Categories
     where srcCategories.Any(s => s.ApplicationId == c.ApplicationId && s.Name == s.Name)
     select c.Id)
    .ToArray();

// Use Tuples instead of anon types
var srcCategories = srcSet.Categories.Select(c => Tuple.Create(...));
var trgCategoryIds =
    (from c in core.Domain.Categories
     let ci = Tuple.Create(c.ApplicationId, c.Name)
     where srcCategories.Contains(ci)
     select c.Id)
    .ToArray();

4 个答案:

答案 0 :(得分:2)

你想做的事情真的不可能,因为首先没有简单的SQL。实际上,你想要:

select * from Catagories where (ApplicationID = 1 and Name = "Foo") 
                            or (ApplicationID = 2 and Name = "Bar") 
                            or (ApplicationID = 2345 and Name = "Fizbuzz")
                            or ...

实体框架,据我所知,无法自动创建该类型的查询。它可以通过将Contains()转换为IN (...)来处理单个测试,但是当您不能IN时,没有简单的SQL用于join。但是,您可以使用Predicate Builder库来构造此类OR查询。页面上的第二个示例应该就是您所需要的。

适合您的使用:

var predicate = PredicateBuilder.False<Category>();

  foreach (var cat in srcCategories)
  {
    var temp = cat;
    predicate = predicate.Or (p => p.ApplicationId == temp.ApplicationId && p.Name == temp.Name);
  }
  return core.Domain.Categories.AsExpandable().Where (predicate);
}

答案 1 :(得分:1)

如果两个Categories集合来自不同的数据库上下文,那么就无法将其中一个集合到内存中。

如果他们共享数据库上下文,那么您要做的就是简单地加入两个表:

var query =
    from domainCat in srcCategories
    join sourceCat in srcSet.Categories
    on new { domainCat.ApplicationId, domainCat.Name } equals
        new { sourceCat.ApplicationId, sourceCat.Name }
    select sourceCat.Id;

答案 2 :(得分:0)

如何调整以下代码,将ks作为格式化类别(Id +“ - ”+ Name)。

using System;
using System.Linq;
using System.Data.Entity;
using System.Collections.Generic;
using System.Data.Entity.ModelConfiguration;
using System.Data.Objects.SqlClient;

namespace testef {
    //Model
    public class CObj {
        public CObj() {
        }
        public Int32 Id { get; set; }
        public String SomeCol { get; set; }
    }

    //Configuration for CObj
    public class CObjConfiguration : EntityTypeConfiguration<CObj> {
        public CObjConfiguration() {
            HasKey(x => x.Id);
        }
    }

    public class TestEFContext : DbContext {
        public IDbSet<CObj> objects { get; set; }

        public TestEFContext(String cs)
            : base(cs) {
            Database.SetInitializer<TestEFContext>(new DropCreateDatabaseAlways<TestEFContext>());
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Configurations.Add(new CObjConfiguration());
        }
    }

    class Program {
        static void Main(String[] args) {
            String cs = @"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";                

            using (TestEFContext c = new TestEFContext(cs)) {
                c.objects.Add(new CObj { Id = 1, SomeCol = "c"});
                c.SaveChanges();
            }

            IEnumerable<String> ks = new List<String> { String.Format("{0,10}-c", 1) };
            foreach (var k in ks) {
                Console.WriteLine(k);
            }

            using (TestEFContext c = new TestEFContext(cs)) {
                var vs = from o in c.objects
                         where ks.Contains(SqlFunctions.StringConvert((Decimal?)o.Id, 10) + "-" + o.SomeCol)
                         select o;

                foreach (var v in vs) {
                    Console.WriteLine(v.Id);
                }
            }

        }
    }
}

答案 3 :(得分:-1)

var trgCategoryIds =
    (from c in core.Domain.Categories.AsEnumerable()
     where sourceCategories.Any(sc=> sc.ApplicationId == c.ApplicationId 
                     && sc.Name == c.Name)
     select c.Id)
    .ToArray();