orm - framework - dapper.net core
¿Cómo mapeo listas de objetos anidados con Dapper? (6)
Actualmente estoy usando Entity Framework para mi acceso de db, pero quiero echarle un vistazo a Dapper. Tengo clases como esta:
public class Course{
public string Title{get;set;}
public IList<Location> Locations {get;set;}
...
}
public class Location{
public string Name {get;set;}
...
}
Entonces, se puede enseñar un curso en varios lugares. Entity Framework hace el mapeo para que mi objeto Course se rellene con una lista de ubicaciones. ¿Cómo podría hacer esto con Dapper, incluso es posible o tengo que hacerlo en varios pasos de consulta?
Algo falta. Si no especifica cada campo desde Ubicaciones en la consulta SQL, la Ubicación del objeto no se puede completar. Echar un vistazo:
var lookup = new Dictionary<int, Course>()
conn.Query<Course, Location, Course>(@"
SELECT c.*, l.Name, l.otherField, l.secondField
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course)) {
lookup.Add(c.Id, course = c);
}
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(a);
return course;
},
).AsQueryable();
var resultList = lookup.Values;
Usando "l. *" En la consulta, tenía la lista de ubicaciones pero sin datos.
Alternativamente, puede usar una consulta con una búsqueda:
var lookup = new Dictionary<int, Course>();
conn.Query<Course, Location, Course>(@"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course)) {
lookup.Add(c.Id, course = c);
}
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(l); /* Add locations to course */
return course;
}).AsQueryable();
var resultList = lookup.Values;
Dapper no es un ORM completo, no maneja la generación mágica de consultas y demás.
Para su ejemplo particular, probablemente lo siguiente funcione:
Toma los cursos:
var courses = cnn.Query<Course>("select * from Courses where Category = 1 Order by CreationDate");
Agarra el mapeo relevante:
var mappings = cnn.Query<CourseLocation>(
"select * from CourseLocations where CourseId in @Ids",
new {Ids = courses.Select(c => c.Id).Distinct()});
Agarra los lugares relevantes
var locations = cnn.Query<Location>(
"select * from Locations where Id in @Ids",
new {Ids = mappings.Select(m => m.LocationId).Distinct()}
);
Hacer un mapa de todo
Dejando esto al lector, usted crea algunos mapas e itera a través de sus cursos poblando con las ubicaciones.
Advertencia: el truco funciona si tienes menos de 2100 búsquedas (servidor SQL), si tienes más, probablemente quieras modificar la consulta para select * from CourseLocations where CourseId in (select Id from Courses ... )
si ese es el caso, también puede tirar todos los resultados de una vez con QueryMultiple
No estoy seguro si alguien lo necesita, pero tengo una versión dinámica sin Modelo para una codificación rápida y flexible.
var lookup = new Dictionary<int, dynamic>();
conn.Query<dynamic, dynamic, dynamic>(@"
SELECT A.*, B.*
FROM Client A
INNER JOIN Instance B ON A.ClientID = B.ClientID
", (A, B) => {
// If dict has no key, allocate new obj
// with another level of array
if (!lookup.ContainsKey(A.ClientID)){
lookup[A.ClientID] = new {
ClientID = A.ClientID,
ClientName = A.Name,
Instances = new List<dynamic>()
};
}
// Add each instance
lookup[A.ClientID].Instances.Add(new {
InstanceName = B.Name,
BaseURL = B.BaseURL,
WebAppPath = B.WebAppPath
});
return lookup[A.ClientID];
}, splitOn: "ClientID,InstanceID").AsQueryable();
var resultList = lookup.Values;
return resultList;
Sé que llegué muy tarde a esto, pero hay otra opción. Puedes usar QueryMultiple aquí. Algo como esto:
var results = cnn.QueryMultiple("select * from Courses where Category = 1 Order by CreationDate; select A.*, B.CourseId from Locations A Inner Join CourseLocations B on A.LocationId = B.LocationId Inner Join Course C On B.CourseId = B.CourseId And C.Category = 1");
var courses = results.Read<Course>();
var locations = results.Read<Location>(); //(Location will have that extra CourseId on it for the next part)
foreach (var course in courses) {
course.Locations = locations.Where(a => a.CourseId == course.CourseId).ToList();
}
No es necesario lookup
diccionario
var coursesWithLocations =
conn.Query<Course, Location, Course>(@"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (course, location) => {
course.Locations = course.Locations ?? new List<Location>();
course.Locations.Add(location);
return course;
}).AsQueryable();