c# - sintaxis - Pase una expresión a Seleccionar de linq
operadores en linq c# (4)
Esto es linq-to-sql
Tengo muchas clases diferentes, todas haciendo la misma consulta, pero proyectando los resultados de forma ligeramente diferente. Idealmente, me gustaría poder tener la consulta en un solo lugar y hacer que la proyección pase al método Seleccionar. Funciona bien para tipos de concreto:
public void GetResults() {
var junk = db.SiteProducts.Select(Project());
}
public Expression<Func<DbEntities.SiteProduct, string>> Project() {
return p => p.ProductAlert;
}
Pero cuando trato de devolver un tipo anónimo, falla
public void GetResults() {
var junk = db.SiteProducts.Select(Project());
}
public Expression<Func<DbEntities.SiteProduct, TResult>> Project<TResult>() {
return p => new { p.ProductAlert };
}
Entiendo completamente por qué la inferencia de tipo genérico está fallando en el segundo caso. Pero, ¿hay algún truco para crear mis propias Expresiones desde cero? ¿Me falta algo que pueda hacer que esto funcione?
Esto no funcionará en tiempo de compilación. Usando cosas dinámicas puedes hacer que funcione, por supuesto.
Una solución simple es no usar un tipo anónimo sino una clase de DTO hecha a medida. Tal clase de DTO solo toma muy pocas líneas y es fácil de mantener. Por lo general, esta es una buena solución.
IdeaBlade tiene una clase ProjectionSelector
que puede usar para abstraer sus proyecciones. Cuando necesita construir una consulta de proyección pero no conoce los tipos involucrados en el tiempo de compilación, puede crear una instancia de la clase ProjectionSelector
y pasar la información de tipo en el tiempo de ejecución.
La clase y el código de muestra se pueden encontrar aquí:
Crear cláusulas dinámicas "Seleccionar", "SeleccionarMany" y "GroupBy"
http://drc.ideablade.com/xwiki/bin/view/Documentation/dynamic-projection
Esta es una pregunta intrigante. Creo que un DTO puede ayudarte aquí, pero hay limitaciones y peligros a tener en cuenta. Tome el siguiente ejemplo de LINQPad :
class ProjectDTO
{
public string Name { get; set; }
public static Expression<Func<Project, ProjectDTO>> ToDTO = (e) => new ProjectDTO
{
Name = e.Name
};
public ProjectDTO() {}
public ProjectDTO(Project project)
{
Name = project.Name;
}
}
void Main()
{
Projects.Select(p => p.Name).Dump();
Projects.Select(ProjectDTO.ToDTO).Dump();
Projects.Select(p => new ProjectDTO(p)).Dump();
}
SQL generado:
SELECT [t0].[Name]
FROM [Project] AS [t0]
GO
SELECT [t0].[Name]
FROM [Project] AS [t0]
GO
SELECT [t0].[ProjectId], [t0].[Name], [t0].[Description], [t0].[DateCreated], [t0].[DateModified], [t0].[DateComplete], [t0].[CreatedBy]
FROM [Project] AS [t0]
Como puede ver, no puede usar un constructor de copias para asignar las propiedades del DTO ya que esto obliga a que todo el objeto se retire de la base de datos.
Esto también limita un poco si desea ampliar el DTO base y agregar más propiedades para vistas más especializadas de los datos, lo que significa que podría terminar con múltiples expresiones con un código similar.
Sin embargo, me gusta bastante la opción dos, pero estoy seguro de que esta opción probablemente esté restringida a las proyecciones de tipo único, considere el siguiente ejemplo:
var query = from p in Projects
join t in Tasks on p.ProjectId equals t.ProjectId
select ProjectDTO.ToDTO; //Can''t be used like this
No creo que pueda usar Expression en este tipo de sintaxis de consulta. En términos generales, no creo que haya una solución que funcione en todos los ámbitos. Es posible que deba revisar su diseño para ver si puede proporcionar menos proyecciones, en función de que algunas de las propiedades son muy baratas para incluirlas siempre en la consulta.
Sin utilizar la biblioteca Dynamic LINQ o construir el árbol de expresiones manualmente, también me gustaría ver si es posible con LINQ-SQL / LINQ-Entities para crear selecciones dinámicas.
Si entiendo tu pregunta correctamente, puedes usar este código:
primero declare un método para seleccionar sus datos de esta manera:
public List<TResult> FindAll<TResult>(Func<Regions, TResult> selector) where TResult : class
{
using (RepositoryDataContext = new DataClasses1DataContext())
{
return RepositoryDataContext.Regions.Select<Regions, TResult>(selector).ToList<TResult>();
}
}
entonces puedes construir tu declaración de selección así:
Func<Regions, SelectAllRegion> select = r => new SelectAllRegion
{
RegionID = r.RegionID,
RegionDescription = r.RegionDescription
};
mi SelectAllRegion
:
public class SelectAllRegion
{
public SelectAllRegion()
{
}
public int RegionID { get; set; }
public string RegionDescription { get; set; }
}
y la región es Region
tabla de la Region
en Northwing. Espero que esto te ayude