W ramach kontynuacji serii wpisów związanych Managed Add-in Framework (MAF / System.AddIn) ([1], [2], [3]) chciałbym pokazać przykład pokazujący, w jaki sposób MAF rozwiązuje problem z kompatybilnością wtyczek po zmianie kontraktu.
Niniejszy przykład jest pewnego rodzaju kontynuacją przykładu przedstawionego we wpisie pt.: "Przykład prostej aplikacji z obsługą wtyczek z wykorzystaniem Managed Add-in Framework (System.AddIn)", zakłada on sytuację w której istniejący kontrakt ulega zmianie, by umożliwić przekazywanie parametry do wtyczki. Następnie dodawana jest nowa wtyczka, która
wykorzystuje nowy parametr, a fragmenty istniejącej infrastruktury są modyfikowane tak, by zapewnić obsługę obydwu wersji wtyczek, przy jednoczesnym braku zmian (czy rekompilacji) po stronie poprzedniej wtyczki.
W celu realizacji zamierzonego scenariusza trzeba trzeba odrobinę zmodyfikować istniejącą infrastrukturę (Host, Host view of add-ins, Host adapter), częściowo wybudować nową
(nowy kontrakt, nowy host adapter, nowy add-in adapter, nowy add-in view i oczywiście nowe wtyczki), fragment natomiast pozostanie bez zmian (stara wtyczka, stary widok wtyczki, stary adapter wtyczki i stary kontrakt). Pokazano to na poniższym rysunku:
Wspomniane zmiany przenoszą się na solution
z poprzedniego przykładu następująco:
Teraz zwróćmy uwagę na kod źródłowy. Kontrakt będzie wyglądał
następująco:
[AddInContract] public interface IDoSthContractV2: IContract { string DoSth(string param); }
Nowa wtyczka, jej widok na kontrakt oraz adapter pomiędzy widokiem
wtyczki, a kontraktem:
//Add-in: [AddIn( "DoSthV2SampleString" )] public class SampleString: IDoSthAddInViewV2 { public string DoSth(string param) { return "Hello from Plugin Class, the param is: " + param; } } // Add-in view: [AddInBase] public interface IDoSthAddInViewV2 { string DoSth(string param); } // Add-in adapter: [AddInAdapter] class DoSthViewToContractAddInSideAdapterV2: ContractBase, IDoSthContractV2 { private IDoSthAddInViewV2 _view; public DoSthViewToContractAddInSideAdapterV2( IDoSthAddInViewV2 view ) { _view = view; } public virtual string DoSth(string param) { return _view.DoSth(param); } }
Nową infrastrukturę mamy już za sobą, przyjrzyjmy się teraz
zmianom w istniejącym kodzie. Zacznijmy od adapterów hosta, do
których został dodany jeden nowy, a ten istniejący zmodyfikowany.
Zwróćcie szczególną uwagę na implementację interfejsu widoku.
Jeden z adapterów przekazuje parametr do przez interfejs kontraktu
(dzięki temu zapewniamy obsługę nowych wtyczek), drugi natomiast
pomija parametr i wywołuje metodę DoSth z interfejsu
kontraktu bez parametru (w celu zapewnienia obsługi stary wtyczek
bazujących na starym kontrakcie):
// 1st version support: [HostAdapterAttribute()] public class DoSthHostSideAdapter: IDoSthAppView { private IDoSthContract _contract; private System.AddIn.Pipeline.ContractHandle _handle; //critical fo lifetime management public DoSthHostSideAdapter( IDoSthContract contract ) { _contract = contract; _handle = new ContractHandle( contract ); } public virtual string DoSth(string param) { return _contract.DoSth(); } } // 2nd version support: [HostAdapterAttribute()] public class DoSthHostSideAdapterV2: IDoSthAppView { private IDoSthContractV2 _contract; private System.AddIn.Pipeline.ContractHandle _handle; //critical fo lifetime management public DoSthHostSideAdapterV2( IDoSthContractV2 contract ) { _contract = contract; _handle = new ContractHandle( contract ); } public virtual string DoSth(string param) { return _contract.DoSth(param); } }
Nowy widok po stronie hosta oczywiście jest niemalże identyczny z
nowym kontraktem:
public interface IDoSthAppView { string DoSth(string param); }
Ostatecznie aplikację wczytującą wtyczki zmodyfikowano
nieznacznie, by obsługiwała więcej niż jedną wtyczkę:
class Program { static void Main( string[] args ) { String pipelineInRoot = Environment.CurrentDirectory; string[] warnings = AddInStore.Rebuild( pipelineInRoot ); foreach ( string warning in warnings ) { Console.WriteLine( warning ); } Collection<AddInToken> tokens = AddInStore.FindAddIns( typeof( IDoSthAppView ), pipelineInRoot ); if ( tokens.Count > 0 ) { foreach ( var token in tokens ) { IDoSthAppView plugin = token.Activate<IDoSthAppView>( AddInSecurityLevel.Internet ); Console.WriteLine( "Message from plugin: {0}", plugin.DoSth( "Passed parameter" ) ); } } else Console.WriteLine("No plugins are found"); Console.WriteLine( "Press Enter to exit" ); Console.ReadLine(); } }
Jak widać, dzięki MAF, udało nam się zapewnić wsparcie dla kilku
różnych wersji wtyczek (o różnym kontrakcie), zrobiliśmy to bez
potrzeby zmian (rekompilacji) starej wtyczki.
Zauważcie też, że stosując podobne podejście (ale z wieloma
adapterami po stronie wtyczki) można by również stworzyć wtyczkę
obsługującą więcej niż jedną wersję aplikacji host'a. Tą ideę
przedstawiono na poniższym rysunku:
Brak komentarzy:
Prześlij komentarz