Cada um no seu quadrado no ASP.NET MVC

Como nos 2 últimos posts eu falei sobre dúvidas que eu tinha recebido, vou continuar na mesma linha, só que agora busquei a dúvida nesta thread do grupo .NetArchitects, entre outras (muitas) coisas que foram discutidas foi levantado a dúvida sobre a responsabilidade da View,  na dúvida o Emmanuel propõe um cenário que está exposto abaixo:

Se você tem que exibir transações de conta corrente, por exemplo, débito e crédito, mas quando o saldo fica negativo você quer colocar a linha em vermelho, é uma regra de apresentação. Mas onde estaria esse código? Acho que não estaria no domínio por que não diz respeito a ele... Seria no Controller?

Esta é uma pergunta mais comum do que se imagina, já me perguntaram diversas vezes sobre a responsabilidade de cada letra do MVC, e é por isso que eu coloquei como título deste post como “Cada um no seu quadrado”.

A resposta simples e direta para a pergunta acima é que essa regra fica na View, mas vamos elaborar um pouco mais, por que não no Controller? Simples, por que assim ele estaria invadindo o quadrado da View, vou exemplificar o meu ponto de vista:

No cenário proposto pelo Emmanuel, o Controller enviaria os dados através da ViewData para a View e ela por sua vez verificaria se o Saldo é negativo, caso fosse exibiria ele em vermelho, vamos ver um pouco de código.

Vamos começar pelo modelo, para representar o cenário criei 3 classes, a Conta que contem os dados da conta e as transações, a Transação que representa cada transação da conta e o Repositório que para o exemplo irá preencher os objetos com dados randômicos.

Conta

public class Conta
{
    public int Numero { get; set; }
    public int Agencia { get; set; }
    public IList<Transacao> Transacoes { get; set; }
    public double Saldo { get; set; }
}

Transação

public class Transacao
{
    public DateTime Data { get; set; }
    public string Descricao { get; set; }
    public double Valor { get; set; }
}

Repositório

public class ContaRepositorio
{
    public Conta get(int Agencia, int Numero) {
        //aqui faria a consulta no banco, vou fazer um
        //faz de conta para facilitar
        Conta conta = new Conta();
        conta.Agencia = Agencia;
        conta.Numero = Numero;
        conta.Transacoes = new List<Transacao>();
        Random rnd = new Random();
        for (int i = 0; i < 15; i++)
        {
            conta.Transacoes.Add(new Transacao{Data = DateTime.Now.AddDays(-i),Descricao = "Transação nº "+i, Valor= rnd.Next(0,358)});
        }
        conta.Saldo = rnd.Next(-1245, 1245);
        return conta;
    }
}

Vamos ver agora o Controller, criei uma única action que mandará através da ViewData os dados da Conta, neste ponto é que surge a dúvida, posso colocar a inteligência de trocar a cor aqui? Bom, tecnicamente pode, poderia criar um objeto intermediário que tem sido chamado de ViewModel para armazenar esta “condição”, mas na minha opinião o Controller estará invadindo o quadrado da View. Na soluçao proposta abaixo eu passo somente os dados para a View.

Controller

public class ContaController : Controller
{
    //
    // GET: /Conta/

    public ActionResult Index()
    {
        ContaRepositorio rep = new ContaRepositorio();
        ViewData.Model = rep.get(1234, 123456);
        return View();
    }

}

Vamos agora para a View, de acordo com o valor do saldo é escolhido a classe que será aplicano na tag que contém o saldo.

View

<h2>Extato de Conta</h2>

<fieldset>
    <p>Numero: <%= Html.Encode(Model.Numero) %></p>
    <p>Agencia: <%= Html.Encode(Model.Agencia) %></p>
    <table>
        <thead>
            <tr>
                <th>Data</th>
                <th>Descrição</th>
                <th>Valor</th>
            </tr>
        </thead>
        <tbody>
            <%foreach (var item in Model.Transacoes){%>
            <tr>
                <td><%= Html.Encode(String.Format("{0:d/M/yyyy}", item.Data))%></td>
                <td><%= Html.Encode( item.Descricao)%></td>
                <td><%= Html.Encode(String.Format("{0:F}", item.Valor)) %></td>
            </tr>

            <%} %>
        </tbody>
    </table>
    <%
    string classe;
    if (Model.Saldo < 0)
    {
        classe = "negativo";
    }
    else {
        classe = "positivo";
    } %>
    <p>Saldo: <span class="<%=classe%>"><%= Html.Encode(String.Format("{0:F}", Model.Saldo)) %></span></p>
</fieldset>

No CSS adicionei as seguintes classes:

.positivo{color:Blue;}
.negativo{color:Red;}

O resultado esta na imagem abaixo:

image

Este código eu fiz com a intenção de demonstrar que a View não é burra (no bom sentido), ela pode e deve conter regras que são de interface, como no exemplo acima.

O Emmanuel quis complicar um pouco mais o cenário e colocou o seguinte:

E se esse núcleo do meu sistema é consumido por diversas interfaces? Mobile, desktop, web; essa regra da interface, não deveria estar em outro local que não a interface?

Quando tive os primeiros contatos com o ASP.NET MVC tive a mesma dúvida, mas com o tempo e várias pesquisas fiquei convencido que isto não é possível e nem interessante, digo isso por 2 motivos:

  1. O primeiro é técnico, como o MVC é um padrão de Interface Gráfica ele depende muito do ambiente, que neste caso é o ASP.NET MVC, ou seja, eu não conseguiria reutilizar o Controller do ASP.NET MVC para uma aplicação Windows Form ou uma aplicação Mobile.
  2. O segundo é sobre os diferente tipos de mídias que poderíamos utilizar para a nossa interface, cada uma dessas mídias tem suas próprias preocupações de acessibilidade e usabilidade e para lidar com essas preocupações cada uma delas deve ter autonomia.

O código deste exemplo já está disponível.

Bom, espero ter respondido a dúvida do Emmanuel e de outras pessoas. Até a próxima.


Welcome to BlogEngine.NET 1.6.0

If you see this post it means that BlogEngine.NET 1.6.0 is running and the hard part of creating your own blog is done. There is only a few things left to do.

Write Permissions

To be able to log in to the blog and writing posts, you need to enable write permissions on the App_Data folder. If you’re blog is hosted at a hosting provider, you can either log into your account’s admin page or call the support. You need write permissions on the App_Data folder because all posts, comments, and blog attachments are saved as XML files and placed in the App_Data folder. 

If you wish to use a database to to store your blog data, we still encourage you to enable this write access for an images you may wish to store for your blog posts.  If you are interested in using Microsoft SQL Server, MySQL, VistaDB, or other databases, please see the BlogEngine wiki to get started.

Security

When you've got write permissions to the App_Data folder, you need to change the username and password. Find the sign-in link located either at the bottom or top of the page depending on your current theme and click it. Now enter "admin" in both the username and password fields and click the button. You will now see an admin menu appear. It has a link to the "Users" admin page. From there you can change the username and password.  Passwords are hashed by default so if you lose your password, please see the BlogEngine wiki for information on recovery.

Configuration and Profile

Now that you have your blog secured, take a look through the settings and give your new blog a title.  BlogEngine.NET 1.4 is set up to take full advantage of of many semantic formats and technologies such as FOAF, SIOC and APML. It means that the content stored in your BlogEngine.NET installation will be fully portable and auto-discoverable.  Be sure to fill in your author profile to take better advantage of this.

Themes and Widgets

One last thing to consider is customizing the look of your blog.  We have a few themes available right out of the box including two fully setup to use our new widget framework.  The widget framework allows drop and drag placement on your side bar as well as editing and configuration right in the widget while you are logged in.  Be sure to check out our home page for more theme choices and downloadable widgets to add to your blog.

On the web

You can find BlogEngine.NET on the official website. Here you'll find tutorials, documentation, tips and tricks and much more. The ongoing development of BlogEngine.NET can be followed at CodePlex where the daily builds will be published for anyone to download.

Good luck and happy writing.

The BlogEngine.NET team


Upload com ASP.NET MVC e jQuery

Já havia recebido 3 e-mails com dúvidas de como fazer upload no ASP.NET MVC e hoje me chamaram no  messenger com a mesma dúvida, então decidi escrever um post de como fazer isso de forma simples e bonita, é claro que vou usar o jQuery para isso.

No Web Form tínhamos um controle server side chamado FileUpload e no ASP.NET MVC não existe este controle, então temos que escrever o HTML necessário para fazer o formulário da forma correta, eu estou usando o plugin uploadify que faz o envio assíncrono de arquivos para o servidor, ele é muito flexível, mas irei mostrar somente o básico dele.

A primeira coisa a saber é que para enviar um arquivo para o server é necessário que o formulário contenha o parâmetro enctype configurado para "multipart/form-data", sem isso ele não vai conseguir enviar o arquivo, vejam o código HTML abaixo:

<h2>Upload</h2>
<% using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{ %>
    <fieldset>
    <label for="photo">Insira a sua foto:</label><input type="file" id="photo" name="photo" /><br />
    <input type="submit" value="Enviar" />
    </fieldset>
<%} %>

Agora  faça o download do plugin uploadify, copie os arquivos jquery.uploadify.v2.1.0.min.js e swfobject.js para a pasta Scripts, copie também os arquivos cancel.png, uploadify.css e uploadify.swf para a pasta Content.

Crie uma pasta com o nome de files na raiz da aplicação, é nesta pasta que será salvo os arquivos enviados.

Inclua o código JavaScript na MasterPage Site.Master (este é apenas um exemplo, eu prefiro colocar este javascript na View):

<script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script>
<link href="../../Content/uploadify.css" rel="stylesheet" type="text/css" />
<script src="../../Scripts/swfobject.js" type="text/javascript"></script>
<script src="../../Scripts/jquery.uploadify.v2.1.0.min.js" type="text/javascript"></script>
<script type="text/javascript">
    $(document).ready(function() {
        $('#photo').uploadify({
            'uploader': '/content/uploadify.swf',
            'script': '<%=Url.Action("Upload","Home") %>',
            'cancelImg': '/content/cancel.png',
            'fileDesc':'Aruivos de Imagem',
            'fileExt': '*.jpg;*.gif',
            'sizeLimit':'4000000',
            auto:true
        });
    });
</script>

No código acima eu estou configurando o endereço de onde estão os arquivos necessários para o plugin, defino o caminho da action que irá receber o arquivo, o tamanho máximo do arquivo, habilitei a opção auto que automaticamente irá enviar o arquivo após a seleção do mesmo e também defino a extensão permitida para fazer o upload, isso é bem interessante porque na caixa de dialogo só irá aparecer os arquivos com a extensão que você definir.

Existem mais configurações que podem ser utilizadas, você pode conferir elas na documentação do plugin.

Agora só está faltando o código server side, eu criei uma action chamada upload no HomeController que irá receber e salvar o arquivo:

[AcceptVerbs(HttpVerbs.Post)]
public void Upload(string id)
{
    var file = this.Request.Files[0];
    string savedFileName = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "files");
    savedFileName = System.IO.Path.Combine(savedFileName, System.IO.Path.GetFileName(file.FileName));
    file.SaveAs(savedFileName);
    Response.Write("OK");
}

Com isso você já terá funcionando o jQuery uploadify com o ASP.NET MVC, vejam como fica no final:

jquery_uploadfy_in_action

Baixe a solução completa.