piątek, 9 marca 2012

[MAF 04] Managed Add-in Framework (System.AddIn) – przykład z kompatybilnością [PL]

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:
Promuj

Brak komentarzy:

Prześlij komentarz

Posty powiązane / Related posts