Trova un DbSet generico in un DbContext dinamicamente

.net asp.net-mvc-5 c# entity-framework-core

Domanda

So che questa domanda è già stata posta ma non sono riuscito a trovare una risposta che mi soddisfacesse. Quello che sto cercando di fare è recuperare un particolare DbSet<T> base al nome del suo tipo.

Ho il seguente:

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyDllAssemblyName")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyCallingAssemblyName")]

class MyDbContext : DbContext {

    public DbSet<ModelA> A { get; set; }
    public DbSet<ModelB> B { get; set; }

    public dynamic GetByName_SwitchTest(string name) {
        switch (name) {
            case "A": return A;
            case "B": return B;
        }
    }

    public dynamic GetByName_ReflectionTest(string fullname)
    {
        Type targetType = Type.GetType(fullname);
        var model = GetType()
            .GetRuntimeProperties()
            .Where(o => 
                o.PropertyType.IsGenericType &&
                o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
                o.PropertyType.GenericTypeArguments.Contains(targetType))
            .FirstOrDefault();
        if (null != model)
            return model.GetValue(this);
        return null;
    }
}

Non ho problemi a ottenere il tipo stesso, sia tramite un semplice switch o riflesso. Devo comunque restituire il tipo come dinamico poiché non so quale sarà DbSet. Poi da qualche altra parte nello stesso assembly, lo uso in questo modo:

// MyDbContext MyDbContextInstance..
var model = MyDbContextInstance.GetByName_SwitchTest("A");
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException

A questo punto il model contiene un'istanza di tipo InternalDbSet<ModelA> . Da lì, qualsiasi utilizzo che faccio con l'oggetto del model ottengo un oggetto RunTimeBinderException: 'Microsoft.Data.Entity.Internal.InternalDbSet' non contiene una definizione per 'FirstOrDefault'

Investigando sul web, ho trovato un post sul blog che spiega (dixit il suo blog):

il motivo per cui la chiamata a FirstOrDefault () non riesce è che le informazioni sul tipo del modello non sono disponibili in fase di runtime. Il motivo per cui non è disponibile è perché i tipi anonimi non sono pubblici. Quando il metodo restituisce un'istanza di quel tipo anonimo, restituisce un System.Object che fa riferimento a un'istanza di un tipo anonimo, un tipo le cui informazioni non sono disponibili per il programma principale.

E poi indica una soluzione:

La soluzione è in realtà abbastanza semplice. Tutto ciò che dobbiamo fare è aprire AssemplyInfo.cs del progetto ClassLibrary1 e aggiungere la seguente riga: [assembly:InternalsVisibleTo("assembly-name")]

Ho provato questa soluzione sul mio codice ma non funziona. Per informazioni ho una soluzione asp.net 5 con due assembly in esecuzione su dnx dotnet46. Un'app e una dll contenenti tutti i miei modelli e DbContext. Tuttavia, tutte le chiamate interessate che faccio si trovano sulla dll.

Questa soluzione ha qualche possibilità di funzionare? Mi sto perdendo qualcosa ? Qualche suggerimento sarebbe molto apprezzato?

Grazie in anticipo

[MODIFICARE]

Ho provato a restituire IQueryable<dynamic> piuttosto che dynamic e potrei fare il modello di query di model.FirstOrDefault(); ma soprattutto mi piacerebbe poter filtrare anche su un campo:

var record = model.FirstOrDefault(item => item.MyProperty == true);

Risposta popolare

Quindi, come ho fatto quando non sono a conoscenza di <T> durante la compilazione.

Innanzitutto occorre ottenere il tipo come metodo DbContext.Set restituisce un'istanza DbSet non generica per l'accesso alle entità del tipo specificato nel contesto e nell'archivio sottostante.

public virtual DbSet Set(Type entityType)

L'argomento Nota qui è il tipo di entità per cui deve essere restituito un set. E impostare per il tipo di entità dato è il valore di ritorno.

var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == <Pass your table name>);

ora una volta ho questo tipo

if(type != null)
{
DbSet context = context.Set(type);
}

O un solo liner sarebbe

DbSet mySet = context.Set(Type.GetType("<Your Entity Name>"));


Related

Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché