Je sais que cette question a déjà été posée mais je n’ai pas trouvé de réponse qui me satisfasse. Ce que j'essaie de faire est de récupérer un DbSet<T>
fonction du nom de son type.
J'ai le suivant:
[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;
}
}
Je n'ai aucun problème à obtenir le type lui-même, que ce soit par simple changement ou réflexion. J'ai cependant besoin de renvoyer le type en tant que dynamique car je ne sais pas quel type DbSet ce sera. Ensuite, quelque part dans le même assemblage, je l’utilise de la manière suivante:
// MyDbContext MyDbContextInstance..
var model = MyDbContextInstance.GetByName_SwitchTest("A");
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException
À ce stade, le model
contient une instance de type InternalDbSet<ModelA>
. À partir de là, toute utilisation que je fais avec l'objet model
une exception RunTimeBinderException: 'Microsoft.Data.Entity.Internal.InternalDbSet' ne contient pas de définition pour 'FirstOrDefault'
En cherchant sur le web, j'ai trouvé un billet de blog expliquant que (dixit son blog):
La raison pour laquelle l'appel à FirstOrDefault () échoue est que les informations de type du modèle ne sont pas disponibles au moment de l'exécution. La raison pour laquelle il n'est pas disponible est que les types anonymes ne sont pas publics. Lorsque la méthode renvoie une instance de ce type anonyme, elle renvoie un System.Object qui référence une instance de type anonyme - un type dont les informations ne sont pas disponibles pour le programme principal.
Et puis il indique qu'une solution:
La solution est en fait assez simple. Il suffit d'ouvrir le fichier AssemplyInfo.cs du projet ClassLibrary1 et de lui ajouter la ligne suivante:
[assembly:InternalsVisibleTo("assembly-name")]
J'ai essayé cette solution sur mon code mais cela ne fonctionne pas. Pour info j'ai une solution asp.net 5 avec deux assemblys fonctionnant sur dnx dotnet46. Une application et une DLL contenant tous mes modèles et DbContext. Tous les appels concernés que je fais sont situés sur la dll cependant.
Cette solution a-t-elle une chance de fonctionner? Est-ce que je manque quelque chose? Des pointeurs seraient grandement appréciés?
Merci d'avance
[MODIFIER]
J'ai essayé de retourner IQueryable<dynamic>
plutôt que dynamic
et je pouvais faire le model.FirstOrDefault();
requête de model.FirstOrDefault();
mais avant tout j'aimerais pouvoir filtrer sur un champ aussi:
var record = model.FirstOrDefault(item => item.MyProperty == true);
Alors, comment l’ai-je fait quand je ne suis pas au courant de <T>
au moment de la compilation?
D'abord, il faut obtenir le type car la méthode DbContext.Set renvoie une instance DbSet non générique pour l'accès aux entités du type donné dans le contexte et le magasin sous-jacent.
public virtual DbSet Set(Type entityType)
Remarque l'argument ici est le type d'entité pour lequel un ensemble doit être renvoyé. Et l'ensemble pour le type d'entité donné est la valeur de retour.
var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == <Pass your table name>);
maintenant une fois j'ai ce type
if(type != null)
{
DbSet context = context.Set(type);
}
Ou un one-liner serait
DbSet mySet = context.Set(Type.GetType("<Your Entity Name>"));