Semana de acontecimentos

 Esta semana passou rápido. Tivemos o TechEd em São Paulo e foi muito bom ver o evento tendo grande aceitação, principalmente quanto ao conteúdo técnico. O Danilo e o Rogério fizeram um trabalho excelente de coordenação e todo o time de track owners e palestrantes não fizeram por menos. Para mim, a única baixa foi ainda a pequena participação do público feminino de TI. Não sei como podemos influenciar nisto, mas a verdade é que perdemos muito com a diminuição na diversidade de pensamento e cultura.

Outra: Markus Christen chegou para nos ajudar falando de arquitetura de infra-estrutura. Seu blog já está no ar em https://blogs.technet.com/markuschristen . Como o Gebara resolveu abraçar outros temas (ver), o Markus chega para trazer a sua experiência de anos como consultor. Ele vai errar um pouco no português, pois sua origem suíça ainda o faz confundir o gênero dos nossos substantivos, mas seu conhecimento e experiência vão nos ajudar muito a esclarecer os caminhos da arquitetura para a infra-estrutura. Muito bem vindo, Markus!

Steve Ballmer esteve aqui e deu uma excelente apresentação no TechEd. Falou da necessidade de uma TI ágil e reafirmou que S+S, virtualização, interoperabilidade, segurança e experiência do usuário se tornaram a base para os novos desenvolvimentos na nossa área (leia em https://www.microsoft.com/presspass/exec/steve/2008/10-14TechEdBrazil.mspx). Estive em outras conversas com ele também, e é impressionante a sua energia, amplitude e clareza.

Por fim, muita gente no TechEd pediu o código que apresentei sobre Entity Framework e desenvolvimento em N camadas. Bem, a base está no artigo do John Papa. Minhas mudanças no código do artigo foram as seguintes:

1) No Façade dos serviços incorporar transação e liberação dos contextos do Entity Framework. Exemplo:

public List<Customer> FindCustomerList(string companyName)
{
    List<Customer> lCustomer = null;
    using (TransactionScope transaction = new TransactionScope()) {
        try {
            lCustomer = new CustomerManager().FindCustomerList(companyName);
            EFExtension.SaveAllContexts();
            transaction.Complete();
        }
        catch (Exception ex) {
            // dispose nos ObjectContext
            throw ex;
        }
        finally {
            // Dispose all Contexts
            EFExtension.DisposeAllContexts();
        }
    }
    return lCustomer;
}

2) Incluir na biblioteca de apoio um conjunto de funções que administram contextos visando ter um singleton para cada contexto numa thread:

// Insere contexto no dicionário da thread para criar um singleton
public static void SetContext(string ctxName, ObjectContext ctx) {
    Dictionary<string, ObjectContext> ctxDict =
            Thread.GetData(Thread.GetNamedDataSlot("ContextDictionary")) as Dictionary<string, ObjectContext>;
    if (ctxDict == null) {
        ctxDict = new Dictionary<string, ObjectContext>();
        Thread.SetData(Thread.GetNamedDataSlot("ContextDictionary"), ctxDict);
    }

    try {
        ctxDict.Add(ctxName, ctx);
    } catch ( Exception ex ) {
        ctx.Dispose(); // dispose it if a copy already exists
    }
}

// Retorna contexto do dicionário da thread
public static ObjectContext GetContext(string ctxName) {
    Dictionary<string, ObjectContext> ctxDict =
            Thread.GetData(Thread.GetNamedDataSlot("ContextDictionary")) as Dictionary<string, ObjectContext>;
    if (ctxDict == null ) {
        ctxDict = new Dictionary<string, ObjectContext>();
        Thread.SetData(Thread.GetNamedDataSlot("ContextDictionary"), ctxDict);
    }

    ObjectContext ctx;
    try {
        ctxDict.TryGetValue(ctxName, out ctx);
    } catch ( Exception ex ) {
        return null;
    }
    return ctx;
}

// Salva todos os contextos das threads
public static void SaveAllContexts() {
    Dictionary<string, ObjectContext> ctxDict =
            Thread.GetData(Thread.GetNamedDataSlot("ContextDictionary")) as Dictionary<string, ObjectContext>;
    if (ctxDict == null)
        return;
    foreach (ObjectContext ctx in ctxDict.Values) {
        ctx.SaveChanges();
    }
}

// Libera todos os contextos das threads
public static void DisposeAllContexts() {
    Dictionary<string, ObjectContext> ctxDict =
            Thread.GetData(Thread.GetNamedDataSlot("ContextDictionary")) as Dictionary<string, ObjectContext>;
    if (ctxDict == null)
        return;
    foreach (ObjectContext ctx in ctxDict.Values) {
        ctx.Dispose();
    }
    Thread.SetData(Thread.GetNamedDataSlot("ContextDictionary"), new Dictionary<string, ObjectContext>());
}

3) Nas regras de negócio, garantir o uso do singleton através dos métodos acima. Exemplo:

public class CustomerManager
{
    private const string ctxName = "ctxCustomerManager";

    public CustomerManager()
    {
        if ( EFExtension.GetContext(ctxName) == null )
            EFExtension.SetContext(ctxName, new NorthwindEFEntities() );
    }

    public List<Customer> FindCustomerList(string companyName)
    {
        NorthwindEFEntities context = EFExtension.GetContext(ctxName) as NorthwindEFEntities;

        var q = from c in context.CustomerSet
                where c.CompanyName.StartsWith(companyName)
                select c;
        return q.ToList();
    }

...

Abraço a todos.