1. Requirements

Windows 7 Enterprise Edition (32-Bit OS)
.Net Framework 3.5 SP1
Compiler Version 3.5.30729.4918
Visual Studio 2008 Version 9.0.30729.1

2. Purpose

Sometimes it can be cumbersome to debug multi-thread applications, trying to use very simple features such as “Step Into” (F11), “Step Over” (F10) and “Step Out” (Shift + F11) can easily mess your mind up. Most developers that use Visual Studio as their primary IDE, are not aware of its multi-thread debugging features. So, I decided to post several tips and tricks that can be very handy while debugging this kind of applications.

3. Demo

Before we begin, a sample multi-thread application will be necessary to apply the techniques throughout this post. It is a simple Console Application that creates two threads, Thread A and Thread B, which will count from 0 to 100, printing “Thread A - 0”, “Thread A - 1”, and so forth. Take a look at the following illustrations:

using System;
using System.Threading;

namespace MultiThreadDebugging
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread threadA = new Thread(Execute);
            threadA.Name = "Thread A";
            threadA.Start(threadA.Name);

            Thread threadB = new Thread(Execute);
            threadB.Name = "Thread B2";
            threadB.Start(threadB.Name);

            Console.ReadLine();
        }

        static void Execute(object threadName)
        {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine(threadName + " - " + i);
            }
        }
    }
}

The following illustration shows the expected output from the application:






Set up a break point on line 25 and hit F5 to run the console application.






After the execution stop on the breakpoint, click on menu Debug > Windows > Threads or press CRTL + D + T to exhibit the Threads window. Below, you will find an illustration of this window:






As you can see in the above picture, the Main Thread is differentiated with a green square and the current thread is marked with a yellow arrow. Also, other threads appear on the list, which are loaded by Visual Studio and CLR. Thread B does not appear in the thread list because it is waiting to execute. However, there is an easy way to show where are all the threads hanging around. I order to do that, click on menu

Click on the Show Threads in Source button located on Debug toolbar:






Next, you will see that an icon composed of two waves appear on the left side of the row numbers indicating where the Main Thread execution is hanging. Also, the line is highlited, in this case, with a gray background color. That is why Thread B does not appear on the list, it did not started yet.






Now, that the basics are covered, let’s see how can tell the Debbugger to stop only when the executing thread is Thread B, which is very handy while executing very common features such as Step Into, Step Over and Step Out.

Stop running the console application and right click the breakpoint and choose Filter option:






Using the ThreadName instead of ThreadId is most of the time better, because the thread id is provided by the operational system kernel and changes among. Hit F5 run the console application again and notice that while debugging the breakpoint will stop only for Thread B.






Another feature that I would like to point here is breakingponts acting like trace. Let’s suppose that you would like to trace your application, a very powerful and common way is to instrument your with the trace mechanisms of the .NET platform. However, sometimes, you only need a simple information, so it’s possible to achieve the desired result without having to change your code.

Rick click the breakpoint, choose When Hit option and set the “Print a message” with $TNAME = {i}, where $TNAME will print the thread name (Thread A or Thread B) and {i} will print the i variable value. Take a look at following illustration:









Last tip, if want to save the Output Windows content on a text file just press CTRL + S.

4. Resources

There is more content regarding breakpoint and tracepoints on MSDN http://msdn.microsoft.com/en-us/library/ktf38f66.aspx.

1 Requirements

  • Windows 7 Enterprise Edition (32-Bit OS)
  • .Net Framework 3.5 SP1
  • C# Compiler Version 3.5.30729.4918
  • Visual Studio 2008 Version 9.0.30729.1


2 Problem

Recently, I have been seeing people developing N-layer applications coding all classes from all layers with the public access modifier. The motivation for that is because functionalities that reside on an assembly needs be accessed from the previous assembly in the call stack, for example, a class on the business logic layer needs to access another in the data access layer. Depending on the size of your team, the application complexity, project milestones and deadlines, etc., this design decision can lead to undesired results, such as:

  • Inexperienced developers can accidentally make a mess with the calls, for example, coding the presentation layer to access directly the data access layer.
  • Developers pressured by a tight milestone/deadline can be tempted to do exact the same of previous item.
  • Suddenly, you realize that the robust exception handling mechanism implemented on the façades in the business logic layer is not working for several features. Moreover, the integration with third-party applications won’t be that easy because several features do not have a business logic entry.

Before getting the hands dirty, I do think is important to state several concepts regarding layers.
Dividing the application in layers allows you to create logical divisions to group code that have similar purpose. On one hand, this can help developers to understand how the application is organized, where they should put their code, etc. However, on the other hand, keep in mind that a logical architecture is not required to make your application work. Also, try not to confuse the term layer with tier, which is more related to physical division (process and/or machine). Below, there is an illustration of a simple N-layer application:




3 Workaround

There are several workarounds that can be applied to solve the encapsulation problems presented on the previous section.

One possibility it to create the layers on only one logical unit (assembly) coding all BLL, DAL and DTO classes and/or structs, which should be exposed with the internal access modifier, for example:



I have been hearing several people discussing about this approach not being efficient, because the project would always be checked out to another developer, becoming a contention point during coding phase. Presonaly, I think this can be true on several scenarios, such as:

  • Project has more than least 20 developers and coding phase is soaring with people are adding objects to the Visual Studio project like crazy.
  • Project has 5 developers and a couple of them constantly forget to check-in the project after adding an object, preventing others to do their jobs. This can be a consequence of: lack of a simple check-in/check-out process; or inexperience of a team member, who does not know the trouble his making to others or he is to focus to get the job done and just forget to check-in the project.

Tip: after adding an object to a project, for example, a class, make the check-in without starting coding, this way you avoid breaking the code. After that, check the file out and start getting your hands dirty.

Another approach would be having the layers separated in different assemblies, coding all classes with the internal access modifier and configuring which assemblies would be able to make calls to these internal functionalities.
To better exemplify this approach, the following steps will describe how it is possible to exposed functionalities from the Data Access Layer (DAL) only to the Business Logic Layer (BLL), supposing that DAL classes have the internal access modifier specified:



  • Sign both Application.BLL.dll and Application.DAL.dll with a strong name (through VS IDE or sn.exe);
  • Get the public key from the Application.BLL.dll assembly with sn –Tp Application.BLL.dll;
  • Configure Application.DAL.dll to expose internal classes to Application.BLL.dll. This can be achieved through adding the [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(”Application.BLL, PublicKey=002400000480000094000000060200000024000052
    53413100040000010001003396ebc2b7199d23b73c40982b891fb3eb
    ecaa93deceaf189f12b38b252d7e144991ad1736ab900945e5b3d0e0
    15e833631cadb239c8325baa3f7662c7b1208456173cc8b233485bee
    32e4c8b5292b0d26703585737de948af9d95318751df369896a76cfb
    561ec1cad90a324a131bfeb7ecb54c9f35f262c2851234749ec8c9″)]
    in the AssemblyInfo.cs in Application.DAL project. Caution: your public key will be different from the one in this example;
  • Compile your solution and go for it.

Ralph Marston
http://greatday.com/ralph/personal.html

Objetivo

O propósito deste artigo é apresentar um mecanismo que viabilize a leitura de chaves de configuração “atualizadas” após a alteração do arquivo de configurações da aplicação (.exe.config).
Este mecanismo pode ser muito útil caso você tenha um sistema non-stop, como por exemplo um Windows Service, onde você não precisará reiniciar o serviço para que as novas configurações sejam reconhecidas pelo serviço.
O exemplo descrito ao longo deste post não se aplica ao ASP.NET, visto que por padrão, o IIS 6.0/7.0/7.5 cria um novo processo quando o web.config é alterado.

Solução

A solução consiste em criar um objeto FileSystemWatcher que receba uma notificação quando o arquivo de configurações for alterado. Segue abaixo o código-fonte de uma aplicação console:

class Program
{
    static void Main(string[] args)
    {
        /* Objeto FileSystemWatcher que sera notificado quando
         * o arquivo de configurações for alterado.
         */
        string filePath=System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(”ConfigurationManagerCache.exe”, “”);
        System.IO.FileSystemWatcher watcher = new System.IO.FileSystemWatcher(filePath, “ConfigurationManagerCache.exe.config”);
        watcher.NotifyFilter = System.IO.NotifyFilters.LastAccess;
        watcher.Changed += new System.IO.FileSystemEventHandler(ConfigurationFileChange);
        watcher.EnableRaisingEvents = true;

        while (true)
        {
            System.Console.WriteLine(”Name={0}”, System.Configuration.ConfigurationManager.AppSettings[”Name”]);
            System.Console.WriteLine(”LocalDB={0}”, System.Configuration.ConfigurationManager.ConnectionStrings[”LocalDB”].ConnectionString);
            System.Console.WriteLine();
            System.Threading.Thread.Sleep(1000);
        }
    }

    static void ConfigurationFileChange(object sender, System.IO.FileSystemEventArgs e)
    {
        /* Este sleep e necessario para que o usuario tenha ate 5 segundos
         * para salvar e fechar o arquivo antes da tentativa de leitura
         * pelo programa o que causara uma excecao caso o arquivo ainda
         * esteja aberto.
         */
        System.Threading.Thread.Sleep(5000);

        //Faz SOMENTE o refresh das secoes connectionStrings e appSettings
        System.Configuration.ConfigurationManager.RefreshSection(”connectionStrings”);
        System.Configuration.ConfigurationManager.RefreshSection(”appSettings”);

        System.Console.WriteLine();
        System.Console.WriteLine(”– Sections ‘connectionStrings’ and ‘appSettings’ refreshed. –”);
        System.Console.WriteLine();
    }
}

1 Alterações

A versão 1.0.0.1 da ferramena CRUDMatrixGenerator possou por um processo de Code Refactory e possui as seguintes novas funcionalidades:

  • Detecção de queries executadas contra outros bancos de dados que não o atual.
  • Além de analisar Stored Procedure, a ferramente também analisará Views.
  • Inclusão da coluna Type, responsável por identificar o tipo de objeto analisado, Stored Procedure ou View.

Veja abaixo as ilustrações, respectivamente, do log gerado pela ferramenta e da visualização dos dados no Excel (após procedimento importação):


Log


Log

2 Instalador

O instalador está disponível no endereço CRUDMatrixGeneratorSetup_1.0.0.1.zip.

3 Código-fonte

Você pode baixar o código-fonte desta ferramenta no endereço CRUDMatrixGenerator_SourceCode_1.0.0.1.zip.

1 Objetivo

Decidi desenvolver a ferramenta CRUDMatrixGenerator quando vi um amigo fazendo um levantamento para mapear quais stored procedures de um determinado banco de dados referenciavam quais tabelas. O processo era bem manual e poderia ser bastante trabalhoso dependendo do número de objetos no banco de dados.

2 Demonstração

A utilização da ferramente é bem simples, basta executar o CRUDMatrixGenerator informando a string de conexão do banco de dados e o diretório onde o relatório deverá ser gravado, respectivamente, definidos pelos parâmetros -c e -o. Veja a ilustração abaixo:

De acordo com a execução ilustrada acima, um arquivo texto, cujo nome segue o formato [GUID].txt, é gravado no diretório C:\Temp. Veja a ilustração abaixo:

Em seguida, basta importar o arquivo para o Excel informando o como separador de colunas. Veja o resultado na ilustração abaxo:

3 Instalador

O instalador está disponível no endereço CRUDMatrixGeneratorSetup.zip e o script do banco de dados utilizado no exemplo está disponível em DatabaseScript.zip.

4 Código-fonte

Você pode baixar o código-fonte desta ferramenta no endereço
CRUDMatrixGeneratorSourceCode.zip
.

1 Requerimentos de sistema

O bug relacionado neste post foi detectado utilizando-se os softwares abaixo:
• Windows Vista Enterprise Edition SP1.
• Visual Studio Team System 2008 (2008 9.0.21022.8 RTM).
• .NET Framework 3.5.
• Compilador C# 3.5.21022.8

2 Demonstração

Veja abaixo um exemplo de utilização do atributo Obsolete:

public class User
{
    private System.Collections.Generic.Dictionary<int, string> users;

    [System.Obsolete(”Esta propriedade está obsoleta.”, true)]
    public System.Collections.Generic.Dictionary<int, string> Users
    {
        get
        {
            return this.users;
        }
    }

    public User()
    {
        this.users=new System.Collections.Generic.Dictionary<int,string>();
        this.users.Add(0, “Evandro de Paula”);
    }
}

Note que a propriedade Users foi marcada como obsoleta, onde os parâmetros abaixo foram definidos como:
message = “Esta propriedade está obsoleta” (mensagem de erro ou alerta que é exibida durante a compilaçào quando a propriedade for utilizada).
error = true (indica que a utilização da propriedade obsoleta deve implicar em um erro de compilação).

Conforme o esperado, quando compilamos o código abaixo, obtemos um erro na linha em destaque:

class Program
{
    static void Main(string[] args)
    {
        User user = new User();
        System.Collections.Generic.Dictionary users=user.Users;
        System.Console.ReadLine();
    }
}

Entretanto, se implemetarmos a alteração em destaque abaixo, onde não utilizamos diretamente a propriedade obsoleta, mas sim um elemento da coleção, notaremos que nenhum erro é informado durante o processo de compilação:

class Program
{
    static void Main(string[] args)
    {
        User user = new User();
        System.Console.WriteLine("Name: " + user.Users[0]);
        System.Console.ReadLine();
    }
}

3 Conclusão

Mantenha atenção redrobrada no emprego do atributo Obsolete na tentativa de minimizar a possibilidade de comportamentos inesperados pelo sistema.
A boa notícia é que a correção deste bug já foi providenciada pelo time da Microsoft e deve estar disponível na próxima versão da plataforma .NET.

4 Código-fonte

Você pode baixar o código-fonte desta ferramenta no endereço http://www.orchestratechnology.com.br/blog/csharp/ObsoleteCheck.zip.

1 Objetivo

Desenvolvi a ferramenta VS2008SolutionDowngrader com o propósito de converter soluções criadas no Visual Studio 2008 para o Visual Studio 2005. É importante mencionar que a ferramenta foi desenvolvida com o intuito de migrar apenas soluções com projetos C#.

Um backup dos arquivos .sln e .csproj. é realizado antes que o processo de conversão seja executado, isso garante que você não perca a versão original dos seus arquivos em caso de uma falha na ferramenta.

2 Demonstração

Abaixo você pode acompanhar uma demonstração de conversão da solução VS2008CSharpSolution, criada noVisual Studio Team System 2008, contendo 5 projetos do tipo: Class Library, Console Application, ASP.NET web site, Windows Application e Windows Service:

Solução criada no Visual Studio Team System 2008:

Solução criada no Visual Studio Team System 2008

Executando a ferramenta de conversão:

Executando a ferramenta

Resultado da execução:

Output 1
Output 2

Abrindo a solução no Visual Studio Team System 2005:

Output 2

3 Instalador

O instalador está disponível no endereço http://www.orchestratechnology.com.br/blog/ferramentas/VS2008SolutionDowngraderSetup.zip .

4 Código-fonte

Você pode baixar o código-fonte desta ferramenta no endereço http://www.orchestratechnology.com.br/blog/ferramentas/VS2008SolutionDowngrader.zip .

1 Introdução

No SQL Server é possível criarmos dois tipos de arquivos de dados, o primário e o secundário, sendo o primeiro tipo mandatório. De acordo com a documentação é possível criarmos 32.766 arquivos secundários.

Uma boa prática é deixar o arquivo de dados primário para o catálogo do banco de dados e armazenar os dados e objetos, tais como, tabelas, procedimentos armazenados, visões, funções, etc., em arquivos secundários. Desta forma, podemos reduzir a contenção no acesso aos dados.

Grupos de arquivo são estruturas lógicas de agrupamento de arquivos que podem ser gerenciados como uma unidade. Assumindo a recomendação apresentada no parágrafo anterior como premissa, por conseqüência é uma boa prática definir o arquivo de grupo do arquivo secundário como padrão, pois nele serão armazenados os dados e os objetos do banco de dados.

Veja o exemplo abaixo:

--CRIA O BANCO DE DADOS
PRINT 'Criando o banco de dados OrchestraTechnologyBlog...'
CREATE DATABASE OrchestraTechnologyBlog
ON
	PRIMARY
	(
		NAME='OrchestraTechnologyBlog',
		FILENAME='C:Program FilesMicrosoft SQL ServerMSSQL.1MSSQLDataOrchestraTechnologyBlogCatalog.mdf',
		SIZE=100MB,
		MAXSIZE=300MB,
		FILEGROWTH=50MB
	),
	FILEGROUP OrchestraTechnologyBlogFG1
	(
		NAME='OrchestraTechnologyBlogData1',
		FILENAME='C:Program FilesMicrosoft SQL ServerMSSQL.1MSSQLDataOrchestraTechnologyBlogData1.ndf',
		SIZE=500MB,
		MAXSIZE=UNLIMITED,
		FILEGROWTH=500MB
	)
	LOG ON
	(
		NAME='OrchestraTechnologyBlogLog',
		FILENAME='C:Program FilesMicrosoft SQL ServerMSSQL.1MSSQLDataOrchestraTechnologyBlogLog.ldf',
		SIZE=250MB,
		MAXSIZE=1GB,
		FILEGROWTH=15%
	)
GO
PRINT '>> Procesimento finalizado'

--DEFINE O FILEGROUP DE DADOS E OBJETOS DE BANCO COMO DEFAULT
PRINT 'Definindo o FILEGROUP OrchestraTechnologyBlogFG1 como padrao...'
ALTER DATABASE
	OrchestraTechnologyBlog
MODIFY FILEGROUP
	OrchestraTechnologyBlogFG1 DEFAULT
GO
PRINT '>> Procedimento finalizado'

2 Pontos de atenção

• Caso a propriedade o SIZE não seja especificada com um valor para arquivos do tipo secundários ou para o arquivo de log, os mesmos serão criados com o tamanho de 1MB.

• O valor máximo que pode ser definido na propriedade MAXSIZE para arquivos do tipo primário é de 16TB e para arquivos de log de 2TB. É possível definir esta propriedade como UNLIMITED para que o tamanho do arquivo cresça até que o espaço em disco acabe.

• O número 0 no FILEGROWTH serve para indicar que o arquivo não cresça automaticamente quando for necessário.

3 Boas práticas

• A recomendação da Microsoft para as extensões dos arquivos primário, secundário e de log é: .mdf para o arquivo primário, .ndf para arquivos secundários e .ldf para arquivo de log.

• Baseado no plano de crescimento do banco de dados crie os arquivos com o maior tamanho possível para evitar a fragmentação, que por sua vez, exerce pressão no processo de I/O.

• Caso opte por utilizar o UNLIMITED no MAXSIZE, é vital ter um mecanismo para monitorar o espaço em disco que envie alertas quando o mesmo estiver acabando, como por exemplo, o SCOM 2007.

O exemplo abaixo é um código que compila sem problema algum, porém, durante a sua execução lança a exceção System.ArrayTypeMismatchException na linha em que o inteiro 1 é atribuído a posição 0 do array b.

using System;

namespace CompilerTypeCheck
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string[] a = new string[3];
                object[] b = a;
                b[0] = 1;
            }
            catch (System.ArrayTypeMismatchException atmex)
            {
                System.Console.WriteLine("An error occured while executing this program. Error details: " + atmex.StackTrace);
            }

            System.Console.WriteLine("Press  to exit…”);
            System.Console.ReadLine();
        }
    }
}

- Próxima Página »