2015-09-24 3 views
5

Я видел несколько ответов на подобные вопросы, однако я не могу понять, как применить ответ на мою проблему.Условное включение() в инфраструктуре Entity

var allposts = _context.Posts 
      .Include(p => p.Comments) 
      .Include(aa => aa.Attachments) 
      .Include(a => a.PostAuthor) 
      .Where(t => t.PostAuthor.Id == postAuthorId).ToList(); 

Вложения могут быть загружены Автором (Тип Автор) или Участником (введите Contributor). То, что я хочу сделать, - это получить только вложения, в которых владелец приложения имеет тип Author.

Я знаю, что это не работает и выдает ошибку:

.Include(s=>aa.Attachments.Where(o=>o.Owner is Author)) 

Я читал о отфильтрованных спроецированных здесь

EDIT - ссылка на статью: : http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx,

но Я просто не могу обойти его.

Я не хочу включать фильтр в final where, поскольку я хочу ВСЕ сообщения, но я хочу только получить вложения для тех сообщений, которые принадлежат автору.

EDIT 2: - Сообщение схемы просил

public abstract class Post : IPostable 
{ 

    [Key] 
    public int Id { get; set; } 

    [Required] 
    public DateTime PublishDate { get; set; } 

    [Required] 
    public String Title { get; set; } 

    [Required] 
    public String Description { get; set; } 

    public Person PostAuthor { get; set; } 
    public virtual ICollection<Attachment> Attachments { get; set; } 
    public List<Comment> Comments { get; set; } 
} 
+0

Не могли бы вы показать нам схему «Сообщений», пожалуйста? – DarkKnight

+0

@DarkKnight - см. Редактировать – grayson

+0

@grayson То, что вы просите сделать, невозможно. 'Linq2Sql' преобразует ваш код в raw' SQL', и он будет возвращать дочерние строки через соединение. Вы не можете выполнить это условное соединение в 'SQL'. Единственным вариантом является удаление '.Include (aa => aa.Attachments)' и второй запрос, который возвращает вложения в зависимости от того, является ли владелец автором/вкладчиком. – Rob

ответ

5

Из ссылки, которую вы опубликовали, я могу подтвердить, что трюк работает, но только для отношений один-много (или много-один). В этом случае ваше Post-Attachment должно быть отношением «один-много», поэтому оно полностью применимо. Вот запрос, вы должны иметь:

//this should be disabled temporarily 
_context.Configuration.LazyLoadingEnabled = false; 
var allposts = _context.Posts.Where(t => t.PostAuthor.Id == postAuthorId) 
         .Select(e => new { 
          e,//for later projection 
          e.Comments,//cache Comments 
          //cache filtered Attachments 
          Attachments = e.Attachments.Where(a => a.Owner is Author), 
          e.PostAuthor//cache PostAuthor 
         }) 
         .AsEnumerable() 
         .Select(e => e.e).ToList(); 
+0

Мне нужно добавить «_context.Configuration.LazyLoadingEnabled = true;» поменять ленивую загрузку после этого звонка? – grayson

+0

@grayson Да, если вы хотите снова включить это. – Hopeless

0

Lambda в Include() может указывать только на свойства:

.Include(a => a.Attachments) 
.Include(a => a.Attachments.Owner); 

Ваше состояние не имеет смысла для меня, потому что Include() означает join и вы либо делать это, либо нет. И не условно.

Как вы напишете это в необработанном SQL?


Почему не только это:

context.Attachments 
     .Where(a => a.Owner.Id == postAuthorId && 
        a.Owner.Type == authorType); 

?

+0

Спасибо. Я довольно новичок в этом. Я могу сделать это, чтобы получить вложения желаемого типа, но я не знаю, как объединить его с Post. Мой DisplayTemplate происходит из Post и отображает Post.Attachments – grayson

3

Вы можете использовать this implementation из метода расширения Include2() (например.). После этого, вы можете позвонить:

_context.Posts.Include2(post => post.Attachments.Where(a => a.OwnerId == 1)) 

Код выше включает в себя только вложения, где это Attachment.OwnerId == 1.

+0

Привет, это не работает, когда нет прикрепленных сообщений. A, я что-то пропустил – grayson

1

Удалите virtual ключевое слово из вашего Attachments свойства навигации для предотвращения отложенной загрузки:

public ICollection<Attachment> Attachments { get; set; }

Первый метод: Выпуск два отдельных запросов: один для сообщений, один для вложений, и пусть отношения fix- до делать все остальное:

List<Post> postsWithAuthoredAttachments = _context.Posts 
    .Include(p => p.Comments) 
    .Include(p => p.PostAuthor) 
    .Where(p => p.PostAuthor.Id == postAuthorId) 
    .ToList(); 

List<Attachment> filteredAttachments = _context.Attachments 
    .Where(a => a.Post.PostAuthor.Id == postAuthorId) 
    .Where(a => a.Owner is Author) 
    .ToList() 

Отношение Fixup означает, что вы можете получить доступ к этим отфильтрованным вложениям через Пост свойства навигации

Второй метод: один запрос к базе данных, за которым следует в памяти запроса:

var query = _context.Posts 
    .Include(p => p.Comments) 
    .Include(p => p.PostAuthor) 
    .Where(p => p.PostAuthor.Id == postAuthorId) 
    .Select(p => new 
     { 
      Post = p, 
      AuthoredAttachments = p.Attachments 
       Where(a => a.Owner is Author) 
     } 
    ); 

Я бы просто использовать анонимный тип здесь

var postsWithAuthoredAttachments = query.ToList() 

или я бы создать класс ViewModel к избежать анонимного типа:

List<MyDisplayTemplate> postsWithAuthoredAttachments = 
    //query as above but use new PostWithAuthoredAttachments in the Select 

Или, если вы действительно хотите распаковывать сообщения:

List<Post> postsWithAuthoredAttachments = query.//you could "inline" this variable 
    .AsEnumerable() //force the database query to run as is - pulling data into memory 
    .Select(p => p) //unwrap the Posts from the in-memory results 
    .ToList() 
-1

Предполагая, что тип «a» типа «YourType», conditonal include можно решить, используя расширение метода, например.

public static class QueryableExtensions 
{ 
    public static IQueryable<T> ConditionalInclude<T>(this IQueryable<T> source, bool include) where T : YourType 
    { 
     if (include) 
     { 
      return source 
       .Include(a => a.Attachments) 
       .Include(a => a.Attachments.Owner)); 
     } 

     return source; 
    } 
} 

... тогда просто используйте это, как будто вы используете .Include, например.

bool yourCondition; 

.ConditionalInclude(yourCondition) 
+3

это шутка или еще что-то? – sam