2013-04-03 2 views
0

У меня есть коллекция анонимных объектов, созданные, как это:Эффективного LINQ содержит с анонимным типом

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(); 
+0

Являются ли эти две разные базы данных, или одна база данных? Являются ли коллекции '' '' '' '' те же 'DataContext'? – Servy

+0

@Servy Нет, 'srcCategories' происходит от ввода из внешней системы - в частности, веб-служба предоставляет список категорий, которые затем отфильтровываются на основе параметров командной строки. –

+1

Хорошо, если два IQueryables поступают из разных источников, как бы вы ожидали избежать того, чтобы вытащить один из двух наборов данных в память? Они почти наверняка не узнают, как разговаривать друг с другом. – Servy

ответ

2

То, что вы хотите сделать, это не представляется возможным, потому что нет простого 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 IN, когда вы не можете 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); 
} 
+0

+1 Это выглядит многообещающе, но я получаю: «* Invoke не может быть вызван рекурсивно - попробуйте использовать временную переменную.» « –

+0

Nevermind, я случайно набрал« predicate.Name == temp.Name' Он работает! –

-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(); 
+1

Я уже пробовал это. Он не преобразуется в sql, даже если вы берете '.AsEnumerable()'. –

0

насчет adaptating следующего кода, с кс, как отформатированные категории (Id + «-» + имя).

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

     } 
    } 
} 
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; 
Смежные вопросы