5

Tengo una aplicación ASP.NET MVC que utiliza Entity Framework para obtener datos.¿Cómo reutilizar las proyecciones en Entity Framework?

Necesito transformar Entites en Modelos antes de pasarlos a Ver. Las proyecciones pueden ser muy complejos, pero que sea sencillo:

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

Esto puede ser usado en un controlador de la siguiente manera:

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

muy buena. Pero, ¿y si quiero usar esta proyección dentro de otra? Ejemplo:

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

(supongamos que el blog puede tener más de un autor y es de tipo Usuario).

¿Puedo de alguna manera separar las proyecciones y reutilizarlas dentro de otras?

+1

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

Respuesta

8

Aquí hay algo que realmente funciona (en una aplicación sencilla prueba) para solo seleccionar los campos solicitados:

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

para probarlo sin tener que crear una base de datos:

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

¡Buena idea! Pero creo que no es una proyección "anidada" que ocurre en la base de datos. Cargará las entidades 'Autor' completas y luego se proyectará en el setter en la memoria, es decir, tirará algunas o quizás muchas de las columnas/propiedades cargadas. – Slauma

+0

@Slauma Oh, tienes razón, me lo perdí. Lo pensaré un poco. – hvd

+0

Esto se compila y se ejecuta correctamente, pero @Slauma tiene razón ... Revisé el Analizador y recupera todo el Usuario de la base de datos. ¿Qué piensas, es solucionable? ¡Gracias! – jakubka

Cuestiones relacionadas