Ho diviso il mio problema in una versione breve e lunga per le persone con poco tempo a disposizione.Architettura plug-in C# con interfacce condivise tra i plugin
Versione corta:
Ho bisogno di un po 'di architettura per un sistema con il fornitore e dei consumatori plugin. I provider dovrebbero implementare l'interfaccia IProvider ei consumatori dovrebbero implementare IConsumer. L'applicazione in esecuzione dovrebbe essere a conoscenza solo di IProvider e IConsumer. Un'implementazione consumer può chiedere all'assembly in esecuzione (tramite un ServiceProcessor) quali provider implementano InterfaceX e ottiene un elenco indietro. Questi oggetti IProvider devono essere castati su InterfaceX (nel consumer) per poter agganciare il consumatore ad alcuni eventi definiti da InterfaceX. Ciò fallirà perché l'assembly in esecuzione in qualche modo non conosce questo tipo di InterfaceX (cast fallisce). La soluzione sarebbe includere InterfaceX in un assembly che sia il plug-in che il riferimento di assembly in esecuzione, ma questo dovrebbe significare una ricompilazione per ogni nuova coppia di provider/consumer ed è altamente indesiderabile.
Qualche suggerimento?
Versione lunga:
che sto sviluppando una sorta di servizio generico che utilizzerà i plugin per il raggiungimento di un più alto livello di riutilizzabilità. Il servizio consiste in una sorta di implementazione del pattern Observer usando Provider e Consumatori. Sia i provider che i consumatori dovrebbero essere plug-in per l'applicazione principale. Permettetemi innanzitutto di spiegare come funziona il servizio elencando i progetti che ho nella mia soluzione.
Progetto A: un progetto di servizio Windows per l'hosting di tutti i plug-in e funzionalità di base. Un progetto Windows Form di TestGUI viene utilizzato per semplificare il debug. Un'istanza della classe ServiceProcessor del progetto B sta eseguendo il plugin. Le sottocartelle "Consumatori" e "Provider" di questo progetto contengono sottocartelle in cui ogni sottocartella contiene rispettivamente un plug-in del provider o del provider.
Progetto B: una libreria di classi che contiene la classe ServiceProcessor (che esegue tutti i plug-in di caricamento e dispatch tra plug-in, ecc.), IConsumer e IProvider.
Progetto C: una libreria di classi, collegata al progetto B, costituita da TestConsumer (che implementa IConsumer) e TestProvider (che implementa IProvider). Un'interfaccia aggiuntiva (ITest, derivata da IProvider) è implementata dal TestProvider.
L'obiettivo qui è che un plug-in Consumer può chiedere al ServiceProcessor quali Provider (che implementano almeno IProvider) che ha). Gli oggetti IProvider restituiti devono essere inoltrati all'altra interfaccia implementata (ITest) nell'implementazione IConsumer in modo che l'utente possa collegare i gestori eventi agli eventi ITest.
All'avvio del progetto A, vengono caricate le sottocartelle contenenti i plug-in consumer e provider. Di seguito sono riportati alcuni problemi che ho riscontrato finora e ho cercato di risolvere.
L'interfaccia ITest utilizzata per risiedere nel Progetto C, poiché ciò si applica solo ai metodi e agli eventi di cui TestProvider e TestConsumer sono a conoscenza. L'idea generale è di mantenere il progetto A semplice e inconsapevole di ciò che i plugin fanno l'uno con l'altro.
Con ITest nel progetto C e codice nel metodo Initialize del TestConsumer che esegue l'IProvider su ITest (questo non dovrebbe fallire in una singola libreria di classi stessa quando un oggetto che implementa ITest è noto come oggetto IConsumer) non valido si verificherebbe un errore di trasmissione.Questo errore può essere risolto posizionando l'interfaccia ITest nel progetto B a cui fa riferimento anche il progetto A. È tuttavia molto indesiderato poiché è necessario ricompilare il progetto A quando viene creata una nuova interfaccia.
Ho provato a mettere ITest in una libreria a classe singola a cui fa riferimento solo il progetto C, poiché solo il fornitore e il consumatore devono essere a conoscenza di questa interfaccia, ma senza successo: quando carica il plugin il CLR afferma che il progetto di riferimento potrebbe non essere trovato Questo potrebbe essere risolto agganciando l'evento AssemblyResolve dell'attuale AppDomain ma in qualche modo ciò sembra indesiderato. ITest è tornato di nuovo al Progetto B.
Ho provato a suddividere il progetto C in un progetto separato per l'utente e il provider e caricare entrambi gli assembly che funzionano correttamente: entrambi gli assembly sono residenti nella raccolta Assemblies o nell'AppDomain corrente: Assembly trovato: Datamex.Projects. Polaris.Testing.Providers, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = 2813de212e2efcd3 montaggio trovati: Datamex.Projects.Polaris.Testing.Consumers, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = ea5901de8cdcb258
Dal momento che il Consumatore utilizza il Fornitore, è stato fatto un riferimento dal Consumatore al Fornitore. Ora l'evento AssemblyResolve sparò di nuovo affermando di cui ha bisogno il seguente file: AssemblyName = Datamex.Projects.Polaris.Testing.Providers, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = 2813de212e2efcd3
Le mie domande: perché è questo ? Questo file è già caricato correttamente? Perché il cast di IProvider su qualche interfaccia so che implementa impossibile? Ciò è probabilmente dovuto al fatto che il programma in esecuzione non conosce questa interfaccia, ma non può essere caricato in modo dinamico?
Il mio obiettivo finale: I plug-in consumer chiedono al ServiceProcessor che Provider ha implementato l'interfaccia x. I provider possono essere convertiti in questa interfaccia x, senza che l'assembly sia a conoscenza dell'interfaccia x.
Qualcuno che può aiutare?
Grazie in anticipo, Erik
A rischio di sembrare glib, è necessario provare a rendere questa domanda più breve. –
Woah ... d'accordo! – Svish
Ho aggiunto una versione breve in cima alla versione lunga :-) Scusate per il post lungo ma è abbastanza difficile descrivere il mio problema. –