Outubro 2006 - Posts

Como eu entendo o padrão MVP (Model/View/Presenter), a responsabilidade das decisões de apresentação pertencem ao Presenter.

No mundo do CAB, isso deveria incluír as informações de apresentação das smart parts.

Mas a interface IWorkspace apenas permite que se forneçam as informações de apresentação (uma classe que implementa a interface ISmartPartInfo e que cada work space usa uma implementação específica), ou a smart part (a View no padrão MVP) deverá saber que informação fornecer ao work space em que vive implementando a interface ISmartPartInfoProvider.

Para permitir ao Presenter fornecer informações de apresentação ao work space em que vive, é necessário introduzir algumas modificações à interface IWorkspace e implementatações. Novos métodos Show e ApplySmartPartInfo devem ser adicionados:

/// <summary>
/// Applies the smartPartInfo to the smartPart.

/// </summary>
void ApplySmartPartInfo(object smartPart, ISmartPartInfoProvider smartPartInfoProvider);

/// <summary>
/// Shows SmartPart using the given SmartPartInfo
/// </summary>
/// <param name="smartPart">Smart part to show.</param>
/// <param name="smartPartInfoProvider"></param>
void Show(object smartPart, ISmartPartInfoProvider smartPartInfoProvider);

E devem ser implementados em todas as classes que implementem a interface IWorkspace:

/// <summary>
/// Applies the smartPartInfo to the smartPart.
/// </summary>
public void ApplySmartPartInfo(object smartPart, ISmartPartInfoProvider smartPartInfoProvider)
{
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(smartPart, "smartPart");
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(smartPartInfoProvider, "smartPartInfoProvider");
    ThrowIfUnsupportedSP(smartPart);
    ThrowIfSmartPartNotShownPreviously((TSmartPart)smartPart);

    TSmartPart typedSmartPart = (TSmartPart)smartPart;
    TSmartPartInfo typedSmartPartInfo = GetSupportedSPI(smartPartInfoProvider.GetSmartPartInfo(typeof(TSmartPartInfo)));
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(typedSmartPartInfo, "typedSmartPartInfo");

    OnApplySmartPartInfo(typedSmartPart, typedSmartPartInfo);
}

/// <summary>
/// Shows SmartPart using the given SmartPartInfo
/// </summary>
/// <param name="smartPart">Smart part to show.</param>
/// <param name="smartPartInfoProvider"></param>
public void Show(object smartPart, ISmartPartInfoProvider smartPartInfoProvider)
{
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(smartPart, "smartPart");
    Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(smartPartInfoProvider, "smartPartInfoProvider");
    ThrowIfUnsupportedSP(smartPart);
       
    TSmartPart typedSmartPart = (TSmartPart)smartPart;

    if (smartParts.Contains(typedSmartPart))
    {
        ApplySmartPartInfo(typedSmartPart, smartPartInfoProvider);
        Activate(typedSmartPart);
    }
    else
    {
        TSmartPartInfo typedSmartPartInfo = GetSupportedSPI(smartPartInfoProvider.GetSmartPartInfo(typeof(TSmartPartInfo)));
        Microsoft.Practices.CompositeUI.Utility.Guard.ArgumentNotNull(typedSmartPartInfo, "typedSmartPartInfo");
        smartParts.Add(typedSmartPart);
        OnShow(typedSmartPart, typedSmartPartInfo);
    }
}

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/paulomorgado/]

Para quem não sabe, a ManagedObjectCollection regista os seus elementos usando o tipo do objecto como parte da chave.

Ora, recentemente, necessitei de adicionar e obter um componente da colecção de itens do WorkItem independentemente do seu tipo. Eu apenas queria obter o componente (usando uma ComponentDependency) com um um determinado nome. Escusado será dizer que não consegui, porque estava a tentar obter um object, e não era o tipo do componente que tinha sido adicionado ao WorkItem.

Comecei a pensar que a capacidade da ServiceCollection de adicionar itens registando-os com um tipo diferente do do item seria muito útil à ManagedObjectCollection. Decidi, então fazer algumas alterações.

Para se puderem construír elementos e registá-los com outro tipo, foi necessário alterar o método de construção e registo dos objectos: 

private TItem Build(Type typeToBuild, Type typeToRegisterAs, string idToBuild, object item)
{
	if (idToBuild != null && Contains(idToBuild, SearchMode.Local, true))
		throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
			Properties.Resources.DuplicateID, idToBuild));

	if (item != null && Object.ReferenceEquals(item,
		locator.Get(new DependencyResolutionLocatorKey(typeof(WorkItem), null))))
		throw new ArgumentException(Properties.Resources.CannotAddWorkItemToItself, "item");

	if (item == null)
		item = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, idToBuild, null);
	else if (!container.Contains(item))
		item = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, idToBuild, item);
	else
		BuildRepeatedItem(typeToBuild, typeToRegisterAs, idToBuild, item);

	return (TItem)item;
}

private object BuildFirstTimeItem(Type typeToBuild, Type typeToRegisterAs, string idToBuild, object item)
{
    item = builder.BuildUp(locator, typeToBuild, NormalizeID(idToBuild), item);

    if (typeToRegisterAs != typeToBuild)
    {
        locator.Add(new DependencyResolutionLocatorKey(typeToRegisterAs, idToBuild), item);
        locator.Remove(new DependencyResolutionLocatorKey(typeToBuild, idToBuild));
    }

    return item;
}

private void BuildRepeatedItem(Type typeToBuild, Type typeToRegisterAs, string idToBuild, object item)
{
    locator.Add(new DependencyResolutionLocatorKey(typeToRegisterAs, NormalizeID(idToBuild)), item);
}

E, para garantir compatibilidade com os métodos já existentes:

private TItem Build(Type typeToBuild, string idToBuild, object item)
{
    return Build(typeToBuild, typeToBuild, idToBuild, item);
}

Agora só faltava adicionar os novos métodos para adicionar elementos:

public void Add(Type typeToRegisterAs, TItem item)
{
    Add(typeToRegisterAs, item, null);
}

public void Add(Type typeToRegisterAs, TItem item, string id)
{
    Guard.ArgumentNotNull(item, "item");
    Guard.TypeIsAssignableFromType(typeof(TItem), typeToRegisterAs, "typeToBuild");

    Build(typeToRegisterAs, item.GetType(), id, item);
}

public void Add(TTypeToRegisterAs item)
    where TTypeToRegisterAs : TItem
{
    Add(item, null);
}

public void Add(TTypeToRegisterAs item, string id)
    where TTypeToRegisterAs : TItem
{
    Guard.ArgumentNotNull(item, "item");

    Build(item.GetType(), typeof(TTypeToRegisterAs), id, item);
}

public TItem AddNew(Type typeToBuild, Type typeToRegisterAs)
{
    Guard.TypeIsAssignableFromType(typeToBuild, typeToRegisterAs, "typeToBuild");

    return AddNew(typeToBuild, typeToRegisterAs, null);
}

public TItem AddNew(Type typeToBuild, Type typeToRegisterAs, string id)
{
    Guard.TypeIsAssignableFromType(typeToBuild, typeof(TItem), "typeToBuild");
    Guard.TypeIsAssignableFromType(typeToBuild, typeToRegisterAs, "typeToRegisterAs");

    return Build(typeToBuild, typeToRegisterAs, id, null);
}

public TTypeToBuild AddNew()
    where TTypeToRegisterAs : TItem
    where TTypeToBuild : TTypeToRegisterAs
{
    return (TTypeToBuild)Build(typeof(TTypeToBuild), typeof(TTypeToRegisterAs), null, null);
}

public TTypeToBuild AddNew(string id)
    where TTypeToRegisterAs : TItem
    where TTypeToBuild : TTypeToRegisterAs
{
    return (TTypeToBuild)Build(typeof(TTypeToBuild), typeof(TTypeToRegisterAs), id, null);
}
[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/paulomorgado/]

Nem todos sabem que o ASP.NET Development Server não é parte do Visual Studio, mas, na verdade, é parte da plataforma .NET 2.0.

O ASP.NET Development Server é um executável (WebDev.WebServer.EXE) que se encontra na pasta de instalação da plataforma (usualmente: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727).

Para descobrir como usá-lo basta correr o referido executável. No entanto, aqui ficam as instruções:

ASP.NET Development Server Usage:
WebDev.WebServer /port:<número do porto> /path:<caminho físico> [/vpath:<caminho virtual>]

número do porto:
[Opcional] Um número do porto ainda não utilizado entre 1 e 65535.
Por omissão é 80 (pode ser usado se o IIS ainda não estiver a ser usado com o memso porto.).

caminho físico:
Um directório válido onde se encontra a raíz da aplicação Web.

caminho virtual:
[Opcional] O caminho virtual ou a raíz da aplicação na forma '/<app name>'.
Por omissão é simplesmente '/'.

Examplo:
WebDev.WebServer /port:8080 /path:"c:\inetpub\wwwroot\MyApp" /vpath:"/MyApp"

Pode-se a aceder à aplicação Web usando um URL no formato:
http://localhost:8080/MyApp

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/paulomorgado/]

Windows Internet Explorer

Download: IE7 Final For Windows XP 32-Bit
http://download.microsoft.com/download/3/8/8/38889DC1-848C-4BF2-8335-86C573AD86D9/IE7-WindowsXP-x86-enu.exe

Download: IE7 Final For Windows Server 2003 32-Bit
http://download.microsoft.com/download/D/1/3/D1346F12-F3A0-4AC6-8F5C-2BEA2A184957/IE7-WindowsServer2003-x86-enu.exe

Download: IE7 Final for Windows XP/Server 2003 64-Bit
http://download.microsoft.com/download/1/1/4/114D5B07-4DBC-42F3-96FA-2097E207D0AF/IE7-WindowsServer2003-x64-enu.exe

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/paulomorgado/]
Posted por Paulo Morgado | with no comments

Ontem foi criado o projecto no CodePlex para o WebBrowserControl para a Plataforma .NET 2.0.

Já há muito tempo que pretendia iniciar este projecto, mas ainda não se tinha proporcionado. Veremos como vai correr.

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/paulomorgado/]

Finalmente publiquei o "meu" WebBrowserControl para a Plataforma .NET 1.1.

Era meu desejo fazer um belo artigo sobre os mas, comos e porquês, mas nunca houve tempo (nem jeito) para tal. E agora que a .NET 3.0 nos está a bater à porta, brevemente ninguém quererá nada para .NET 1.1. :)

Já fiz algum trabalho para o migrar para a plataforma 2.0. O meu desejo era enriquecer o controlo WebBrowser fornecido, mas eles tornaram quase impossível extendê-lo.

Uma vez mais, os meus agradecimentos a Igor Tandetnik, João Melo, Luís Abreu, Oleg Mihailik e muitos mais.

Quem já tentou adicionar um serviço implementado sobre Remoting à colecção de serviços de um WorkItem teve a desagradavel surpresa de não o poder fazer.

E porquê?

Quando usamos o sguinte código:

WorkItem.Services.Add(serviceInstance);

Isto vai dar origem a algo do género:

if (!typeof(IServiceContract).IsAssignableFrom(serviceInstance.GetType())
	throw new ArgumentException();

E como é que resolvemos isto?

Fácil: genéricos.

É só fazer umas pequenas alterações à classe ServiceCollection.

Modificamos a implementção do método Add(TService serviceInstance) para ficar com a seguinte implementação:

public void Add(TService serviceInstance) 
{ 
	Guard.ArgumentNotNull(serviceInstance, "serviceInstance"); 

	Build(serviceInstance); 
} 

Refactorizamos o método Build(Type typeToBuild, Type typeToRegisterAs, object serviceInstance) para teremos dois pontos de entrada distinctos para uma lógica comum:

private object Build(Type typeToBuild, Type typeToRegisterAs, object serviceInstance) 
{ 
	Guard.TypeIsAssignableFromType(typeToBuild, typeToRegisterAs, "typeToBuild"); 

	return BuildImplementation(typeToBuild, typeToRegisterAs, serviceInstance); 
} 

private TService Build(TService serviceInstance) 
{ 
	return (TService)BuildImplementation(serviceInstance.GetType(), typeof(TService), serviceInstance); 
} 

private object BuildImplementation(Type typeToBuild, Type typeToRegisterAs, object serviceInstance) 
{ 
	if (locator.Contains(new DependencyResolutionLocatorKey(typeToRegisterAs, null), SearchMode.Local)) 
		throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Properties.Resources.DuplicateService, typeToRegisterAs.FullName)); if (serviceInstance == null) serviceInstance = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, null); else if (!container.Contains(serviceInstance)) serviceInstance = BuildFirstTimeItem(typeToBuild, typeToRegisterAs, serviceInstance); else BuildRepeatedItem(typeToRegisterAs, serviceInstance); return serviceInstance; }

Simples, não é? E suspeito que há lugar para mais melhoramentos destes no CAB.

Ah! E porque é que nós haveríamos de querer Remoting? Para usar o NMock2 nos testes unitários, claro!

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/paulomorgado/]

Actualizado: 14 de Novembro de 2006

Modificado:

private TService Build<TService>(object serviceInstance)
{
	Guard.TypeIsCompatibleType<TService>(serviceInstance, "typeToBuild");

	return (TService)BuildImplementation(serviceInstance.GetType(), typeof(TService), serviceInstance);
}

Removido:

E adicionamos o método TypeIsCompatibleType<T>(object providedInstance, string argumentName) à classe Guard:

public static void TypeIsCompatibleType<T>(object providedInstance, string argumentName) 
{ 
	if (!(providedInstance is T)) 
		throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
			Properties.Resources.TypeNotCompatible, typeof(T), providedInstance.GetType()),
			argumentName); 
} 

Quem já desevolveu sobre o CAB sabe que documentação e informação são bens escassos, mas quando nos deparamos com aquilo com que eu me deparei, tudo se torna ainda mais difícil.

Quando tentei usar o visualizer na minha aplicação deparei-me com algo muito estranho.

(Para quem não sabe, o visualizer é um componente do CAB que coleciona informação sobre os WorkItems da aplicação e disponibiliza essa informação a visualizations que fazem a apresentação dessa informação.)

Descobrir que necessitava de um elemento visualizer no ficheiro de configuração não foi fácil. Mas o mais difícil foi descobrir que este elemento, contra tudo o que seria de prever, é apenas uma colecção de visualizations.

A secção de configuração tem, portanto, o seguinte aspecto:

<visualizer>
	<add type="visualization type"/>
	...
	<add type="visualization type"/>
</visualizer>

Não seria mais lógico ser algo deste tipo?

<visualizer type="visualizer type">
	<visualizations>
		<add type="visualization type"/>
		...
		<add type="visualization type"/>
	<visualizations>
</visualizer>

Até poderíamos configurar o tipo do nosso visualizer, ao contrário do que é possível agora.

E esta pérola saíu do PAG.

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/paulomorgado/]

A red-gate acabou de lançar a beta pública do SQL Refactor.

O SQL Refactor é um Add-In ao SQL Server Management Studio da Microsoft. É necessário, portanto, ter o SQL Server Management Studio instalado. As funcionalidades do SQL Refactor estão disponíveis a partir dos menus do Management Studio, que podem aceder tanto ao SQL Server 2000 como ao SQL Server 2005.

Nesta distribuição do SQL Refactor estão disponíveis as seguintes funcionalidades:

  • SQL Lay Out - Reformata o código T-SQL. Pode-se seleccionar esta funcionalidade a partir do menu de topo SQL Refactor. Há mais de 30 opções para controlar esta funcionalidade, a que se pode aceder a partir do menu SQL Refactor.
  • Smart Rename - Renomeia funções, vistas, stored procedures e tabelas e actualiza todas as referências para os objectos renomeados. Pode-se aceder a esta funcionalidade a partir do menu de contexto do Object Explorer do Management Studio.
  • Table Split - Separa uma tabela em duas, e automáticamente reescreve todas as stored procedures e funções, e colunas de tabelas e vistas. Pode-se aceder a esta funcionalidade a partir do menu de contexto do Object Explorer do Management Studio.
  • Uppercase keywords - Transforma em letra maiúscula o script ou selecção.
  • Summarize Script - Dá uma sinopse do código script. Seleccionando itens na sinopse podem-se ver as instrucções correspondentes seleccionadas no script.
  • Encapsulate as stored procedure - Transforma o código seleccionado numa stored procedure, e se pretendido, introduz no script uma referência para a stored procedure criada.
  • Expand wildcards - Expande instrucções SELECT * para incluir a lista completa de colunas.
  • Find unused variables and parameters - Mostra as variáveis e parâmetros do código que não são usados, ou a que é apenas atribuído valor.
  • Qualify Object Names - Modifica o código de modo a que todos os nomes sejam qualificados. Esta funcionalidade pode ser acedida a partir do menu de topo SQL Refactor.

Para instalar o SQL Refactor beta, descarregar e correr o instalador a partir de:
ftp://ftp.red-gate.com/sqlrefactorbeta/sqlrefactorsetup.exe

Para desinstalar o SQL Refactor beta:

- No Management Studio, seleccionar Tools -> Customize… .
A caixa de diálogo Opções é apresentada.
- Arrastar o menu "SQL Refactor" para fora da barra de menu do Management Studio e largá-la na caixa de diálogo Options.
- Saír do Management Studio.
- Correr o desinstalador.

Se já estiver a ser usada uma CTP do SQL Refactor, deve ser desinstalada primeiro.

Limitações
Esta é uma distribuição beta. Há discrepancias conhecidas entre a documentação e as funcionalidades. Provavelmente conterá erros. A red-gate ficaria muito agradecida se os erros encontrados fossem reportados nos foruns ou enviados directamente. Apesar desta beta já ser considerada feature complete, quaisquer sugestões para futuros refactorings serão bem-vindas. É aconselhada a leitura da documentação antes de usar o SQL Refactor.

Esta versão não deve ser instalada em ambientes de produção.

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/paulomorgado/]
Posted por Paulo Morgado | with no comments
Filed under: , , ,