2015-01-03 13 views
5

Vorrei ottenere un elenco di messaggi inclusi i tag.C# e EF non sono in grado di aggiungere entità correlate al modello

Ho la Modello:

public class BlogPostGetByUrlSlugDto 
{ 
    public int Id { get; set; } 
    public string Title { get; set; } 
    public string Category { get; set; } 
    public string Color { get; set; } 
    public string UrlSlug { get; set; } 
    public string Description { get; set; } 
    public IList<BlogTagGetByPostIdDto> Tags { get; set; } 
} 

public class BlogTagGetByPostIdDto 
{ 
    public string Name { get; set; } 
    public string UrlSlug { get; set; } 
} 

My Code finora:

public BlogPostGetByUrlSlugDto GetByUrlSlug(string urlSlug) 
{ 
    var blogPostQuery = _blogPostRepository.Query; 

    return blogPostQuery 
       .Where(b => b.UrlSlug.Equals(urlSlug)) 
       .ToList() 
       .Select(bp => new BlogPostGetByUrlSlugDto 
       { 
        Id = bp.Id, 
        Title = bp.Title, 
        Category = bp.BlogCategory.Name, 
        Color = bp.BlogCategory.Color, 
        UrlSlug = bp.UrlSlug, 
        Description = bp.Description, 
        Tags = bp.BlogTags.Select(t => new BlogTagGetByPostIdDto 
             { 
              Name = t.Name, 
              UrlSlug = t.UrlSlug 
             }) 
             .ToList() 
       }) 
       .Single(); 
} 

ottengo Object reference not set to an instance of an object nella linea .Select(bp => new BlogPostGetByUrlSlugDto.

Qualche idea del perché?

Il Repository per _blogPostRepository.Query è:

public interface IRepository<T> where T : class 
{ 
    T FindById(int id, bool asNoTracking = false); 

    T FindSingle(Expression<Func<T, bool>> predicate = null, bool asNoTracking = false, params Expression<Func<T, object>>[] includes); 

    IQueryable<T> Find(Expression<Func<T, bool>> predicate = null, bool asNoTracking = false, params Expression<Func<T, object>>[] includes); 


    /// <summary> 
    /// Add entity to the repository 
    /// </summary> 
    /// <param name="entity">The entity to add</param> 
    void Add(T entity); 

    /// <summary> 
    /// Attach entity to the repository 
    /// </summary> 
    /// <param name="entity">The entity to attach</param> 
    void Attach(T entity); 

    bool Exists(T entity); 

    /// <summary> 
    /// Updates entity within the repository 
    /// </summary> 
    /// <param name="entity">The entity to update</param> 
    void Update(T entity, bool partial = false); 


    /// <summary> 
    /// Mark entity by id to be deleted within the repository 
    /// </summary> 
    /// <param name="entity">The entity to delete</param> 
    void Delete(object id); 


    /// <summary> 
    /// Mark entity to be deleted within the repository 
    /// </summary> 
    /// <param name="entity">The entity to delete</param> 
    void Delete(T entity); 


    /// <summary> 
    /// Get an item matching the id 
    /// </summary> 
    T GetById(int id); 

    /// <summary> 
    /// Get an item or itens matching the Expression including opcional parameters 
    /// </summary> 
    IList<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = ""); 

    /// <summary> 
    /// Get an item matching to prediate 
    /// </summary> 
    //T Get(Func<T, bool> predicate); 


    /// <summary> 
    /// Get all the itens matching to prediate 
    /// </summary> 
    IList<T> GetAll(Func<T, bool> predicate); 


    ///// <summary> 
    ///// Get all the element of this repository 
    ///// </summary> 
    ///// <returns>Entities list</returns> 
    IList<T> GetAll(); 


    /// <summary> 
    /// Allow to send Linq queries to the entity 
    /// </summary> 
    IQueryable<T> Query { get; } 


    /// <summary> 
    /// Saves the pending changes back into the DataContext. 
    /// </summary> 
    void Save(); 
} 

Attuazione della Query:

public class Repository<T> : IRepository<T> where T : class 
{ 
    protected DbContext _dataContext; 
    protected DbSet<T> _dbSet; 

    public virtual IQueryable<T> Query 
    { 
     get 
     { 
      return _dbSet; 
     } 
    } 
} 
+0

Che tipo è 'blogPostQuery'? O forse dovrei chiedere: cosa succede nella proprietà '_blogPostRepository.Query'? Se restituissi la query EF originale probabilmente non avresti un'eccezione di riferimento null. –

+0

Ciao Grazie. blogPostQuery è di tipo IRepository _blogPostRepository; dove nel mio repository ho tutte le operazioni su EF come Aggiungi Attach Exists Update ma no Include :( – Patrick

+0

Devi restituire l'originale 'IQueryable' da EF, altrimenti la query interna restituirà sempre * tutti * i record. utilizzare proiezioni o predicati che vengono tradotti in SQL e ridurre il carico utile e non è possibile comporre query da più repository in un modo in cui viene generata una query SQL e non c'è 'Include', no' AsNoTracking'. applicando LINQ-to-objects a un risultato di query di grandi dimensioni, da cui il riferimento a oggetti null (un 'bp.BlogTags') .In SQL questo non sarebbe un problema –

risposta

5

Per caricare le entità all'interno della query principale, (questo processo è chiamato eager loading), è possibile utilizzare il Include() method e passa la tua raccolta come espressione.

Per usare alcune estensioni di Entity Framework, ricordarsi di aggiungere il seguente spazio dei nomi:

using System.Data.Entity; 

Per il campione, provare qualcosa di simile:

var result = _blogPostRepository.Query 
           .Where(b => b.UrlSlug.Equals(urlSlug)) 
           .Include(b => b.Tags) 
           .Select(bp => new BlogPostGetByUrlSlugDto 
           { 
            Id = bp.Id, 
            Title = bp.Title, 
            Category = bp.BlogCategory.Name, 
            Color = bp.BlogCategory.Color, 
            UrlSlug = bp.UrlSlug, 
            Description = bp.Description, 
            Tags = bp.Tags.Select(t => new BlogTagGetByPostIdDto 
                 { 
                  Name = t.Name, 
                  UrlSlug = t.UrlSlug 
                 }) 
           }) 
           .FirstOrDefault(); 

return result; 

Dal momento che si chiama ToList o Single o FistOrDefault metodi , eseguirà la query nel database. Nel tuo codice, chiami lo ToList() e eseguirà la query nel database ed eseguirà ogni query (caricamento lento) per i tag.

Leggi questo articolo per saperne di più su come guidare con caricamento Earger/Lazy. http://msdn.microsoft.com/en-us/data/jj574232.aspx

+0

Ciao grazie! Secondo te qual è l'opzione migliore? con ToList() da eseguire in memoria o senza eseguire nel database? – Patrick

+0

ToList verrà eseguito nel database e tutto ciò che fai dopo ToList, sarà in memoria. Penso che sarebbe meglio provare a caricare i tag usando il metodo Include e dopo chiamare "FirstOrDefault' (che restituisce null se non si trova alcun registro) invece di chiamare ToList e Single. –

+0

Non ho accesso a ".Include" perché utilizzo un repository che utilizza l'interfaccia IQueryable Query {get; } per la variabile blogPostQuery. – Patrick

1

Si dovrebbe essere in grado di utilizzare .include con IQueryable ma si dovrebbe aggiungere

using System.Data.Entity; 

in quanto è un metodo di estensione IQueryable.

specifica Metodo: DbExtensions.Include Method


Oltre a questo, si dovrebbe verificare se BlogCategory può essere nullo, se questo è il caso, è necessario affrontare il problema nel vostro prescelto, altrimenti sarà nullo e lancia l'errore mentre stai tentando di accedere a una proprietà di un oggetto nullo.

Qualcosa di simile:

public BlogPostGetByUrlSlugDto GetByUrlSlug(string urlSlug) 
{ 
    var blogPostQuery = _blogPostRepository.Query; 

    return blogPostQuery 
       .Where(b => b.UrlSlug.Equals(urlSlug)) 
       .Include(b => b.Tags) 
       .Select(bp => new BlogPostGetByUrlSlugDto 
       { 
        Id = bp.Id, 
        Title = bp.Title, 
        Category = bp.BlogCategory == null ? string.Empty : bp.BlogCategory.Name, 
        Color = bp.BlogCategory == null ? string.Empty : bp.BlogCategory.Color, 
        UrlSlug = bp.UrlSlug, 
        Description = bp.Description, 
        Tags = bp.BlogTags.Select(t => new BlogTagGetByPostIdDto 
             { 
              Name = t.Name, 
              UrlSlug = t.UrlSlug 
             }) 
             .ToList() 
       }) 
       .FirstOrDefault(); 
} 
1

Cambiare la raccolta Tags in BlogPostGetByUrlSlugDto a questo (è richiesta la parola chiave virtuale):

public virtual ICollection<BlogTagGetByPostIdDto> Tags { get; set; } 

Questo è neccessary Lazy Loading al lavoro. La classe BlogTagGetByPostIdDto ha anche bisogno di una chiave primaria, in modo da cambiare per questo:

public class BlogTagGetByPostIdDto 
{ 
    [Key] 
    public string Name { get; set; } 
    public string UrlSlug { get; set; } 
} 

Se non si desidera che il nome per essere unico, aggiungere una proprietà ID a questa classe. Quando si è recuperato un oggetto BlogPostGetByUrlSlugDto e si utilizza la proprietà Tags, verranno recuperati i tag associati a questo oggetto.

Problemi correlati