2015-07-12 11 views
7

Sospetto che ciò non sia possibile, ma non ho visto un no definitivo.È possibile creare un metodo generico su un oggetto dinamico/ExpandoObject

Il mio attuale (di lavoro) l'attuazione è come seguito ..

public static Main(param args[]) 
{ 
    dynamic Repository = GetRepository(); 

    var query = (Repository.QueryUser() as IQueryable<User>) 
       .Where(user => user.Name.ToLower().Contains("jack")); 
} 

public static dynamic GetRepository() 
{ 
    dynamic repo = new System.Dynamic.ExpandoObject();   
    repo.QueryUser = new [] { new User() { Name = "Jack Sparrow"}}.AsQueryable<User>(); 
    return repo; 
} 

per deridere meglio il (comunemente utilizzata) Interfaccia repository con i metodi generici, vorrei implementare qualcosa di simile al seguente:

public interface IRepository 
{ 
    IQueryable<T> Query<T>(); 
} 

public static Main(param args[]) 
{ 
    IRepository Repository = GetRepository(); // return a dynamic 

    var query = Repository.Query<User>() 
       .Where(user => user.Name.ToLower().Contains("jack")); 
} 


public static dynamic GetRepository() 
{ 
    dynamic repo = new System.Dynamic.ExpandoObject(); 

    // the issue is on the following line 
    repo.Query<User> = new [] { 
          new User() { 
           Name = "Jack Sparrow" 
          } 
         }.AsQueryable<User>(); 
    return repo; 
} 

Esiste una possibile soluzione per questo (utilizzando qualcosa nello spazio dei nomi System.Dynamic forse)?

+0

Ti andrebbe bene creare una classe con un metodo 'Query '? – usr

+0

Sospetto che ciò non sia possibile, poiché i modelli vengono compilati in classi e chiamate univoche. Potrebbe essere possibile emettendo il codice IL, comunque. – Rob

+0

http: // stackoverflow.it/questions/3712732/how-to-create-a-generic-list-con-a-dynamic-object-type? rq = 1 ... hmm ... questo potrebbe dimostrarsi relativo –

risposta

4

Ne dubito, è possibile solo ottenere o impostare "proprietà" (o events) di un ExpandoObject, non definire nuovi metodi. Se si desidera farlo, sarà necessario creare il proprio oggetto dinamico per il quale si aggiungono i propri membri.

Ma mi sento di doverlo dire, perché non usare la composizione invece? Crea una classe a cui aggiungi i tuoi metodi e disponi di una proprietà per l'expando.

class MyClass 
{ 
    public dynamic Expando { get; } = new ExpandoObject(); 
    public void MyMethod<T>() { } 
} 

Se è assolutamente voluto fare questo, è possibile creare un oggetto dinamico con una dinamica involucro oggetto meta su una ExpandoObject. Quindi nel wrapper, inoltra tutti i binding ai tuoi membri al tuo oggetto dinamico e tutti gli altri al expando. L'oggetto sarebbe quello che tu definisci, con la funzionalità di un expando.

esempio,

// be sure to explicitly implement IDictionary<string, object> 
// if needed forwarding all calls to the expando 
class ExtendedExpandoObject : IDynamicMetaObjectProvider 
{ 
    DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) => new MyMetaObject(parameter, this); 

    public ExtendedExpandoObject(ExpandoObject expandoObject = null) 
    { 
     Value = expandoObject ?? new ExpandoObject(); 
    } 
    public ExpandoObject Value { get; } 

    // the new methods 
    public string GetMessage() => "GOT IT!"; 
    public string GetTypeName<T>() => typeof(T).Name; 

    // be sure to implement methods to combine results (e.g., GetDynamicMemberNames()) 
    class MyMetaObject : DynamicMetaObjectWrapper 
    { 
     public MyMetaObject(Expression parameter, ExtendedExpandoObject value) 
       : base(new DynamicMetaObject(parameter, BindingRestrictions.Empty, value)) 
     { 
      var valueParameter = Expression.Property(
       Expression.Convert(parameter, typeof(ExtendedExpandoObject)), 
       "Value" 
      ); 
      IDynamicMetaObjectProvider provider = value.Value; 
      ValueMetaObject = provider.GetMetaObject(valueParameter); 
     } 
     protected DynamicMetaObject ValueMetaObject { get; } 

     public override DynamicMetaObject BindGetMember(GetMemberBinder binder) 
     { 
      if (IsMember(binder.Name)) 
       return base.BindGetMember(binder); 
      return ValueMetaObject.BindGetMember(binder); 
     } 

     public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) 
     { 
      if (IsMember(binder.Name)) 
       return base.BindSetMember(binder, value); 
      return ValueMetaObject.BindSetMember(binder, value); 
     } 

     private bool IsMember(string name) => typeof(ExtendedExpandoObject).GetMember(name).Any(); 
    } 
} 

Con questo, si potrebbe usare come si farebbe con qualsiasi oggetto expando.

dynamic expando = new ExtendedExpandoObject(); 
Console.WriteLine(expando.Value);   // the original expando 
expando.Length = 12;      // set a new property on the expando 
Console.WriteLine(expando.Length);   // get a property on the expando 
Console.WriteLine(expando.GetMessage()); // call the new method 
Console.WriteLine(expando.GetTypeName<ExtendedExpandoObject>()); // call the generic method 
Console.WriteLine(expando.Value.Length); // get the property on the original expando 

ti basta la DynamicMetaObjectWrapper:

public abstract class DynamicMetaObjectWrapper : DynamicMetaObject 
{ 
    protected DynamicMetaObjectWrapper(DynamicMetaObject metaObject) 
      : base(metaObject.Expression, metaObject.Restrictions, metaObject.Value) 
    { 
     BaseMetaObject = metaObject; 
    } 
    public DynamicMetaObject BaseMetaObject { get; } 

    public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) => BaseMetaObject.BindBinaryOperation(binder, arg); 
    public override DynamicMetaObject BindConvert(ConvertBinder binder) => BaseMetaObject.BindConvert(binder); 
    public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) => BaseMetaObject.BindCreateInstance(binder, args); 
    public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) => BaseMetaObject.BindDeleteIndex(binder, indexes); 
    public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) => BaseMetaObject.BindDeleteMember(binder); 
    public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) => BaseMetaObject.BindGetIndex(binder, indexes); 
    public override DynamicMetaObject BindGetMember(GetMemberBinder binder) => BaseMetaObject.BindGetMember(binder); 
    public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) => BaseMetaObject.BindInvoke(binder, args); 
    public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) => BaseMetaObject.BindInvokeMember(binder, args); 
    public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) => BaseMetaObject.BindSetIndex(binder, indexes, value); 
    public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) => BaseMetaObject.BindSetMember(binder, value); 
    public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) => BaseMetaObject.BindUnaryOperation(binder); 
    public override IEnumerable<string> GetDynamicMemberNames() => BaseMetaObject.GetDynamicMemberNames(); 
} 
+0

"Ne dubito, puoi solo ottenere o impostare" proprietà "(o eventi) di un ExpandoObject, non definire nuovi metodi.". Questo non è vero. È possibile aggiungere nuovi metodi su ExpandoObject. Controlla la documentazione: https://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28v=vs.110%29.aspx Ecco l'esempio dei documenti: 'expando dinamico = new ExpandoObject(); ' ' expando.number = 10; ' ' expando.Increment = (Action) (() => {expando.number ++;}); ' ' Console.WriteLine (expando.number); // Stampa 10' 'expando.Increment();' 'Console.WriteLine (expando.number); // Stampa 11' –

+0

Questo non è un metodo, ovvero una proprietà che contiene un delegato. Non è lo stesso. Non può partecipare al sovraccarico del metodo, tra le altre cose. –

+0

Sì, hai ragione, in quanto ExpandoObject non è il tipo dichiarante del metodo, ma contiene semplicemente un riferimento a un metodo in una proprietà. Questo è anche documentato nel link che ho fornito. Tuttavia, i documenti lo spiegano ancora come un metodo "aggiunto a ExpandoObject". Un delegato è un riferimento a un metodo (in questo caso un lambda). Se è memorizzato in una proprietà sulla dinamica, non lo degrada dall'essere un metodo. –

3

L'implementazione oggetto expando ipotetica non "soddisfare" l'interfaccia; non ha un metodo generico Query<T> che può essere chiamato come Query<Foo>() o Query<Bar>(). Ha solo un metodo non generico Query() che restituirà IQueryable<User>. Detto questo, non sembra che Query<T> sia una cosa ragionevole da inserire nell'interfaccia, specialmente senza vincoli di tipo.

Io suggerirei di usare sia utilizzando metodi non generici come QueryUsers, QueryFoos ecc, o che ha un interfaccia IRepository<T> che specifica un metodo IQueryable<T> Query(). Ecco come è possibile utilizzare la libreria Impromptu-Interface per l'attuazione del secondo approccio:

using ImpromptuInterface; 
using ImpromptuInterface.Dynamic; 
using System.Linq; 

public interface IRepository<T> 
{ 
    IQueryable<T> Query(); 
} 

public class User 
{ 
    public string Name { get; set; } 
} 

public class Program 
{ 
    public static void Main(params string[] args) 
    { 
     IRepository<User> repo = GetUserRepository(); // dynamically construct user repository 

     var query = repo.Query() 
        .Where(user => user.Name.ToLower().Contains("jack")); 
    } 

    public static IRepository<User> GetUserRepository() 
    { 
     var repo = new 
     { 
      Query = Return<IQueryable<User>>.Arguments(() => new[] { 
       new User() { 
        Name = "Jack Sparrow" 
       } 
      }.AsQueryable()) 
     }.ActLike<IRepository<User>>(); 

     return repo; 
    } 
} 

Usando questo approccio, il repository di cemento sarebbe semplicemente implementare un'interfaccia IRepository<T> per ogni T che è in realtà un tipo di modello.

+0

Una raccomandazione e un approccio molto interessanti –