1

我有一个这样创建的匿名对象集合:

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

请注意,此集合并非来自我的数据上下文;它是从外部系统的输入生成的。我需要同时映射数据库 ApplicationId中的实体和Name实体。到目前为止,这是我能够成功使其工作的唯一方法:

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

4 回答 4

3

您想要做的事情实际上是不可能的,因为一开始就没有简单的 SQL。实际上,您想要:

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

据我所知,Entity Framework 无法自动创建这种类型的查询。Contains()它可以通过转换为来处理单个测试,但是当你不能时,IN (...)没有简单的 SQL 用于 anded 。但是,您可以使用Predicate Builder库来构造这种类型的查询。页面上的第二个示例应该正是您所需要的。INjoinOR

适合您的使用:

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);
}
于 2013-04-03T16:23:01.997 回答
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;
于 2013-04-03T16:14:54.163 回答
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);
                }
            }

        }
    }
}
于 2013-04-03T16:14:26.177 回答
-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();
于 2013-04-03T15:51:23.833 回答