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 http://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 http://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>()); }
// 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(); } ...
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.