Cambiar la segunda función a esto:
public IEnumerable<DO_RoleInfo> SelectAll()
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return from role in _ctx.DB_RoleInfo.ToList()
select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role);
}
AutoMapper funciona bien con LINQ to SQL, pero no se puede ejecutar como parte de la consulta diferida. Agregar ToList()
al final de su consulta Linq hace que evalúe inmediatamente los resultados, en lugar de tratar de traducir el segmento AutoMapper como parte de la consulta.
Aclaración
La noción de ejecución diferida (no "carga perezosa") no tiene ningún sentido una vez que haya cambiado el tipo resultante a algo que no es una entidad de datos. Tenga en cuenta estas dos clases:
public class DB_RoleInfo
{
public int ID { get; set; }
public string Name { get; set; }
}
public class DO_RoleInfo
{
public Role Role { get; set; } // Enumeration type
}
Consideremos ahora la siguiente asignación:
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>
.ForMember(dest => dest.Role, opt => opt.MapFrom(src =>
(Role)Enum.Parse(typeof(Role), src.Name)));
Esta asignación es completamente bien (a menos que hiciera un error tipográfico), pero digamos que escribe el método SelectAll
en su puesto original en lugar de mi revisó uno:
public IQueryable<DO_RoleInfo> SelectAll()
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return from role in _ctx.DB_RoleInfo
select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role);
}
en realidad, esto tipo de obras, pero llamando a sí mismo un "consultable", que se encuentra. ¿Qué sucede si trato de escribir esto en su contra:
public IEnumerable<DO_RoleInfo> SelectSome()
{
return from ri in SelectAll()
where (ri.Role == Role.Administrator) ||
(ri.Role == Role.Executive)
select ri;
}
Piensa mucho en esto. ¿Cómo podría Linq to SQL posiblemente ser capaz de convertir con éxito su where
en una consulta de base de datos real?
Linq no sabe nada sobre la clase DO_RoleInfo
. No sabe cómo hacer la asignación hacia atrás - en algunos casos, eso puede que ni siquiera sea posible. Claro, puedes mirar este código e ir al "Oh, eso es fácil, solo busca 'Administrador' o 'Ejecutivo' en la columna Name
", pero eres el único que sabe eso. En lo que se refiere a Linq to SQL, la consulta es pura tontería.
Imagine que alguien le dio estas instrucciones:
Go to the supermarket and bring back the ingredients for making Morton Thompson Turkey.
A menos que usted lo ha hecho antes, y la mayoría de la gente no tiene, su respuesta a la instrucción es muy probable que va a ser:
Puede ir al mercado, y se puede obtener ingredientes específicos por su nombre, pero no se puede evaluar la condición que le he dado mientras estás allí. Tengo que "quitar el mapa" de los criterios primero. Tengo que decirte, aquí están los ingredientes que necesitamos para esta receta - ahora ve a buscarlos.
En resumen, esto no es una sencilla incompatibilidad entre LINQ to SQL y AutoMapper. No es exclusivo de ninguna de esas dos bibliotecas. No importa cómo realizas la asignación a un tipo que no sea de entidad; podrías hacer la asignación de la forma más fácil manualmente, y aún obtendrías el mismo error, porque ahora le estás dando a Linux un conjunto de instrucciones que ya no son comprensibles, que tratan con clases misteriosas que no tienen un mapeo intrínseco a ningún tipo de entidad particular.
Este problema es fundamental para el concepto de Asignación de O/R y la ejecución diferida de consultas. A proyección es operación unidireccional. Una vez que haya proyectado, ya no podrá regresar al motor de consultas y decir , por cierto, aquí hay algunas condiciones más para usted. Es demasiado tarde. Lo mejor que puede hacer es tomar lo que ya le dio y evaluar las condiciones adicionales usted mismo.
Por último, pero no por eso menos importante, lo dejo con una solución. Si la cosa única desea ser capaz de hacer de su asignación es filtro las filas, puede escribir lo siguiente:
public IEnumerable<DO_RoleInfo> SelectRoles(Func<DB_RoleInfo, bool> selector)
{
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>();
return _ctx.DB_RoleInfo
.Where(selector)
.Select(dbr => Mapper.Map<DB_RoleInfo, DO_RoleInfo>(dbr));
}
Este es un método de utilidad que se encarga de la asignación para usted y acepta un filtro en la entidad original, y no la entidad mapeada. Puede ser útil si tiene muchos tipos diferentes de filtros, pero siempre debe hacer el mismo mapeo.
En lo personal, creo que será mejor simplemente escribir las consultas correctamente, determinando en primer lugar lo que necesita para recuperar de la base de datos de, a continuación, hacer cualquier proyecciones/asignaciones, y luego, por último, si es necesario do filtro adicional (que no debe), luego materializar los resultados con ToList()
o ToArray()
y escribir más condiciones en la lista local.
No intente utilizar AutoMapper o cualquier otra herramienta para ocultar las entidades reales expuestas por Linq a SQL.El modelo de dominio es su interfaz pública. Las consultas que escribe son un aspecto de su implementación privada . Es importante entender la diferencia y mantener una buena separación de preocupaciones.
Sólo una nota en su actualización, además de mi respuesta: la segunda versión funciona porque Linq to SQL ya * sabe * que ha realizado una proyección irreversible; el 'SelectByKey' en realidad solo usa Linq para objetos. Si examina la consulta real que se está generando, creo que encontrará que todavía está seleccionando todas las entidades de la base de datos, lo que equivale a usar 'ToList()' y luego filtrar la lista resultante. – Aaronaught
No es que AutoMapper admita LINQ. Es que LINQ to SQL no es compatible con AutoMapper. El proveedor de consultas de LINQ a SQL observa el árbol de expresiones para determinar cómo generar la consulta SQL. Cuando se trata de la pieza Mapper.Map, no tiene idea de cómo generar el SQL. –