.NET Transactional File Manager

10/2/2013 12:00:00 AM By Felipe Pessoto

Estamos acostumados a usar transações sempre que trabalhamos com banco de dados, mesmo pra quem usa um ORM como o Entity Framework, que cria as transações implicitamente. Isso tudo pra garantir que nossas alterações sejam atomicas, evitando pedidos de compra sem itens, um débito sem um crédito, um pagamento de salário sem marcá-lo como pago, etc.

Esse processo fica mais complicado quando temos arquivos em disco envolvidos, por exemplo, um processo onde se insere uma linha no banco e move um arquivo, principalmente dentro de um laço. Se ocorrer uma exception de disco cheio no 10º arquivo, como fazer pra voltar os 9 anteriores ao local original? Tratar manualmente não parece muito confiável e limpo.

Pra estes casos existe uma biblioteca chamada .NET Transactional File Manager, é bem simples e funciona praticamente sem overhead, implementando a interface IEnlistmentNotification, permitindo participar de uma transação do System.Transactions.Transaction.

A biblioteca não usa o Transactional NTFS, o que permite o uso no Windows XP e Windows Server 2003, por isso também não faz uso do DTC.

Então a biblioteca não é confiavel? Depende! Da maneira que ela funciona não vai garantir o Rollback em caso de falha de hardware ou se o processo for finalizado, ela funciona automatizando o que teriamos que tratar manualmente se a transação for cancelada, movendo de volta os arquivos, apagando o que foi copiado ou criado, etc. No meu caso é o suficiente. Se for necessário usar Transactional NTFS existe a biblioteca AlphaFS.

A utilização é muito simples, parecido com a classe File, só não é static, não sei o motivo.

new TxFileManager().Move(fonte, destino);

.NET Transactional File Manager no Codeplex

.NET Transactional File Manager no NuGet

A diferença que o C# 5.0 faz com o async/await

3/11/2013 11:51:00 PM By Felipe Pessoto

A melhor forma de ver a grande diferença que o async faz é usando exemplos. Saindo de um código não só maior, mas muito mais complicado, com callbacks, tratamento de contexto e thread de execução, tratamento de erros aninhados com os callbacks, pra um código simples, como se fosse um código síncrono comum.

Código Antigo:

void ShowStuff ()
{
	var client = new WebClient ();
	var content = JsonValue.Parse (client.DownloadString ("http://api.worldbank.org/countries?format=json&per_page=50"));
	int number_of_countries = content [0] ["total"];
	int done = 0, error = 0;

	InvokeOnMainThread (() => {
		CountriesLabel.Text = string.Format ("Countries: {0} done: 0 error: 0", number_of_countries);
	});

	foreach (JsonObject c in content [1]) {
		string country_url = string.Format ("http://api.worldbank.org/countries/{0}/indicators/NY.GDP.MKTP.CD&format=json", (string)c ["id"]);
		JsonValue json = null;

		try {
			json = JsonValue.Parse (client.DownloadString (country_url));
		} catch (Exception e){
			++error;
			InvokeOnMainThread (()=> status.Text = "Got exception "+ e);
			continue;
		}

		ThreadPool.QueueUserWorkItem (delegate {
			Map map = null;
			try {
				map = LoadCountryLogo (c ["name"]).Result;
			} catch (Exception e){
				++error;
				InvokeOnMainThread (()=> status.Text = "Got exception "+ e);
			}
			if (map != null){
				ThreadPool.QueueUserWorkItem (delegate {
					Position position = null;

					try {
						position = LookupCountryPosition (c ["longitude"], c ["latitude"]).Result;
						if (position != null)
							InvokeOnMainThread (() => {
								AddPin (map, position); 
								++done;
								status.Text = json ["name"];

							});
					} catch (Exception e){
						error++;
						InvokeOnMainThread (()=> status.Text = "Got exception "+ e);
					}

				});
			}
		});
		
		InvokeOnMainThread (() => CountriesLabel.Text = string.Format ("Countries: {0} done: {1} error: {2}", number_of_countries, done, error));
	}
	InvokeOnMainThread (() => {
		CountriesLabel.Text = string.Format ("Countries: {0}", number_of_countries);
	});
}
 

Código usando C# 5.0:

async Task ShowStuffAsync ()
{
	var client = new HttpClient ();

	var content = JsonValue.Parse (await client.GetStringAsync ("http://api.worldbank.org/countries?format=json"));
	int number_of_countries = content [0] ["per_page"];
	int done = 0, error = 0;

	CountriesLabel.Text = string.Format ("Countries: {0} done: 0 error: 0", number_of_countries);

	foreach (JsonObject c in content [1]) {
		try {
			string country_url = string.Format ("http://api.worldbank.org/countries/{0}/indicators/NY.GDP.MKTP.CD&format=json", (string)c ["id"]);
			var json = JsonValue.Parse (await client.GetStringAsync (country_url));
			var map = await LoadCountryLogoAsync (json ["name"]);
			if (map != null){
				var position = await LookupCountryPositionAsync (c ["longitude"], c ["latitude"]);
				if (position != null){
					AddPin (map, position);
					status.Text = json ["name"];
					++done;
				}
			}

		} catch (Exception e) {
			++error;
			status.Text = "Got exception "+ e;
		}
		CountriesLabel.Text = string.Format ("Countries: {0} done: {1} error: {2}", number_of_countries, done, error);
	}
	CountriesLabel.Text = string.Format ("Countries: {0}", number_of_countries);
}
 

Unobtrusive jQuery Validation com forms dinâmicos

3/6/2013 12:53:00 AM By Felipe Pessoto

Recentemente tive um problema ao usar um form dinâmico no ASP.NET MVC. A validação não "intrusiva" é processada assim que a página termina de carregar, e a partir daquele momento não é mais alterada. Porém eu tinha uma página que carregava todo o conteúdo do form via Ajax, e neste caso é ainda pior do que carregar um novo form, pois se o form já existia ele é marcado para não ser processado novamente, então preciso primeiro desmarca-lo e depois ativar a validação novamente. O código é simples, só não é algo suportado ou documentado, e depende de detalhes da implementação atual:

var form = $('#form-adiciona-entrega');

form.removeData('validator').removeData('unobtrusiveValidation');//Este passo só é necessário se o form já existia. Se o form em si é gerado dinamicamente, não precisa desse "reset"

$.validator.unobtrusive.parse(form);

 

Encontrei também este código, que diz fazer isso automaticamente a cada chamada ajax, mas que depende também de detalhes do ASP.NET MVC:

$(document).ajaxSuccess(function (event, xhr, settings) {
   if (settings.mvcTargetElement) {
       $(settings.mvcTargetElement.getAttribute("data-ajax-update")).each(function () {
          $.validator.unobtrusive.parse(this);
     });
  }
});

 

Fontes:

http://btburnett.com/2011/01/mvc-3-unobtrusive-ajax-improvements.html

http://stackoverflow.com/questions/11045256/jquery-client-side-validate-not-working

ASP.NET MVC, usando Array, List, IEnumerable no ViewModel e ModelBind

11/16/2012 9:44:00 PM By Felipe Pessoto

Um problema comum que sempre me fazia perder um bom tempo e deixava o código um pouco mais sujo era trabalhar com Arrays ou melhor, IEnumerable's em geral. Normalmente tenho um IEnumerable<AlgumaCoisa> no meu ViewModel e quero usar todas as funcionalidades do ASP.NET MVC normalmente com o Model Binding.

Assumindo o seguinte ViewModel:

public class PredioViewModel
{
    public string Nome { get; set; }
    public List<AndarViewModel> Andares { get; set; }
}
public class AndarViewModel
{
    public int Id { get; set;}
    public string Nome { get; set; }
    public decimal Preco { get; set; }
}

Read more...