Este artigo foi traduzido por máquina.

Fundações

Fluxos de trabalho do controle de versão

Matthew Milner

Download do código disponível na Galeria de código do MSDN
Procure o código on-line

Conteúdo

Os problemas
Versão do .NET
Controle de versão com definições de fluxo de trabalho XOML
Versão de atividade
Serviços de fluxo de trabalho de controle de versão
A atualização dinâmica

No qualquer aplicativo, há uma coisa que você pode estar certo de: alteração ocorre. Um dos problemas mais comuns que localizar os desenvolvedores enfrentando dificuldades com é como lidar com os fluxos de trabalho do controle de versão e suas classes relacionadas. Na coluna deste mês, VOU discutir os problemas principais relacionados à versão do fluxo de trabalho e fornecer Meus recomendações para fazer alterações em definições de fluxo de trabalho, atividades e serviços de fluxo de trabalho.

fig01.gif

Figura 1 fluxos de trabalho simples que podem persistir

Se você criar um aplicativo .NET hoje, digamos, um aplicativo Windows Presentation Foundation (WPF) ou ASP.NET — você pode pensar sobre controle de versão, ou você não pode. Quando você deseja implantar atualizações em um aplicativo ASP.NET, você normalmente ter um processo para simplesmente copiar pela todos ou alguns dos componentes incluindo páginas, binários e configuração. Em algumas situações, talvez seja necessário preocupado com essa implantação, como certificando-se nenhuma solicitação no momento está executando, mas esse processo é geralmente gerenciável. Caso o motivo pelo qual é implantar novas versões de aplicativos de fluxo de trabalho tão difícil?

Fluxos de trabalho permitem que você o modelo de processo comercial de execução demorada ou lógica comercial. Além disso, o Windows Workflow Foundation (WF) fornece serviços de persistência que permitam para o estado de instâncias individuais de um processo comercial sejam salvos para um armazenamento durável, como um banco de dados Microsoft SQL Server. Que salva o estado é composto de objetos .NET serializados; e nele estará ocorrendo o problema.

Considere a possibilidade de um fluxo de trabalho que usa alguns tipos de .NET como dados, digamos, uma classe Order. O fluxo de trabalho for iniciado com um objeto de Order passado no como um parâmetro, em seguida, persistido para o banco de dados, seu estado contendo o objeto serializado do pedido. Agora altere a classe de objeto de Order e recriar a biblioteca de aplicativo. Quando que persistentes fluxo de trabalho precisa continuar porque um atraso expirou ou algum evento é aumentado para o fluxo de trabalho, o objeto de Order precisa ser desserializado. Infelizmente, porque esse tipo foi alterado, a desserialização falha, emitir uma exceção.

Algumas alterações de causar problemas com a desserialização porque a serialização binária de .NET padrão falha e outros problemas ocorrem devido a implementação do WF de serialização. Independentemente da causa, o resultado final é o mesmo: uma exceção ao tentar carregar um fluxo de trabalho que faz referência a tipos que foram alterados. Talvez esses tipos de classes ou interfaces que você criar ou podem ser atividades personalizadas que você tenha escrito.

Para fornecer um exemplo concreto, considere o fluxo de trabalho na Figura 1 . Ele simplesmente grava alguns dados no console de usando uma atividade WriteLine personalizada, atrasos (que também permite que persistem) e, em seguida, grava mais dados ao console.

Agora, se eu iniciar uma instância deste fluxo de trabalho e permitir que ele execute para o ponto de ociosidade — o atraso — persistirão no banco de dados. Eu posso desligar o tempo de execução e começar a trabalhar na minha versão 1.1 do fluxo de trabalho. Eu simplesmente irá adicionar uma atividade ao final da definição do fluxo de trabalho para gravar mais dados do console, como mostrado na Figura 2 .

Iniciando o tempo de execução sem iniciar outra instância de fluxo de trabalho fará com que o serviço de persistência pesquisar o banco de dados procurando timers expirados. Quando ele encontra a instância existente, o serviço de persistência tentará carregar o objeto e obterá uma exceção. Poderá detectar esse erro ao registrar um manipulador de eventos para o evento ServicesExceptionNotHandled no WorkflowRuntime. O que eu verá é que o serviço de persistência irá disparar um evento para a exceção com a mensagem "índice estava fora dos limites da matriz."

fig02.gif

A Figura 2 a modificação de definição do fluxo de trabalho

O problema aqui é que eu alterou a definição de meu tipo de fluxo de trabalho e ele não coincida mais a definição que existia quando o fluxo de trabalho foi mantido. Quando o serviço de persistência tenta desserializar o fluxo de trabalho, recebo exceções porque a nova definição de tipo contém campos que não existiam quando a instância foi serializada. O código de desserialização é ler os campos no novo tipo e tentar carregar os valores dos dados serializados. Como alguns campos não existir quando a instância foi serializada, uma exceção é lançada durante a tentativa de acessar os dados.

Versão do .NET

Uma maneira de solucionar os problemas de versão é aproveitar o sistema de controle de versão embutido no .NET Framework. Desde a versão 1.0 do .NET Framework, o controle de versão de módulos (assemblies) tem sido suportado e foi a única maneira de ativar o suporte lado a várias versões de assemblies. Em resumo, cada assembly pode ter um número de versão anexado a ele e quando módulos (assemblies) estiver conectado com uma chave, várias versões do assembly podem ser implantados no global assembly cache (GAC). Para obter mais informações sobre versões de montagem e assemblies de nome fortes, consulte oDocumentação do MSDN em assemblies.

Isso permite que você criar seus fluxos de trabalho e atividades em projetos com uma versão 1.0, assiná-las e implantá-los no GAC. Agora, quando você iniciar um fluxo de trabalho e ele persista, as informações de versão serão parte do estado do serializado. Se você alterar o fluxo de trabalho, é importante alterar a versão do assembly e outros assemblies que o fluxo de trabalho depende se os alterou, também. Por exemplo, se você tivesse o fluxo de trabalho do exemplo anterior e tiver feito uma alteração para a atividade WriteLine e a definição de fluxo de trabalho, em seguida, você precisará incrementa o número de versão para cada projeto e implantar as atualizações ao GAC.

Neste ponto, você terá que versão 1.0 e a versão 1.1 do fluxo de trabalho implantado no GAC. Quando um fluxo de trabalho versão 1.0 é carregado do armazenamento de persistência, o tempo de execução não poderão resolver a definição de tipo para a versão 1.0 e usar essas informações de tipo ao desserializar o fluxo de trabalho. Fluxos de trabalho qualquer versão 1.1 foram mantidos também funcionaria como o tempo de execução deve ser capaz de localizar a definição para a versão 1.1 tipos, muito. Basicamente, contanto que os assemblies que contêm os tipos de fluxo de trabalho e suas classes relacionadas podem ser resolvidos e coincidir com a definição original, o fluxo de trabalho será desserializar corretamente e o processamento pode continuar. Você pode ver como esse processo funciona na Figura 3 onde as versões diferentes são carregadas do banco de dados, e seus tipos resolver corretamente quando encontrado no GAC.

fig03.gif

A Figura 3 lado a lado .NET versão

Um ponto importante a observar é que fluxos de trabalho da versão 1.0 continuarão funcionando e processamento, mas eles ainda são baseados na definição de versão 1.0 do fluxo de trabalho. Isso significa que alterações que você apresentado na versão 1.1 do fluxo de trabalho não estará presentes na definição de fluxo de trabalho. Em outras palavras, todas as atividades você adicionados, removidos ou alterados caso contrário, não estará presentes da definição de fluxo de trabalho e você não verá o impacto dessas alterações em fluxos de trabalho as versão 1.0. Logo, abordarei um recurso conhecido como atualização dinâmica que fornece a capacidade de alterar os fluxos de trabalho versão 1.0 existente e em processo.

A simples presença lado a versões do fluxos de trabalho e assemblies relacionados não significa que as coisas rolo junto bem, no entanto. Seu aplicativo de host provavelmente é construído em uma determinada e provavelmente o mais recente, versão dos fluxos de trabalho para que ele possa interagir com eles. Isso pode causar problemas como um host interno com a versão 1.1 do fluxo de trabalho será ter problemas ao tentar interagir persistentes fluxos de trabalho versão 1.0 que foram criados com a versão 1.0 das classes relacionadas.

Uma área onde esse problema geralmente vem backup para os desenvolvedores é ao usar ExternalDataExchangeService em conjunto com atividades HandleExternalEvent e CallExternalMethod para comunicação local. Muitas pessoas, pensando que estão fazendo a coisa certa para controle de versão, siga as etapas anteriores para que as versões 1.0 e 1.1 estejam disponíveis. No entanto, quando o host tenta enviar dados para o fluxo de trabalho por meio de um evento, o tipo da interface utilizado não corresponde. O host está usando versão 1.1 da interface, enquanto a instância do fluxo de trabalho foi criada com a versão 1.0.

Entender por que este é um problema, lembre-se como comunicações realmente funcionam no WF. (Para obter comunicações de fluxo de trabalho no, consulte oEdição de setembro de 2007 da fundações.) A atividade HandleExternalEvent cria uma fila para receber dados e o nome da fila que inclui as informações de tipo para a interface. Quando dados são enviados do host, ele deve ser enviado para a fila correta. ExternalDataExchangeService (EDS) usa as informações de tipo e o nome do evento para criar o que acredita é o nome de fila apropriada. Infelizmente, como a fila foi criada com base na versão 1.0 da interface e os EDS está criando o nome de fila com base na versão 1.1, os dois objetos IComparable não coincidirá e a fila não será encontrada.

Para totalmente solucionar esse problema, o aplicativo host precisa elevar eventos na interface da versão 1.0. Duas maneiras possíveis para fazer isso incluem reflexão e herança. A primeira opção envolve o uso reflexão para carregar a versão original do serviço local e adicioná-lo a EDS, após o qual você acionar um evento nessa interface, novamente usando reflexão.

A segunda opção envolve a criação de um novo assembly em que você criar versão 1.1 da sua interface e fazer com ele derivar a interface de versão 1.0, permitindo que você adicionar eventos ao tipo derivado ao não alterar o tipo base. Dessa forma, se você gerar um evento que foi definido na versão 1.0 da interface, o nome de fila correta será obter criado e a mensagem entregue mesmo se você chamar essa operação através o tipo derivado. A chave é que a versão 1.0 da interface deve permanecer inalterada e implantado, e a nova interface e o serviço devem derivar da interface versão 1.0. a Figura 4 mostra essa abordagem.

fig04.gif

A Figura 4 derivados interfaces para comunicação local

Observe que somente o serviço derivado e interface são adicionados para os EDS. Isso permite que o host para interagir com um único serviço, mas para disparar eventos com base em várias interfaces. O objetivo é manter a interface de versão 1.0 em torno e em uso, para que o EDS cria nomes de fila que correspondem aos quais os fluxos de trabalho serão criado. Observe também que isso envolve criar um totalmente novo projeto em vez de simplesmente criar uma nova versão da biblioteca de comunicação. Untenable é a palavra que vem à mente ao pensar em usar esta abordagem de longo prazo.

Em geral, minha recomendação em torno de comunicações é evitar as atividades de comunicação local por esse motivo e usar as atividades personalizadas que criar filas com base em nomes mais simples, como seqüências de caracteres, removendo muita a dependência de tipo entre o fluxo de trabalho e o host. A sugestão é usar os serviços de fluxo de trabalho que fornecem recursos adicionais em torno de receber solicitações e roteamento-los para a versão correta do fluxo de trabalho, que discutirei em breve.

Controle de versão com definições de fluxo de trabalho XOML

A primeira etapa de tornar vida mais fácil quando se trata de controle de versão é usar fluxos de trabalho com base em XOML sem compilá-los. Esse modelo realmente declarativo oferece muitos benefícios com controle de versão. Consulte o problema de maio de 2008 de MSDN Magazine para obter detalhes "Carregar modelos de fluxo de trabalho no WF"). Eu abordará os benefícios para versão aqui.

Ao criar fluxos de trabalho por meio de uma abordagem de código, você está definindo um novo tipo. Por exemplo, se você criar um fluxo de trabalho de código novo usando o modelo de projeto ou item do Visual Studio, você está definindo uma nova classe que deriva de um tipo nos assemblies do WF. Isso é o motivo principal para os problemas descritos anteriormente porque quando você altera o fluxo de trabalho, adicionando ou removendo as atividades, você está alterando o tipo. Isso não acontecer quando você cria fluxos de trabalho XOML.

Um fluxo de trabalho XOML que não é compilado é simplesmente um documento XML que descreve uma coleção de tipos existentes. Isso pode parecer uma diferença sutil, mas descrever os tipos existentes em vez de criar um novo tipo faz grande diferença.

Eis uma definição de fluxo de trabalho XOML:

<SequentialWorkflowActivity x:Name="Workflow2" 
    xmlns:ns0="clr-namespace:SimpleWorkflows" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
  <ns0:WriteLineActivity x:Name="Hello" OutputText="Hello" />
  <DelayActivity TimeoutDuration="00:00:03" x:Name="SmallDelay" />
  <ns0:WriteLineActivity x:Name="World" OutputText="World" />
</SequentialWorkflowActivity>

Você pode ver que o elemento raiz é SequentialWorkflowActivity, que é definido no assembly System.Workflow.Activities. Esse tipo é versão, como 3.0 e nonchanging. Se você criar um fluxo de trabalho usando esse tipo como a atividade raiz e o fluxo de trabalho é serializado e persistente, você poderá ter certeza que, quando o fluxo de trabalho é carregado e desserializado, o tempo de execução será consiga localizar a versão 3.0 do SequentialWorkflowActivity no GAC.

Como alterar a definição de fluxo de trabalho, adicionando ou removendo atividades? Em uma definição de fluxo de trabalho XOML, simplesmente você está alterando alguns XML, não altera qualquer tipo. Mesmo se você alterar o elemento raiz da definição de fluxo de trabalho para algum outro tipo, você não está alterando um tipo do qual dependem as instâncias de fluxo de trabalho existente. Na verdade, depois que um fluxo de trabalho é criado de XOML, que XOML poderia não ser referenciado novamente pelo tempo de execução durante a vida útil dessa instância.

Usar fluxos de trabalho com base em XOML significa que você não é preciso para versão seus fluxos de trabalho porque você não está definindo tipos. Porque você não tem nome forte e versão seus fluxos de trabalho, você também não têm que nomear fortemente e versão que o outro tipos, como interfaces de comunicações locais e entidades comerciais. É muito mais fácil fazer alterações incondicionais, como adicionar membros ou métodos de tipos, sem muito trabalho em seu aplicativo.

A desvantagem a trabalhar com os fluxos de trabalho XOML é que você não tem pouco ou nenhum acesso ao código, seja o que é encapsulado em suas atividades, para que você acaba escrevendo atividades mais. Mas como atividades são apenas classes, isso não é um bloqueador de grande e permite que você capturar métodos que podem ser usados para fornecer lógica ou condição verificando atividades em um fluxo de trabalho XOML de manipulação de eventos.

Além disso, como a classe SequentialWorkflowActivity não tem as propriedades de uso para o caso comercial definido, você não consiga passar parâmetros para o método CreateWorkflow. Felizmente, você pode criar seu próprio tipo de atividade raiz, derivando de um dos tipos fluxo de trabalho incluída e adicionar seus propriedades necessárias não existe ou começar seu fluxo de trabalho com uma atividade de recebimento ou uma atividade personalizada para que você pode enviar dados iniciais logo após iniciar o fluxo de trabalho.

Finalmente, procurando frente, fluxos de trabalho totalmente declarativos com base em XAML são a opção de modelagem principal vindo no WF 4.0. Muitos aprimoramentos estão sendo feitos para os recursos, incluindo substituindo eventos por que estão atualmente sendo chamado um ActivityAction. Essencialmente, isso fornecerá você uma maneira de modelo delegados usando atividades e ter a atividade de executar essas atividades em vez de chamar um delegado.

Versão de atividade

Fluxos de trabalho são atividades, para que ela segue que muitas das questões discutidas até o momento com fluxo de trabalho tipos aplicada aos tipos de atividade bem. Na verdade, assim como um tipo de fluxo de trabalho causa problemas se você adicionar atividades, se você adicionar propriedades para uma atividade digite e fazer não versão assembly atividade, você receberá exceções na desserialização do fluxo de trabalho. Infelizmente, atividades precisam ser classes e devem ser compilados tipos, portanto não há nenhuma solução XOML fácil como com os fluxos de trabalho.

A resposta curta para atividades é que você deve versão eles usando técnicas de controle de versão do .NET quando você altera a interface para a atividade de qualquer forma. Isso permite que fluxos de trabalho existentes e novos fluxos de trabalho para fazer referência a versão correta e executado corretamente. Tenha em mente, entretanto, que, se você estiver corrigir um defeito em um método dentro da atividade que não é alterado a interface, você pode fazer a correção e implantar o assembly com o mesmo número de versão. Quando a atividade é carregada e executada por fluxos de trabalho novos ou existentes, o código correto será executado.

A resposta longa envolve uma compreensão profunda dos mecanismos de serialização no .NET Framework, que está além do escopo deste artigo. No entanto se existem duas coisas de chaves pode compartilhar que pode fornecer a maioria dos vantagem com a menor quantidade de investimento.

Primeiro, se você estiver adicionando propriedades ou campos a sua atividade, você pode marcar os campos com o atributo não serializado para evitar a exceção de índice de limites na desserialização de fluxos de trabalho criados com a versão anterior da sua atividade.

Em segundo lugar, você pode substituir o método OnActivityExecutionContextLoad para inicializar qualquer estado que não foi desserializado automaticamente. Este método é chamado quando a atividade é recriada de estado persistente ou quando um novo contexto de execução é criado. Ambas essas técnicas juntas podem ajudar a fazer alterações em atividades sem exigir que você fortemente nomeá-los e alterar números de versão.

Serviços de fluxo de trabalho de controle de versão

Serviços de fluxo de trabalho fornecem desafios semelhantes para comunicações locais bem como outras oportunidades para separação mensagens de fluxos de trabalho. Como um serviço de fluxo de trabalho é criado usando um contrato de serviço, qualquer versão do serviço ou dados contratos significa que o fluxo de trabalho terá que ser refeitas contra as novas versões dos contratos. Quando uma mensagem chega em um ponto de extremidade de serviço, serão recebido por um serviço usando um contrato que possui uma versão específica. Se o fluxo de trabalho para o qual a mensagem é se foi criado com a mesma versão do contrato, as coisas funcionam bem. No entanto, se o fluxo de trabalho foi criado com a versão anterior do contrato e, em seguida, persistir, em seguida, a mensagem não pode ser entregue. Para oferecer suporte a duas versões dos contratos, você precisará implantar duas versões do serviço.

Embora seja infeliz que você não pode aproveitarem os recursos de controle de versão inerentes no WCF (Windows Communication Foundation) que faria permitem que uma nova versão do serviço para processar mensagens de clientes criados com a versão antiga, o WCF fornecem um mecanismo para gerenciar esse cenário. No nível do WCF, clientes e serviços simplesmente estiver trocando mensagens e tipos .NET não cruzar a transmissão. Os problemas de versionamento que estou falando sobre estão puramente no nível do .NET e poderá ser oculto de clientes usando um roteador de mensagens.

A vantagem com os serviços de fluxo de trabalho é que é possível publicar dois serviços lado a lado, com endereços de ponto de extremidade diferente, mas publicar uma fachada que disponibilizar os dois serviços no mesmo endereço. Usando essa abordagem, quando você implantar a versão 1.0 do seu serviço, você pode implantá-lo em um roteador passagem do WCF que simplesmente toma nas mensagens e passa em para o serviço. Quando estiver pronto para implantar a versão 1.1 do serviço, você poderá atualizar as informações de roteamento para que mensagens serão enviadas para o o serviço de versão 1.0 ou o serviço de versão 1.1, dependendo da mensagem ou o contexto no qual ele foi recebido. a Figura 5 mostra esse conceito de roteador com as duas versões de um serviço de um roteador e um cliente interagir com eles.

fig06.gif

A Figura 5 usando um roteador para serviços de controle de versão

A chave para garantir que uma solução com um roteador funcionarão é para implantá-la com a versão 1.0 do seu serviço. Em seguida, quando você move para a próxima versão, o roteador não é algo novo você estiver adicionando a implantação e testando o processo. O SDK do .NET Framework 3.5 fornece um roteador de exemplo e você também pode seguir um artigo prático sobre usando regras do WF para tomar decisões de roteamento.

A atualização dinâmica

Após todos os a discussão sobre como gerenciar melhor alterando a definição de fluxos de trabalho e lidar com as versões novas e antigas em execução lado a, geralmente há uma pergunta sobre como lidar com os fluxos de trabalho antigos. Feito uma alteração seu fluxo de trabalho por um motivo. Talvez ele era corrigir um defeito na lógica, ou talvez você tinha conformidade com regulamentações que precisava ser seguido. Seja qual for o motivo, geralmente é a ocorrência que não é um resultado tenable permitindo que todos os fluxos de trabalho existentes para conclua conforme elas foram definidas. Nesses casos, talvez seja mais adequado tirar proveito de um recurso poderoso no WF: atualização dinâmica.

Em resumo, atualização dinâmica permite que você fazer alterações em uma instância de fluxo de trabalho em execução. Essas alterações podem consistir em adicionando ou removendo as atividades da estrutura de fluxo de trabalho. Que é tudo o que você pode fazer, mas ele pode alterar completamente a definição de seu fluxo de trabalho. Por exemplo, em uma máquina de estado, você ter a capacidade de adicionar todo estados, juntamente com os eventos que ouvir e as transições definidas. Você pode alterar as transições de estado, removendo e re-adding SetState atividades. Em um fluxo de trabalho seqüencial, você pode adicionar ou remover etapas como as alterações do processo de negócios.

Para fazer alterações em um fluxo de trabalho, você primeiro precisará criar um objeto de WorkflowChanges com base no fluxo de trabalho. Nesse ponto, o objeto de WorkflowChanges fornece um clone da estrutura de fluxo de trabalho que você pode manipular através da sua propriedade TransientWorkflow. Você alterar o clone adicionando e removendo atividades. Quando as alterações forem concluídas, você chamar o método ApplyChanges na classe WorkflowInstance, passando o WorkflowChanges para representar as alterações que você deseja aplicar à instância real.

Aqui está o fluxo de trabalho exemplo mostrado anteriormente, que continha duas atividades de WriteLine separadas por uma atividade de atraso, com uma atividade WriteLine adicional adicionada ao final da seqüência. Esse código é executado quando o fluxo de trabalho fica ocioso ou quando o atraso começa a execução.

WorkflowChanges changes = 
  new WorkflowChanges(instance.GetWorkflowDefinition());
CompositeActivity root = changes.TransientWorkflow;
WriteLineActivity wl = new WriteLineActivity {
  Name = "newWriteLine",
  OutputText = "dynamically added"
};
root.Activities.Add(wl);
instance.ApplyWorkflowChanges(changes);

Esse exemplo simples mostra que é possível, mas cenários mais avançados podem ser criados para aplicar muitas alterações diferentes a um fluxo de trabalho em execução. Isso permite que você tirar um fluxo de trabalho versão 1.0, que já foi iniciado e fazer alterações para a instância para que ele funcione mais como um fluxo de trabalho version1.1.

Envie suas dúvidas e comentários para mmnet30@Microsoft.com.

Matt Milner é um membro da equipe técnica na Pluralsight, onde ele se concentra em tecnologias de sistemas conectados (WCF, Windows WF, BizTalk, "Dublin" e a plataforma de serviços Azure). Matt também é um consultor independente especializado em design de aplicativo do Microsoft .NET e desenvolvimento. Matt regularmente compartilha seu amor da tecnologia por palestras em conferências de locais, regionais e internacionais, como técnico Ed. Microsoft reconheceu Matt como MVP para suas contribuições da comunidade em torno de tecnologia de sistemas conectados. Entre em contato com Matt via seu blog em pluralsight.com/community/blogs/Matt.