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/]
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/]