quinta-feira, 2 de outubro de 2014

Pontos em parâmetros no MVC - Problema com virtual directory

Tive um problema com meus parâmetros no MVC. Se você costuma mexer com MVC e já brincou com o routeConfig, sabe que pode mexer na Url como quiser. No meu caso, tudo que eu estava fazendo era passar uma string como parâmetro na url. E funcionava lindamente. Na minha máquina.

Subimos para o ambiente de homologação, tudo certo. Subimos para produção. O cliente pediu para subir temporariamente oculto por uma pasta. Fácil, virtual directory. Tudo funcionando, ótimo... Até aparecer um ponto entre as palavras que estavam como parâmetro. Foi lindo:

"The resource you are looking for has been removed, had its name changed, or is temporarily unavailable."

Muito bonito. Um 404. De uma página que existe. A diferença? O maldito ponto. E vai fazer busca por MVC dot parameter. Enfim, busquei e acabei encontrando uma solução para colocar no web.config: http://stackoverflow.com/questions/11048863/modules-runallmanagedmodulesforallrequests-true-meaning.

Logo abaixo, alguém pergunta sobre a vulnerabilidade da solução. Me deu insegurança. Ninguém respondeu. Mas, em um dos comentários, a pessoa cita outro post: http://stackoverflow.com/questions/20998816/dot-character-in-mvc-web-api-2-for-request-such-as-api-people-staff-45287

Apesar da solução aceita ser a mesma, logo abaixo alguém postou outra solução:

      <remove name="UrlRoutingModule-4.0" />
      <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />

Procurei apenas pelo trecho e achei lugares afirmando que seria mais segura, por ser menos aberta.

Fora isso, apenas uma coisa: para simular o erro com virtual directory não foi necessário ficar publicando diversas vezes. Apenas colocar o path a mais no routeConfig foi suficiente, o erro que aconteceu foi o mesmo.

quarta-feira, 23 de julho de 2014

Criando seu "bloco" de execução

Faz algum tempo que procuro um meio de executar códigos antes e depois da execução de um método, de forma limpa. Estava procurando isto especificamente por causa desde tipo de código:

    public void MyMethod()
    {
            BeginTransaction();

            try
            {

                    [meu código para executar no DB]

                    CommitTransaction();
            }
            catch (Exception)
            {
                    RollbackTransaction();
                    throw;
            }

    }

Este código é complexo de entender, e suja muito os métodos, principalmente se eu tiver que repeti-lo diversas vezes. Além disso, corre o risco, como já aconteceu, de eu esquecer de colocar alguma parte dele (por exemplo, o CommitTransaction).

Procurei um meio de colocar apenas um Attribute acima do método, e ele entender que quero cercar o método com o meu código. Descobri que é necessário mexer com um nível mais baixo, e até há uma biblioteca (paga) que posso baixar para fazer isso por mim.

Mas encontrei uma sugestão que, apesar de não ser tão limpa quanto colocar um Attribute, deixa o código simples e é como se você criasse um bloco de execução seu: http://stackoverflow.com/questions/6046382/suggestions-for-making-a-reusable-try-catch-block-in-c

Seguindo o exemplo, meu código acima ficaria assim:

    public void MyMethod()
    {
            InTransaction(() =>
            {
                    [meu código para executar no DB]
            });
    }

    protected void InTransaction(Action action)
    {
            BeginTransaction();
            try
            {

                    action();

                    CommitTransaction();
            }
            catch (Exception)
            {
                    RollbackTransaction();
                    throw;
            }

    }

Assim todos os métodos que forem usar transaction apenas precisarão ser colocados dentro desse bloco novo. Se for necessário retornar valor, a mudança nesse código seria esta:

    public T MyMethod()
    {
            return InTransaction(() =>
            {
                    return [meu código para executar no DB]
            });
    }

    protected T InTransaction<T>(Func<T> action)
    {
            BeginTransaction();
            try
            {

                    var result = action();

                    CommitTransaction();




                    return result;
            }
            catch (Exception)
            {
                    RollbackTransaction();
                    throw;
            }

    }

Assim é possível encurtar o código e diminuir os erros que podem ser cometidos ao programar.

quarta-feira, 18 de junho de 2014

CheckBoxList no MVC

Criei uma classe e um HTML para fazer um CheckBoxList no MVC. Coloquei na minha biblioteca, a Ak, que está no bitbucket.

A classe:

using System;
using System.Collections.Generic;
using System.Linq;

namespace Ak.MVC.Forms
{
    /// <summary>
    /// To create a CheckBoxList in Form
    /// </summary>
    public class CheckBoxList
    {
        /// <summary>
        /// Items of CheckBoxList
        /// </summary>
        public IEnumerable<CheckBoxItem> Items { get; set; }

        /// <summary>
        /// Constructor for MVC build
        /// </summary>
        public CheckBoxList()
        {
            Items = new List<CheckBoxItem>();
        }

        /// <summary>
        /// Constructor to use in your code
        /// </summary>
        /// <param name="all">All the items to show</param>
        /// <param name="chosen">Just the chosen items IDs</param>
        public CheckBoxList(IEnumerable<IListable> all, IEnumerable<Int32> chosen)
            : this()
        {
            Items = all.Select(e => add(e, chosen));
        }

        private static CheckBoxItem add(IListable item, IEnumerable<Int32> chosen)
        {
            return new CheckBoxItem
            {
                ID = item.ID,
                Name = item.Name,
                Chosen = chosen.Contains(item.ID),
            };
        }

        /// <summary>
        /// Get the chosen ids
        /// </summary>
        public IEnumerable<Int32> GetChosen()
        {
            return Items.Where(cbi => cbi.Chosen).Select(cbi => cbi.ID);
        }


        /// <summary>
        /// Item for CheckBoxList
        /// </summary>
        public class CheckBoxItem
        {
            // ReSharper disable CSharpWarnings::CS1591
            public Int32 ID { get; set; }
            public String Name { get; set; }
            public Boolean Chosen { get; set; }
            // ReSharper restore CSharpWarnings::CS1591
        }

    }
}


O HTML (colar em Views/Shared/EditorTemplates/CheckboxList.cshtml)
@model Ak.MVC.Forms.CheckBoxList

<ul>
    @for (var e = 0; e < Model.Items.Count; e++)
    {
        <li>
            @Html.CheckBoxFor(m => m.Items[e].Chosen)
            @Html.HiddenFor(m => m.Items[e].ID)
            @Html.LabelFor(m => m.Items[e].Chosen, Model.Items[e].Name)
        </li>
    }
</ul>




quinta-feira, 5 de junho de 2014

Connection Refused: tentando ver seu site em outros computadores da rede

Eu estava tentando acessar meu site pela rede. É um site local, configurado no IIS, e eu estava tentando acessar o site pelo IP dele. O motivo do post é porque eu já tinha tido esse problema diversas vezes e, todas elas, desistido de descobrir como resolver.

Pelo browser do celular, ele retornava como se o site não estivesse no ar. Porém, tentando fazer uma chamada pelo android, eu obtia a resposta Connection Refused.

A solução na verdade é bem simples:
Control Panel > Windows Firewall > Advanced Settings (menu lateral) > Inbound Rules

Ali, no final da lista (se estiver ordenada por nome), você encontra dois itens World Wide Web Services. Não fique brigando por mais meia hora como eu e habilite o item certo: um é para HTTP, o outro para HTTPS -- a coluna Name, se você não esticá-la, pode cortar essa informação.



Licença do app android

Recentemente eu coloquei um app na Google Play. Quando fui implementar a licença nele, tive um problema: eu não conseguia, de forma nenhuma, ter a resposta de licença que configurava no Google Play (é possível configurar, para contas de teste).

Fiquei horas mexendo nisso e acabei desistindo. No dia seguinte, alguém que conhecia android me respondeu: o problema é que, para eu ter essa resposta, mesmo que o app esteja em Alpha ou Beta, é necessário que ele esteja publicado.

A resposta que eu recebia, ao invés da que estava configurada, era ERROR_NOT_MARKET_MANAGED. Isso ocorre na classe que você implementou a partir de LicenseCheckerCallback, o método applicationError é chamado e errorCode vem com o valor 3.

quarta-feira, 4 de junho de 2014

Webform com referência a código buildado

Hoje eu fiquei umas 2 ou 3 horas tentando descobrir porque minha página simplesmente se recusava a aceitar que uma classe que eu criei existia. O erro que dava era no arquivo designer:

The type or namespace name MyClass does not exist in the namespace MyProject (are you missing an assembly reference?)

No web.config:
    <system.web>
        <pages>
            <controls>
                <add tagPrefix="tag" namespace="MyProject" />
            </controls>
        </pages>
    </system.web>

Na página:
         <tag:MyClass ID="MyControl" runat="Server" />

No designer (gerado pelo Visual Studio):
         /// <summary>
        /// controle MyControl.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::MyProject:MyClass MyControl;

A classe:
        public class MyClass : Control
        {
                (código fonte)
        }

Tudo no lugar, certo? Foi o que pensei. Então pedi ajuda para alguém que realmente conhece WebForms. Descobri que eu tinha, por acidente, deixado o arquivo MyClass como Content no projeto, quando deveria estar como Compile.

Clique na class, aperte F4, na tela de Properties vá em Build Action e coloque Compile. Pronto. Agora funciona.

sexta-feira, 18 de abril de 2014

Android: como descobrir que o usuário só virou o celular, não recarregou a tela realmente

Meu app se comunica com o meu site para trazer dados. Coloquei isso, feliz e contente, no onCreate das Activities. E, triste, descobri que, ao virar o celular, ele ia de novo lá chamar o site, ao invés de só virar o que existe ali.

Isso acontece porque, na verdade, quando você vira o celular, ele destrói a Activity e cria novamente. Estava procurando um jeito de detectar se minha Activity foi recarregada porque eu virei o celular ou se realmente é para renovar o conteúdo dela.

Achei este post aqui, no StackOverflow: http://stackoverflow.com/questions/7295804/is-that-possible-to-check-was-oncreate-called-because-of-orientation-change

Ao tentar usar a resposta que foi apontada como solução, ganhei deprecateds e métodos inexistentes. Mais abaixo, encontrei uma solução que funcionava.

Para facilitar, construi uma outra classe, que minhas activities herdarão para eu ter este controle:

public class FixOrientationActivity extends Activity
{
    private static int oldConfigInt;
    protected boolean rotated;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
       
        rotated =
            (oldConfigInt & ActivityInfo.CONFIG_ORIENTATION)
            ==
            ActivityInfo.CONFIG_ORIENTATION;
    }
   
    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        oldConfigInt = getChangingConfigurations();
    }
   
    @Override
    protected void onResume()
    {
        super.onResume();
        oldConfigInt = 0;
    }
   
}

sexta-feira, 24 de janeiro de 2014

String de conexão diferente para cada desenvolvedor

Estou trabalhando em um projeto com outra empresa. Eu e um colega ficamos algumas horas tentando fazer com quem pudéssemos ter uma string de conexão diferente desta outra empresa sem ficar alterando isso toda hora que formos dar commit do código para eles.

Eles já usavam uma configuração no appSettings para determinar qual string de conexão seria usada. Meu colega descobriu que é possível mudar o valores do appSettings referenciando um arquivo (usando o atributo file que o appSettings possue). Seu web.config fica dessa forma:

(...)
    <connectionStrings>
        <add name="conn1" connectionString="[banco deles]" />
        <add name="conn2" connectionString="[nosso banco]" />
    </connectionStrings>
    <appSettings file="appsettings_desenv.config">
        <add key="DefaultConnectionString" value="conn1" />
    </appSettings>
(...)


Então você cria o arquivo appsettings_desenv.config com este conteúdo (na mesma pasta do web.config):

<?xml version="1.0"?>
<appSettings>
    <add key="DefaultConnectionString" value="conn2" />
</appSettings>


Na sua máquina, este arquivo existindo, DefaultConnectionString passa a valer conn2. Então você pega, no código, a string de conexão a partir da configuração:

var key = ConfigurationManager.AppSettings["DefaultConnectionString"];
var connectionString = ConfigurationManager.ConnectionStrings[key].ConnectionString;


E virá o que existe em conn2. Se a pessoa não possui o arquivo, virá o conn1.

segunda-feira, 6 de janeiro de 2014

Datepicker sem o campo Dia

Faz pouco tempo, comecei a mexer com android. Já existe em outros lugares essa solução, mas, como os casos que vi apontavam o nome de campo errado e faziam loops através de todos os campos, resolvi criar um post sobre isso.

Eu queria colocar um DatePicket no meu app que não tivesse o campo de Dia. Aparentemente isso não pode ser configurado: tem que ser na base da gambiarra. Então, aqui está:

DatePicker picker = (DatePicker)findViewById(R.id.[seu campo]);
Field field = picker.getClass().getDeclaredField("mDaySpinner");

field.setAccessible(true);
Object yearPicker = field.get(picker);
((View) yearPicker).setVisibility(View.GONE);

Colocando isso na Activity, no onCreate, você consegue deixa apenas o mês e o ano no picker.