5

Ho un'applicazione ASP.NET MVC che utilizza Entity Framework per ottenere dati.Come riutilizzare le proiezioni in Entity Framework?

Devo trasformare le Entità in Modelli prima di passarle a Visualizza. Sporgenze possono essere molto complessa, ma per mantenerlo semplice:

public static IQueryable<UserModel> ToModel(this IQueryable<User> users) 
{ 
    return from user in users 
      select new UserModel 
      { 
       Name = user.Name, 
       Email = user.Email, 
      }; 
} 

Questo può essere utilizzato in un controllore simili:

return View(Repository.Users.ToModel().ToList()); 

molto buona. Ma cosa succede se voglio usare questa proiezione in un altro? Esempio:

public static IQueryable<BlogPostModel> ToModel(this IQueryable<BlogPost> blogs) 
{ 
    return from blogs in blogs 
      select new BlogPostModel 
      { 
       Title = blog.Title, 
       Authors = blog.Authors.AsQueryable().ToModel(), // (entities are POCOs) 
       // This does not work, because EF does not understand method ToModel(). 
      }; 
} 

(supponiamo che il blog possa avere più di un autore ed è di tipo Utente).

Posso in qualche modo separare le proiezioni e riutilizzarle all'interno di altre?

+1

See http: // stackoverflow.com/a/11679134/861716. –

risposta

8

ecco qualcosa che funziona davvero (in una semplice applicazione di test) per solo selezionare i campi richiesti:

namespace Entities 
{ 
    public class BlogPost 
    { 
     public virtual int Id { get; set; } 
     public virtual string Title { get; set; } 
     public virtual DateTime Created { get; set; } 
     public virtual ICollection<User> Authors { get; set; } 
    } 

    public class User 
    { 
     public virtual int Id { get; set; } 
     public virtual string Name { get; set; } 
     public virtual string Email { get; set; } 
     public virtual byte[] Password { get; set; } 
     public virtual ICollection<BlogPost> BlogPosts { get; set; } 
    } 
} 

namespace Models 
{ 
    public class BlogPostModel 
    { 
     public string Title { get; set; } 
     public IEnumerable<UserModel> Authors { get; set; } 
    } 

    public class UserModel 
    { 
     public string Name { get; set; } 
     public string Email { get; set; } 
    } 

    public static class BlogPostModelExtensions 
    { 
     public static readonly Expression<Func<BlogPost, BlogPostModel>> ToModelConverterExpression = 
      p => 
      new BlogPostModel 
      { 
       Title = p.Title, 
       Authors = p.Authors.AsQueryable().Select(UserModelExtensions.ToModelConverterExpression), 
      }; 

     public static readonly Func<BlogPost, BlogPostModel> ToModelConverterFunction = ToModelConverterExpression.Compile(); 

     public static IQueryable<BlogPostModel> ToModel(this IQueryable<BlogPost> blogPosts) 
     { 
      return blogPosts.Select(ToModelConverterExpression); 
     } 

     public static IEnumerable<BlogPostModel> ToModel(this IEnumerable<BlogPost> blogPosts) 
     { 
      return blogPosts.Select(ToModelConverterFunction); 
     } 
    } 

    public static class UserModelExtensions 
    { 
     public static readonly Expression<Func<User, UserModel>> ToModelConverterExpression = 
      u => 
      new UserModel 
      { 
       Name = u.Name, 
       Email = u.Email, 
      }; 

     public static readonly Func<User, UserModel> ToModelConverterFunction = ToModelConverterExpression.Compile(); 

     public static IQueryable<UserModel> ToModel(this IQueryable<User> users) 
     { 
      return users.Select(ToModelConverterExpression); 
     } 

     public static IEnumerable<UserModel> ToModel(this IEnumerable<User> users) 
     { 
      return users.Select(ToModelConverterFunction); 
     } 
    } 
} 

per testarlo senza in realtà creare un database:

var blogPostsQuery = (
    from p in context.BlogPosts 
    where p.Title.StartsWith("a") 
    select p).ToModel(); 
Console.WriteLine(((ObjectQuery)blogPostQuery).ToTraceString()); 
+3

Buona idea! Ma penso che non sia una proiezione "annidata" che accade nel database. Caricherà le entità complete di 'Author' e quindi proietterà nel setter in memoria, cioè getterà via alcune o forse molte delle colonne/proprietà caricate. – Slauma

+0

@Slauma Oh, hai ragione, mi sono perso. Ci penserò un po '. – hvd

+0

Compila ed esegue correttamente, ma @Slauma ha ragione. Ho controllato il Profiler e recupera l'intero Utente dal DB. Cosa ne pensi, è risolvibile? Grazie! – jakubka