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...

ASP.NET MVC Windows Authentication redirecionando pro login

9/26/2012 1:29:23 AM By Felipe Pessoto

Esse problema aconteceu em um novo projeto que estou trabalhando na Athié, em ASP.NET MVC 4, mas deve acontecer no 3 também, já que acredito ser um problema no WebPages utilizado pelo Razor. Não sei exatamente o motivo, mas o WebPages sobrescreve algumas configurações de Membership para "facilitar" o uso do WebMatrix. Não concordo com a decisão já que o produto principal não é o WebMatrix. De qualquer forma, o problema é o seguinte, já configurei o web.config pra usar Windows Authentication, com e sem impersonate e o primeiro request sempre redirecionava para a url de login que no caso não existe e dá erro 404. Pensei ser um problema do Chrome, mas também acontecia com o Firefox, exceto com o Internet Explorer.

Para solucionar o problema é preciso colocar essa chave no appSettings:

<add key="enableSimpleMembership" value="false"/>

Voltando o Membership ao padrão em vez de sobrescrever pelo Membership do WebPages.

É estranho que o ASP.NET MVC 3 e 4 vem com templates de Intranet e os mesmos não incluem essa chave.

UPDATE: A raiz do problema está na presença da dll WebMatrix.Data.dll. No meu caso ela estava presente pois o projeto foi criado com o template Internet e depois alterado pra Windows Authentication. Removendo essa dll não precisa da chave no web.config