LINQ to entities Cambio de Entidad en tiempo de ejecución

0

Pregunta

Tengo una pregunta. Tengo un dbContext que tiene más de 200 clases que representan todas las tablas en la base de datos. Todas las tablas tienen el mismo formato. Es posible cambiar dinámicamente el código en tiempo de ejecución, de la siguiente manera?

var coffeeList = new ObservableCollection<GenericCoffeeList>();
var query = (from c in ctxCoin.Coffee1
             select new GenericCoffeeList { CoffeeCatalogId = c.Id, Name = c.Name, Type = c.Type })
             .ToList();

foreach (var c in query)
{
    coinList.Add(c);
}

Aquí está el siguiente tipo de entidad que es casi la misma. El único cambio es la entidad

var coffeeList = new ObservableCollection<GenericCoffeeList>();

var query = (from c in ctxCoin.Coffee2
             select new GenericCoffeeList { CoffeeCatalogId = c.Id, Name = c.Name, Type = c.Type })
             .ToList();

foreach (var c in query)
{
    coinList.Add(c);
}

Es allí una manera de cambiar la entidad en tiempo de ejecución o voy a tener que dar el código de cada entidad? Gracias por cualquier orientación sobre esta cuestión.

c# dbcontext dynamic entity
2021-11-17 02:18:10
4
0

Esto debería funcionar para EF6 pero estaba probando en EFCore.

Yo he hecho algo similar a esto cuando he necesitado modificar todos los DbSets que implementar una interfaz específica.

Usted tiene dos opciones para su situación. Dado que todos los de su modelo de tipos no tienen una base común de tipo (aparte de object), podría refactorizar esas clases generadas para extraer las propiedades comunes a una clase base (la Id, Namey Type propiedades).

Aquí estoy asumiendo que tu Coffee1 y Coffee2 las tablas sólo tiene el tipo de entidad Coffee1 y Coffee2 respectivamente.

Opción 1:

Refactorizar clases y extraer las propiedades comunes

public partial class Coffee1 : BaseCoffee {}
public partial class Coffee2 : BaseCoffee {}
public abstract class BaseCoffee
{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Type { get; set; }
}

haga su consulta tendría este aspecto

var dbSets = ctxCoin.GetType().GetProperties()
    .Where(p => p.PropertyType.IsAssignableTo(typeof(IQueryable<BaseCoffee>)))
    .Select(set => set.GetValue(ctxCoin))
    .Cast<IQueryable<BaseCoffee>>();

var coffeeList = new ObservableCollection<GenericCoffeeList>();

foreach (var coffee in dbSets)
{
    var query = coffee.Select(c => new GenericCoffeeList { CoffeeCatalogId = c.Id, Name = c.Name, Type = c.Type });

    foreach (var c in query)
    {
        coffeeList.Add(c);
    }
}

Esta opción es mucho más seguro y menos propenso a errores.

Opción 2:

Utilizar la reflexión sobre IQueryable<object>

Mantener los modelos de la misma y el uso de este:

var dbSets = ctxCoin.GetType().GetProperties()
    .Where(p => p.PropertyType.IsAssignableTo(typeof(IQueryable<object>)))
    .Select(set => set.GetValue(ctxCoin))
    .Cast<IQueryable<object>>();

var coffeeList = new ObservableCollection<GenericCoffeeList>();

foreach (var queryableObject in dbSets)
{
    var query = queryableObject.Select(GenerateGenericCoffee);

    foreach (var c in query)
    {
        coffeeList.Add(c);
    }
}

El GenerateGenericCoffee método auxiliar

GenericCoffeeList GenerateGenericCoffee(object coffeeObject)
{
    var objType = coffeeObject.GetType();

    return new GenericCoffeeList
    {
        CoffeeCatalogId = GetProperty<int>("Id"),
        Name = GetProperty<string>("Name"),
        Type = GetProperty<string>("Type"),
    };

    T GetProperty<T>(string name)
    {
        return (T)objType.GetProperty(name).GetValue(coffeeObject);
    }
}

Si todos los modelos contienen Id, Namey Typeusted estará bien en lo contrario, tendrá que comprobar las propiedades que existen antes de hacer el GenericCoffeeList elemento.

2021-11-18 00:47:02

Hank, Gracias por el código. Pregunta de donde obtienen el BaseCoffee?
Bryan K

El DbSets en su contexto ctxCoin, hacer todo tipo de argumentos a aquellos que tienen la misma base de la clase o que acaba de pasar a tener las mismas propiedades? por ejemplo, Usted tiene DbSet<Class1> Coffee1 { get; set; } y DbSet<Class2> Coffee2 { get; set; }y Clase1 y Clase2 ambos tienen la clase base BaseCoffee con las propiedades Id, Namey Type.
Hank McCord

Hank, he utilizado enitity marco para generar los archivos de clase no comparten una base de clase. El marco de trabajo crea un dbContext llamado CoffeeCatalogContext. Yo lo uso para tener acceso a los parciales de las clases creadas por el marco. También estoy usando EF Core
Bryan K

@BryanK he reescrito mi respuesta para darle más opciones que reflejen su situación. Esperemos que esto ayude!
Hank McCord

Hank. Gracias por el código. No tengo la methos IsAssignableTo. Mi aplicación es blazor y c# entity framework core. Por lo que puedo decir de la IsAssignableTo es una .método de red.
Bryan K

@BryanK Es una más reciente .NETO 5 y 6 de la conveniencia del método. Usted puede utilizar su gemelo typeof(IQueryable<object>).IsAssignableFrom(p.PropertyType) en su lugar. Este ha estado disponible desde .NETFramework.
Hank McCord

Voy a darle una oportunidad. Gracias por toda su ayuda.
Bryan K

Hank, no estoy obteniendo los resultados de esta parte del código y no puedo averiguar por qué. var dbSets = ctxCoin.GetType().GetProperties() .Donde(p => p.PropertyType.IsAssignableFrom(typeof(IQueryable<objeto>))) .Seleccione(juego => configurar.GetValue(ctxCoin)) .Cast<IQueryable<objeto>>();
Bryan K
0

Creo que, de crear genética de la clase para la consulta. Al cambiar de Objeto(TEntity), consulta ejecutada por el Objeto(TEntity).

public class QueryRepo<TEntity> where TEntity : class
{
    private readonly ctxCoin;
    public QueryRepo(){
        ctxCoin = new CtxCoin();
    }

    public IEnumerable<GenericCoffeeList> GetCoffeeList()
    {
        var entity = ctxCoin.Set<TEntity>();
        return (from c in entity
            select new GenericCoffeeList
            {
                CoffeeCatalogId = c.Id,
                Name = c.Name,
                Type = c.Type
            }).ToList();
    }
}
2021-11-17 07:31:06

Por favor el código de prueba antes de su publicación.
Gert Arnold

Gracias Gert. Podría perderme tantos. Escribí sin ide.
karagoz

La mayoría de cambios cosméticos. Todavía no se compila.
Gert Arnold

Me dijo que no escribir con un compilador. Crees que este bloque de código hace sentido ?
karagoz

Luego de escribir en un IDE y probarlo. Lo que todos debemos hacer que (y muy pocas personas lo hacen, por desgracia). Si no se compila no tiene sentido.
Gert Arnold
0

Personalmente me gustaría probar el uso de un método genérico con un labda que se utiliza para convertir los diferentes tipos de Café en GenericCoffeeList.

public IEnumerable<GenericCoffeeList> GetCoffeeList<TCoffee>(Func<TCoffee, GenericCoffeeList> toGenericCoffeeList)
{
    return ctxCoin.Set<TCoffee>()
        .Select(coffee => toGenericCoffeeList(coffee)
        .ToList();
}

Haciendo de esta manera se reduce la cantidad de código necesario y la única cosa que necesita ser duplciated es la función que se pasa como toGenericCoffeeList pero también no requiere de refactorización 200+ clases para implementar una interfaz.

Este enfoque puede ser adaptado dependiendo de lo que usted necesita hacer (no estoy exactamente seguro de lo que su método se supone que debe hacer, porque coffeeList nunca se utiliza y coinList nunca es declarado)

2021-12-15 12:00:08

Nannana, puede TCoffee ser una cadena que se pasa?
Bryan K

No se necesita ser el tipo que desea consultar - sería utilizado como GetCoffeeList<Coffee1>(coffee1 => new GenericCoffeeList { CoffeeCatalogId = coffee1 .Id, Name = coffee1 .Name, Type = coffee1 .Type })
Nannanas

Eso es lo que yo pensaba. Eso es lo que estoy tratando de evitar. Tengo 200+ TEntites, así que estoy buscando una solución donde no tengo que codificar cada uno de ellos.
Bryan K
0

Puede asignar un Tipo a otro de mapeo de los campos, como este:

public static Expression<Func<T, R>> MapFields<T, R>(IDictionary<string, string> fieldNamesMapping) 
            where R: new()
{
    var parameter = Expression.Parameter(typeof(T), "o");  
             
    return Expression.Lambda<Func<T, R>>(
        Expression.MemberInit(
            Expression.New(typeof(R)), 
            GetMemberBindings<T,R>(fieldNamesMapping, parameter)), 
        parameter);
}
      

private static IEnumerable<MemberBinding> GetMemberBindings<T,R>(IDictionary<string, string> fieldNamesMapping,
    ParameterExpression parameter) 
        => fieldNamesMapping
            .Select(o => (MemberBinding)Expression.Bind(
                typeof(R).GetProperty(o.Value) ?? throw new InvalidOperationException(), 
                Expression.Property(parameter, typeof(T).GetProperty(o.Key) ?? throw new InvalidOperationException())));

Este código supone que los tipos tienen propiedades con tipos similares pero con diferentes nombres. Así, el Diccionario con el correspondiente filednames.

Si los campos tienen el mismo nombre que usted podría, por supuesto, inferir las propiedades mediante la reflexión.

El uso es así:

var toBeMapped = new List<FooA> {
                new FooA {A = 1, B = 2, C = 3},
                new FooA {A = 4, B = 5, C = 6},
                new FooA {A = 7, B = 8, C = 9},
                new FooA {A = 10, B = 11, C = 12}
            };

            var result = toBeMapped.AsQueryable().Select(
                MemberBindingExpressions.MapFields<FooA, FooB>(
                    new Dictionary<string, string> {["A"] = "A", ["B"] = "E"})).ToList();

            result[0].Should().Be(new FooB {A = 1, E = 2});
            result[3].Should().Be(new FooB { A = 10, E = 11 });
2021-12-15 14:33:41

En otros idiomas

Esta página está en otros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Slovenský
..................................................................................................................