Encuentre un DbSet genérico en un DbContext dinámicamente

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

Pregunta

Sé que esta pregunta ya se ha hecho, pero no pude encontrar una respuesta que me satisficiera. Lo que estoy tratando de hacer es recuperar un DbSet<T> basado en el nombre de su tipo.

Tengo los siguientes

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

No tengo problemas para obtener el tipo en sí, ya sea a través de un simple interruptor o reflexión. Sin embargo, necesito devolver el tipo como dinámico ya que no sé qué tipo de DbSet será. Luego en otro lugar en el mismo ensamblaje, lo uso de esta manera:

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

En este punto, el model contiene una instancia de un tipo InternalDbSet<ModelA> . A partir de ahí, cualquier uso que haga con el objeto model obtengo una excepción RunTimeBinderException: 'Microsoft.Data.Entity.Internal.InternalDbSet' no contiene una definición para 'FirstOrDefault'

Investigando en la web, encontré una publicación en el blog que explica que (dixit su blog):

La razón por la que falla la llamada a FirstOrDefault () es que la información de tipo del modelo no está disponible en tiempo de ejecución. La razón por la que no está disponible es porque los tipos anónimos no son públicos. Cuando el método devuelve una instancia de ese tipo anónimo, devuelve un objeto System.Object que hace referencia a una instancia de un tipo anónimo, un tipo cuya información no está disponible para el programa principal.

Y luego señala que una solución:

La solución es bastante simple. Todo lo que tenemos que hacer es abrir AssemplyInfo.cs del proyecto ClassLibrary1 y agregarle la siguiente línea: [assembly:InternalsVisibleTo("assembly-name")]

Intenté esta solución en mi código pero no funciona. Para información tengo una solución asp.net 5 con dos ensamblados ejecutándose en dnx dotnet46. Una aplicación y un dll que contiene todos mis modelos y DbContext. Todas las llamadas en cuestión que hago están localizadas en el dll sin embargo.

¿Esta solución tiene alguna posibilidad de funcionar? Me estoy perdiendo de algo ? ¿Algún puntero sería muy apreciado?

Gracias por adelantado

[EDITAR]

He intentado devolver IQueryable<dynamic> lugar de dynamic y pude hacer el model.FirstOrDefault(); consulta model.FirstOrDefault(); pero sobre todo me gustaría poder filtrar en un campo también:

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

Respuesta popular

Entonces, ¿cómo lo hice cuando no tengo conocimiento de <T> durante el tiempo de compilación?

Primero debe obtener el tipo como método DbContext.Set devuelve una instancia DbSet no genérica para el acceso a las entidades del tipo dado en el contexto y la tienda subyacente.

public virtual DbSet Set(Type entityType)

Tenga en cuenta que aquí el argumento es el tipo de entidad para el que se debe devolver un conjunto. Y el conjunto para el tipo de entidad dado es el valor de retorno.

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

Ahora, una vez que tenga este tipo

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

O una sola línea sería

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


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué