c# - LINQ Outer se une a Dynamic OrderBy
linq-to-sql gridview (3)
En una Declaración de Linq como la siguiente (reducida a partes relevantes para la pregunta):
var sites = from s in DataContext.Sites
join add in DataContext.Address
on s.PrimaryAddress equals add into sa
from a in sa.DefaultIfEmpty()
select new {
s.Id,
s.SiteName,
PrimaryAddress = a
};
El problema que tenemos es que un control basado en última instancia en una combinación GridView / LinqDataSource no puede clasificarse correctamente en la clase unida a PrimaryAddress (lo ignora). Vemos este mismo comportamiento para todas las clases unidas como esta. ¿Hay alguna forma para que GridView maneje esto? O, alternativamente, ¿hay alguna manera a través de Expressions de que podamos manejarlo en código en un OrderBy dinámico?
Notas adicionales: Al agregar .OrderBy(s => s.PrimaryAddress)
obtenemos Cannot order by type ''Address''
se Cannot order by type ''Address''
, al hacer .OrderBy(gs => gs.PrimaryBusinessAddress.AddressLine1)
obtenemos que el Specified method is not supported.
Al hacer la ordenación en LinqDataSource.OrderBy, es demasiado tarde ... solo ordena los registros en la página actual y no el conjunto general.
Para mayor claridad, este es el formato de la Dirección en la grilla:
public partial class Address
{
public override string ToString()
{
return string.Format("{0} {1}{2} {3}",
AddressLine1,
City,
!string.IsNullOrEmpty(State) && State != "None" ? ", " + State : string.Empty,
!string.IsNullOrEmpty(Country) ? string.Format("({0})", Country) : string.Empty);
}
}
Si pudiéramos ordenar por AddressLine1 + City + State + Country
, sería suficiente, pero no estoy seguro de cómo hacerlo a través de un árbol de expresiones ... no importa qué OrderBy especifiquemos a través de expresiones, se vuelve a ordenar por s.SiteName (la clasificación predeterminada en la grilla). Hay un número muy limitado de clases unidas como esta que se muestran en nuestro control de cuadrícula, tener un interruptor y un caso de Expresión para cada uno no sería un problema en absoluto. ¿Alguna idea sobre una solución o un enfoque completamente diferente?
Al agregar .OrderBy (s => s.PrimaryAddress) obtenemos No se puede ordenar por tipo ''Dirección''
Eso es porque PrimaryAddress solo existe en el código. La instrucción LINQ va a estar saliendo y obteniendo todas las filas para hacer una dirección, luego fusionándolas en código. ¿Has probado alguna forma de .OrderBy (a => a.Address)? A saber, haciendo referencia a las columnas de la subtabla en lugar de su nombre derivado.
Estoy acostumbrado a usar Linq to Entities en el código, pero utilizo LINQ to SQL con suficiente frecuencia en LINQPad que me he familiarizado bastante con él. No estoy del todo seguro de entender dónde estás encontrando dificultades, pero creo que lo siguiente debería funcionar:
var sites = from s in DataContext.Sites
orderby s.PrimaryAddress.AddressLine1,
s.PrimaryAddress.City,
s.PrimaryAddress.State,
s.PrimaryAddress.Country
select new
{
s.Id,
s.SiteName,
s.PrimaryAddress
};
Avíseme si hay algo que no entiendo.
Actualizar
No estoy seguro de por qué esto no funciona para ti. Acabo de hacer lo siguiente en LINQPad (LINQ to SQL mode):
from p in person
orderby p.clue_type.clue_type_id,
p.clue_type.clue_type
select new
{
p.person_id, p.clue_type
}
Todos los resultados tenían clue_type = null. LINQ to SQL solo trata las referencias nulas como valores con propiedades all-null. Aquí está el SQL generado:
SELECT TOP (10) [t0].[person_id], [t2].[test], [t2].[clue_type_id], [t2].[clue_type]
FROM [person] AS [t0]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t1].[clue_type_id], [t1].[clue_type]
FROM [clue_types] AS [t1]
) AS [t2] ON [t2].[clue_type_id] = [t0].[clue_type_id]
ORDER BY [t2].[clue_type_id], [t2].[clue_type]
Observe que IZQUIERDA EXTERIOR SE UNE. ¿Esto no hará lo que estás pidiendo?
Actualización 2
Hacer que la consulta sea dinámica puede ser bastante difícil, según cuán dinámico lo esté haciendo. Aquí hay una solución si desea poder ordenar por cualquiera de las propiedades que está devolviendo, en función de un valor de cadena que se transfiere a su método:
public class SiteDisplayInfo
{
public int Id {get;set;}
public string SiteName {get;set;}
public string PrimaryAddress {get;set;}
public static readonly Dictionary<string, Func<IQueryable<Site>, IOrderedQueryable<Site>>> OrderByFuncs =
new Dictionary<string, Func<IQueryable<Site>, IOrderedQueryable<Site>>>
{
{"Id", q => q.OrderBy(s => s.Id)},
{"SiteName", q => q.OrderBy(s => s.SiteName)},
{"PrimaryAddress",
q => q.OrderBy(s => s.PrimaryAddress.AddressLine1)
.ThenBy(s => s.PrimaryAddress.City)}
};
}
...
public IEnumerable<SiteDisplayInfo> GetSites(string orderByString)
{
IQueryable<Site> sites = DataBase.Sites;
if (orderByString != null && SiteDisplayInfo.OrderByFuncs.ContainsKey(orderByString))
{
sites = SiteDisplayInfo.OrderByFuncs[orderByString](sites);
}
var query = from s in sites
select new SiteDisplayInfo
{
Id = s.Id,
SiteName = s.SiteName,
PrimaryAddress = s.PrimaryAddress.AddressLine1 + s.PrimaryAddress.City
};
return query.ToList();
}
Hay algunas otras maneras de hacer algo similar, pero esto le da una idea general.
Acabo de probar una configuración similar a la que tienes, esto debería funcionar:
var sites = from s in dc.Sites
join addr in dc.Addresses
on s.PrimaryAddress equals addr into sa
from a in sa.DefaultIfEmpty()
orderby a.AddressLine1, a.City, a.State, a.Country /* insert orderby here */
select new
{
s.Id,
s.SiteName,
PrimaryAddress = a
};
Si desea cambiar la orientación del pedido, simplemente agregue descendente a cada columna:
orderby a.AddressLine1 descending, a.City descending, a.State descending, a.Country descending
Aunque este enfoque funciona, no es exactamente dinámico. Debería reescribir la consulta para ordenar otra orientación u orden de columna.
Si esto es algo que desea, le aconsejaría usar el método con algunas tuberías adicionales, o el enfoque completamente dinámico utilizando un OrderBy dinámico que le permite especificar el nombre de la columna como un literal. Para este último verifique este tema y este blogpost .