2011-12-27 15 views
14

Versione brevedinamica Risoluzione dell'Assemblea/Gestione

Ho un'applicazione che utilizza un plug-in infrastrutture. I plug-in hanno proprietà configurabili che li aiutano a sapere come fare il loro lavoro. I plug-in sono raggruppati in profili per definire come completare un'attività ei profili sono archiviati in file XML serializzati da DataContractSerializer. Il problema è quando si leggono i file di configurazione, la deserializzazione dell'applicazione deve avere conoscenza di tutti i plug-in definiti nel file di configurazione. Sto cercando un modo per gestire la risoluzione dei plug-in sconosciuti. Vedere la sezione della soluzione proposta di seguito per un paio di idee che ho cercato di implementare, ma sono aperto a qualsiasi cosa (anche se preferirei non dover reinventare l'applicazione).


Particolare

Sfondo

ho sviluppato una sorta di Business Automation Sistema Processo per uso interno per l'azienda che sto attualmente lavorando per in C# 4. Rende uso completo dei 'plug-in' per definire tutto (dalle attività che devono essere eseguite alla definizione di unità di lavoro) e fa molto affidamento su un modello di configurazione dinamico che a sua volta si affida a C# 4/Oggetti dinamici DLR per soddisfare i lavori. È un po 'pesante durante l'esecuzione a causa della sua natura dinamica, ma funziona in modo coerente e funziona abbastanza bene per le nostre esigenze.

Include un'interfaccia utente di configurazione WinForms che utilizza ampiamente Reflection per determinare le proprietà/campi configurabili dei plug-in, nonché le proprietà/campi che definiscono ciascuna unità di lavoro da elaborare. L'interfaccia utente è anche costruita sulla parte superiore del motore BPA, quindi ha una conoscenza approfondita del modello oggetto (libero) messo in atto che consente al motore di svolgere il proprio lavoro, il che, per coincidenza, ha portato a diversi miglioramenti dell'esperienza utente, come , esecuzione del lavoro ad hoc e convalida in fase di configurazione dell'input dell'utente. Ancora una volta c'è spazio per miglioramenti, tuttavia, sembra fare il suo lavoro.

L'interfaccia utente di configurazione utilizza DataContractSerializer per serializzare/deserializzare le impostazioni specificate, pertanto qualsiasi plug-in referenziato dalla configurazione deve essere caricato prima (o al momento della) carico di configurazione.

Struttura

Il motore BPA è implementato come un assembly condiviso (DLL), che fa riferimento il servizio di BPA (un servizio di Windows), la configurazione dell'interfaccia utente (WinForms app), e un plug-in del tester (Versione dell'applicazione console del servizio Windows). Ciascuna delle tre applicazioni che fanno riferimento all'assembly condiviso include solo la quantità minima di codice necessaria per eseguire il proprio scopo specifico. Inoltre, tutti i plug-in devono fare riferimento a un assemblaggio molto sottile che fondamentalmente definisce solo l'interfaccia (o le interfacce) che il plugin deve implementare.

Problema

A causa del modello di estensibilità utilizzato nell'applicazione, c'è sempre stato un requisito che l'interfaccia utente di configurazione viene eseguito dalla stessa directory (sullo stesso PC), come l'applicazione di servizio. In questo modo l'interfaccia utente è sempre a conoscenza di tutti gli assembly di cui il servizio è a conoscenza, in modo da poter essere deserializzati senza incorrere in gruppi mancanti.Ora che ci stiamo avvicinando al roll out del sistema, una richiesta di consentire l'IU di configurazione da remoto su qualsiasi PC nella nostra rete proviene dai nostri amministratori di rete per motivi di sicurezza. Generalmente questo non sarebbe un problema se esistesse sempre un gruppo noto di assembly da distribuire, tuttavia, con la possibilità di estendere l'applicazione utilizzando gli assembly creati dall'utente, ci deve essere un modo per risolvere gli assembly da cui i plug-in può essere istanziato/usato.

proposto (potenzialmente ovvio) Soluzione

Aggiungere un servizio WCF per l'applicazione di servizio per consentire le operazioni CRUD tipici contro le configurazioni che l'istanza di servizio è a conoscenza e rielaborare il UI configurazione ad agire più come SSMS con un modello Connect/Disconnect. Questo in realtà non risolve il problema, quindi dovremmo anche esporre una sorta di ServiceContract dall'applicazione di servizio per consentire l'interrogazione degli assembly a cui sa/ha accesso. Va bene e abbastanza semplice, tuttavia la domanda sorge spontanea, "Quando l'UI dovrebbe informarsi sugli assembly di cui il Servizio è a conoscenza?" In fase di connessione, è possibile inviare tutti gli assembly dal servizio all'interfaccia utente per assicurarsi che sia sempre a conoscenza di tutti gli assembly che il servizio esegue, ma ciò si confonde con i conflitti di gestione di AppDomain (potenzialmente inutilmente) e di assembly. Quindi ho suggerito di collegarmi agli eventi AppDomain.AssemblyResolve/AppDomain.TypeResolve per scaricare solo gli assembly di cui il client non è ancora a conoscenza e solo se necessario. Ciò non risolve necessariamente i problemi di gestione di AppDomain, ma aiuta sicuramente ad affrontare i conflitti di versione e i problemi correlati.

Domanda

Se hai bloccato con me così a lungo mi congratulo e vi ringrazio, ma ora sto finalmente arrivare alla domanda reale qui. Dopo mesi di ricerche e infine giungere a una conclusione, mi chiedo se qualcuno qui ha dovuto affrontare un problema simile e come hai affrontato le insidie ​​e le carenze? Esiste un modo standard di gestire ciò che mi è sfuggito completamente o hai qualche consiglio basato su come hai visto questo gestito con successo in passato? Vedi qualche problema con gli approcci proposti o puoi offrire un'alternativa?

Sono consapevole che non tutti vivono nella mia testa quindi per favore fatemi sapere se avete bisogno di ulteriori chiarimenti/spiegazioni. Grazie!

Aggiornamento

ho dato MEF una scossa giusta e sentire che è troppo semplicistico per i miei scopi. Non è che non potrebbe essere piegato a gestire i requisiti di plug-in della mia applicazione, il problema è che sarebbe troppo ingombrante e sporco per renderlo fattibile. È un bel suggerimento e ha un grande potenziale, ma nel suo stato attuale non è ancora arrivato.

Qualche altra idea o feedback sulle mie soluzioni proposte?

Aggiornamento

Non so se il problema che sto incontrando è semplicemente troppo localizzata, se non sono riuscito a descrivere correttamente quello che sto cercando di realizzare, o se questa domanda è semplicemente troppo irragionevolmente lungo da leggere nella sua interezza; ma le poche risposte che ho ricevuto sono state abbastanza utili da aiutarmi a riflettere diversamente sul problema e identificare alcune carenze in quello che sto cercando.

In breve, quello che sto cercando di fare è prendere tre applicazioni che nel loro stato corrente condividono le informazioni (configurazione/assiemi) usando una struttura di directory comune e cercano di far funzionare quelle applicazioni su una rete con un impatto minimo su usabilità e architettura.

parti di file sembrano come la risposta ovvia a questo problema (come @SimonMourier proposto nei commenti), ma il loro utilizzo si traduce in mancanza di controllo e debugability quando qualcosa va storto. Posso vederli come una valida soluzione a breve termine, ma a lungo termine non sembrano fattibili.

+1

+1 Per una domanda ben descritta! –

+0

Scusa se sembra stupido, ma non puoi semplicemente distribuire i tuoi plugin in modo centralizzato su una condivisione e (a condizione che i diritti e CAS siano a posto) caricarli da questa condivisione, da qualsiasi processo (Servizio, Config, ecc.)? Altra domanda mentre ci sono, sei a conoscenza del fantastico ma poco conosciuto set di spazi dei nomi System.AddIn: http://msdn.microsoft.com/en-us/library/gg145020.aspx? –

+0

@SimonMourier - Grazie per i suggerimenti. Sì, ho sentito parlare di MAF ma è più pesante di quanto stavo cercando quando ho scritto il mio. Per quanto riguarda la distribuzione dei plug-in su una condivisione di file, l'ho considerato per circa 5 secondi e probabilmente potrebbe funzionare ma sembra solo sporco. Penso che preferirei una soluzione più integrata (in poche parole: sono un programmatore, quindi mi piacerebbe dover codificare qualcosa). –

risposta

4

tl; dr, ma sono sicuro al 90% che dovresti dare un'occhiata a MEF.
Quando l'ho visto per la prima volta ero come "aah, un altro acronimo", ma vedrai che è molto semplice, ed è integrato in .NET 4. La cosa migliore è che funziona perfettamente anche su mono ed è una questione di meno di un'ora (compresa la pausa caffè) tra ascoltarlo e compilare mondi ciao per abituarsi alle funzionalità. È davvero così semplice.

In pratica, si "esporta" qualcosa in un assieme e lo si "importa" in un altro (tutto tramite decorazioni di attributi semplici) e si sceglie dove cercarlo (ad esempio, nella directory delle applicazioni, cartella dei plug-in, eccetera).

Modifica: cosa succede se si tenta di scaricare e caricare (e possibilmente cache) i plug-in sul carico di configurazione?

+0

La struttura del plugin è già ben definita e da quello che ho letto su MEF non avrebbe risolto il problema di rendere i plug-in accessibili e risolvibili attraverso la rete. Ho trascurato questa capacità nella documentazione da qualche parte? –

+1

Bene, MEF utilizza [cataloghi] ​​(http://blogs.microsoft.co.il/blogs/bnaya/archive/2010/02/13/mef-for-beginner-catalogs-part-10.aspx), quindi tu probabilmente potrebbe sottoclasse [ComposablePartCatalog] (http://msdn.microsoft.com/en-us/library/system.componentmodel.composition.primitives.composablepartcatalog.aspx) per creare un "NetworkCatalog" che cerchi gli assembly in un percorso di rete (ad esempio, un server FTP). O potresti semplicemente scaricare gli assembly in fase di esecuzione? –

+0

Guarderò nell'approccio al catalogo MEF e possibilmente ruberò alcuni concetti, ma anche se avessi fatto passare l'applicazione a MEF avrei avuto molti difetti uguali a quelli più lucidi. –

0

Forse si può dividere questo problema in due

  1. amministratore consentono agli utenti di scaricare una delle configurazione predefinita (set di librerie) e MEF aiuta a iniettare le dipendenze richieste
  2. ogni attività da utente deve passare attraverso proxy di sicurezza, i moduli plugin non sono autorizzati a chiamare BL direttamente. Il proxy potrebbe corrispondere all'attributo di sicurezza personalizzato e alle attività consentite. cioè

    [MyRole (Nome = new [] { "Security.Action"})] vuoto BlockAccount (string accountid) {} ​​

    [MyRole (Nome = new [] { "Manager.Action" })] CreateAccount void (String username) {}

    [MyRole (Nome = new [] { "Security.View", "Manager.View"})] Lista <> AcountList (predicato p) {}

e consentire f o gruppi di annunci (alcuni descrizione astratta)

  1. corp \ securityOperators = "Sicurezza. *" // consentire le chiamate a tutti manipolazione sicurezza
  2. corp \ HQmanager = "Manager.View" // consentire solo l'accesso vista
  3. corp \ Operatore = "agenzia *."
+1

Forse sono solo fitto, ma ho difficoltà a capire come questo si collega/risponde alla domanda. –

1

Penso che si potrebbe essere vista una soluzione relativamente semplice che deriva in qualche modo l'approccio web.config Microsoft:

avere due se ctions nel file di configurazione:

La sezione 1 contiene informazioni sufficienti sul plug-in (ad es. nome, versione) per permetterti di caricarlo in un dominio app.

La sezione 2 contiene le informazioni serializzate dal plug-in.

Al caricamento del plug-in, passare le informazioni nella sezione 2 e lasciare che il plug-in deserializzi in base alle proprie esigenze.

+0

Posso vedere come sarebbe utile, ma c'è un modo per ottenerlo usando DataContractSerializer per deserializzare la configurazione? –

+0

Sì. Hai due scelte qui, penso: 1) Deserializza solo la seconda sezione con DataContractSerializer. 2) Avere una deserializzazione a 2 passaggi. Il passaggio 1 viene deserializzato in una classe standard contenente i dettagli del plug-in in 1 proprietà e una versione codificata in base 64 (ad esempio) dei dati serializzati tramite plug-in. Passare 2 sarebbe per il plugin per deserializzare i dati in modo appropriato. Spero che abbia senso. –

+0

Quindi, in pratica, i passaggi sono: 1: utilizzare un parser XML per analizzare la configurazione per determinare le sezioni distinte, 2: deserializzare la parte della configurazione che descrive i plug-in a cui si fa riferimento e risolvere i riferimenti e 3: deserializzare parte della configurazione che è in realtà la configurazione del plug-in? Il buco aperto in questa è la seconda parte del passaggio 2. Qual è il modo "approvato" di risolvere i riferimenti che il mio assembly non conosce? –

0

Non sono sicuro di aver completamente compreso il problema ma ritengo che questa situazione richieda la "serializzazione di conservazione del tipo", ovvero il file serializzato contiene abbastanza informazioni sul tipo per deserializzare nuovamente il grafico dell'oggetto originale senza alcun suggerimento dall'applicazione chiamante su quali tipi sono coinvolti.

Ho utilizzato Json.NET per eseguire questa operazione e consiglio vivamente la libreria per la serializzazione di tipo preservativo dei grafici degli oggetti. Sembra che il NetDataContractSerializer può anche fare questo, da the MSDN Remarks

Il NetDataContractSerializer differisce dal DataContractSerializer in un modo importante: la NetDataContractSerializer include informazioni tipo CLR in XML serializzato, mentre il DataContractSerializer non lo fa. Pertanto, NetDataContractSerializer può essere utilizzato solo se i terminali di serializzazione e deserializzazione condividono gli stessi tipi CLR.

Ho scelto Json.NET perché può serializzare POCO senza attributi o interfacce speciali. Sia Json.NET che NetDataContractSerializer ti consentono di utilizzare un numero personalizzato SerializationBinder - qui puoi inserire qualsiasi logica relativa al caricamento degli assiemi che potrebbero non essere ancora stati caricati.

Sfortunatamente, cambiare gli schemi di serializzazione potrebbe essere la modifica "più interessante" da suggerire perché tutti i file esistenti diventeranno incompatibili. Potrebbe essere possibile scrivere un'utilità di conversione che deserializza un file utilizzando il vecchio metodo e serializza il grafico dell'oggetto risultante utilizzando il nuovo metodo.

+0

Apprezzo il suggerimento, tuttavia determinare quali assiemi/tipi sono referenziati dalla configurazione è già stato indirizzato nella mia implementazione. La domanda riguarda maggiormente l'approccio alla scoperta/trasferimento/accesso agli assembly di riferimento in un ambiente client/server. –

+0

Ho aggiunto un altro aggiornamento alla mia domanda che, si spera, aiuterà a spiegare meglio ciò che sto cercando. –

+0

OK, penso che il tuo aggiornamento si restringa sul problema (almeno per la mia comprensione) ma ci sono ancora molti dettagli che non sono direttamente rilevanti. Il contesto dettagliato può essere utile perché le persone possono [suggerire un approccio migliore] (http://blogs.msdn.com/b/ericlippert/archive/2003/11/03/a-parable.aspx), ma la differenza tra la domanda e il contesto dovrebbe essere chiaro. Come è scritto ora, ho difficoltà a distinguere la tua domanda dal tuo contesto. Comunque, buona fortuna (probabilmente farei solo la condivisione dei file, BTW). –