LINQ la Entitățile Schimba Entitate la Runtime

0

Problema

Am si eu o intrebare. Am o dbContext care are peste 200 de clase care reprezintă toate tabelele din baza de date. Toate mesele urmează același format. Este posibil pentru a modifica dinamic codul de la runtime în modul următor?

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);
}

Aici este următorul tip de entitate care este aproape la fel. Singura schimbare este entitatea

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);
}

Există o modalitate de a schimba entitate la runtime sau va trebui să-codul fiecare entitate? Multumesc pentru orice direcție pe această întrebare.

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

Acest lucru ar trebui să lucreze pentru EF6 dar am fost de testare pe EFCore.

Am făcut ceva oarecum similar cu acest lucru atunci când am nevoie să modificați toate DbSets care implementează o anumită interfață.

Aveți două opțiuni pentru situația dumneavoastră. Deoarece toate tipurile de modele nu au o bază comună de tip (altele decât object), ai putea refactor cele generate clase pentru a extrage proprietățile comune la o clasă de bază (de Id, Nameși Type proprietăți).

Aici eu sunt presupunând Coffee1 și Coffee2 mesele trebuie doar tipul de entitate Coffee1 și Coffee2 respectiv.

Opțiunea 1:

Refactor clase și extrage acele proprietăți comune

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; }
}

apoi interogare ar arata astfel

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);
    }
}

Această opțiune este mai mult de tip în condiții de siguranță și mai puțin predispuse la erori.

Opțiunea 2:

Utilizați reflecție asupra IQueryable<object>

Păstrați modele la fel și de a folosi asta:

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);
    }
}

La GenerateGenericCoffee metoda de ajutor

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);
    }
}

Dacă toate de modele conțin Id, Nameși Type,, va fi bine, altfel va trebui să verificați acele proprietăți exista mai întâi înainte de a face GenericCoffeeList element.

2021-11-18 00:47:02

Hank, Multumesc pentru cod. Întrebarea de unde iei BaseCoffee?
Bryan K

La DbSets pe contextul ctxCoin, nu toate tipul argumente pentru cei au aceeași clasă de bază sau se întâmplă doar pentru a avea aceleasi proprietati? adică Ai DbSet<Class1> Coffee1 { get; set; } și DbSet<Class2> Coffee2 { get; set; }și Class1 și Class2 ambele au clasa de baza BaseCoffee cu proprietăți Id, Nameși Type.
Hank McCord

Hank, am folosit enitity cadru pentru a genera fișierele de clasă nu au o bază de clasă. Cadru creează un dbContext numit CoffeeCatalogContext. Îl folosesc pentru a accesa clasele parțială creat de cadru. Sunt, de asemenea, folosind EF de Bază
Bryan K

@BryanK am rescris răspunsul meu să vă dau mai multe opțiuni care să reflecte situația ta. Să sperăm că acest lucru vă ajută!
Hank McCord

Hank. Multumesc pentru cod. Nu am methos IsAssignableTo. App mea este blazor și c# cadru entitate de bază. Din ceea ce am putea spune IsAssignableTo este un .net metodă.
Bryan K

@BryanK e mai nou .NET 5 și 6 comoditatea metodei. Puteți folosi twin typeof(IQueryable<object>).IsAssignableFrom(p.PropertyType) în schimb. Acesta a fost disponibil atunci .NETFramework.
Hank McCord

O să-i dea un try. Multumesc pentru tot ajutorul tău.
Bryan K

Hank, nu primesc nici rezultatele din această parte a codului și pot da seama de ce. var dbSets = ctxCoin.GetType().GetProperties() .Unde(p => p.PropertyType.IsAssignableFrom(typeof(IQueryable<object>))) .Selectați(set => set.GetValue(ctxCoin)) .Cast<IQueryable<obiect>>();
Bryan K
0

Cred că, creați genetic clasa pentru interogare. Atunci când vă schimbați Obiect(TEntity), interogare executat pentru Obiect(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

Vă rugăm să testați codul înainte de a posta.
Gert Arnold

Multumesc Gert. Am putea pierde ceva. Am scris fără ide.
karagoz

Cea mai mare parte de schimbări cosmetice. Încă nu compila.
Gert Arnold

I-am spus că nu scrie cu un compilator. Crezi că acest bloc de cod are sens ?
karagoz

Apoi scrie-l într-un IDE și testați-l. Noi toți ar trebui să facă asta (și prea puțini oameni fac, din păcate). Dacă nu-l compilați nu are sens.
Gert Arnold
0

Eu personal as încercați să utilizați o metodă generică cu un labda care este folosit pentru a converti diferite tipuri de Cafea în GenericCoffeeList.

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

Procedând în acest fel se reduce cantitatea de cod necesar și singurul lucru care trebuie să fie duplciated este funcția pe care este trecut ca toGenericCoffeeList dar, de asemenea, nu are nevoie de refactorizare 200+ clase de a pune în aplicare o interfață.

Această abordare poate fi adaptat în funcție de ceea ce aveți nevoie pentru a face (nu sunt exact sigur ce metodă ar trebui să facă pentru coffeeList nu este niciodată folosit și coinList nu este niciodată declarate)

2021-12-15 12:00:08

Nannana, poate TCoffee fi un șir de caractere care este trecut?
Bryan K

Nu este nevoie pentru a fi tipul pe care doriți să interogare - ar fi folosit ca GetCoffeeList<Coffee1>(coffee1 => new GenericCoffeeList { CoffeeCatalogId = coffee1 .Id, Name = coffee1 .Name, Type = coffee1 .Type })
Nannanas

Asta e ceea ce am crezut. Asta e ceea ce încerc să evit. Am 200+ TEntites deci, eu sunt în căutarea pentru o soluție în cazul în care nu trebuie să-cod greu fiecare.
Bryan K
0

Puteți mapa un Tip la altul de mapare câmpuri astfel:

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())));

Acest cod presupune ca tipurile au proprietăți cu tipuri similare, dar diferite nume. Astfel Dicționar corespunzătoare filednames.

În cazul în care câmpurile se întâmplă să aibă același nume ai putea, desigur, să deducă proprietățile folosind reflecție.

Utilizarea este astfel:

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

În alte limbi

Această pagină este în alte limbi

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