¿Devolver resultados de tipo anónimo?

Resuelto Jonathan S. asked hace 15 años • 16 respuestas

Utilizando el sencillo ejemplo siguiente, ¿cuál es la mejor manera de devolver resultados de varias tablas utilizando Linq to SQL?

Digamos que tengo dos tablas:

Dogs:   Name, Age, BreedId
Breeds: BreedId, BreedName

Quiero devolver todos los perros con sus BreedName. Debería hacer que todos los perros usen algo como esto sin problemas:

public IQueryable<Dog> GetDogs()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select d;
    return result;
}

Pero si quiero perros con razas y pruebo esto tengo problemas:

public IQueryable<Dog> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result;
}

Ahora me doy cuenta de que el compilador no me permite devolver un conjunto de tipos anónimos porque espera perros, pero ¿hay alguna manera de devolver esto sin tener que crear un tipo personalizado? ¿O tengo que crear mi propia clase DogsWithBreedNamesy especificar ese tipo en la selección? ¿O hay otra manera más fácil?

Jonathan S. avatar Feb 11 '09 06:02 Jonathan S.
Aceptado

Tiendo a optar por este patrón:

public class DogWithBreed
{
    public Dog Dog { get; set; }
    public string BreedName  { get; set; }
}

public IQueryable<DogWithBreed> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new DogWithBreed()
                        {
                            Dog = d,
                            BreedName = b.BreedName
                        };
    return result;
}

Significa que tienes una clase extra, pero es rápida y fácil de codificar, fácilmente extensible, reutilizable y con escritura segura.

teedyay avatar Feb 10 '2009 23:02 teedyay

Puedes devolver tipos anónimos, pero realmente no es bonito .

En este caso creo que sería mucho mejor crear el tipo apropiado. Si solo se va a utilizar dentro del tipo que contiene el método, conviértalo en un tipo anidado.

Personalmente, me gustaría que C# obtuviera "tipos anónimos con nombre", es decir, el mismo comportamiento que los tipos anónimos, pero con nombres y declaraciones de propiedades, pero eso es todo.

EDITAR: Otros sugieren devolver perros y luego acceder al nombre de la raza a través de una ruta de propiedad, etc. Ese es un enfoque perfectamente razonable, pero IME conduce a situaciones en las que ha realizado una consulta de una manera particular debido a los datos que desea. uso, y esa metainformación se pierde cuando regresa IEnumerable<Dog>, es posible que la consulta espere que use (digamos) Breeden lugar Ownerde algunas opciones de carga, etc., pero si lo olvida y comienza a usar otras propiedades, su aplicación puede funcionar, pero no tan eficientemente como había previsto originalmente. Por supuesto, podría estar diciendo tonterías, o optimizando demasiado, etc.

Jon Skeet avatar Feb 10 '2009 23:02 Jon Skeet

Sólo para añadir mi granito de arena :-) Recientemente aprendí una forma de manejar objetos anónimos. Solo se puede usar cuando se apunta al marco .NET 4 y solo cuando se agrega una referencia a System.Web.dll, pero es bastante simple:

...
using System.Web.Routing;
...

class Program
{
    static void Main(string[] args)
    {

        object anonymous = CallMethodThatReturnsObjectOfAnonymousType();
        //WHAT DO I DO WITH THIS?
        //I know! I'll use a RouteValueDictionary from System.Web.dll
        RouteValueDictionary rvd = new RouteValueDictionary(anonymous);
        Console.WriteLine("Hello, my name is {0} and I am a {1}", rvd["Name"], rvd["Occupation"]);
    }

    private static object CallMethodThatReturnsObjectOfAnonymousType()
    {
        return new { Id = 1, Name = "Peter Perhac", Occupation = "Software Developer" };
    }
}

Para poder agregar una referencia a System.Web.dll, deberá seguir los consejos de rushonerok : asegúrese de que el marco de destino de su [proyecto] sea ".NET Framework 4", no ".NET Framework 4 Client Profile".

Peter Perháč avatar Jan 07 '2011 11:01 Peter Perháč

¡En C# 7 ahora puedes usar tuplas!... lo que elimina la necesidad de crear una clase solo para devolver el resultado.

Aquí hay un código de muestra:

public List<(string Name, string BreedName)> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
             join b in db.Breeds on d.BreedId equals b.BreedId
             select new
             {
                Name = d.Name,
                BreedName = b.BreedName
             }.ToList();

    return result.Select(r => (r.Name, r.BreedName)).ToList();
}

Sin embargo, es posible que necesites instalar el paquete nuget System.ValueTuple.

Rosdi Kasim avatar Mar 29 '2017 07:03 Rosdi Kasim