Arquivo da tag: programação

Adicionando Atributos em Propriedades (C#)

Adicionando Atributos em Propriedades (C#)

Nem sempre é possível utilizar objetos complexos (classes) para incluir informações em propriedades de classes. Essa situação pode acontecer quando se está dando manutenção em um código que está amarrado a outro comportamento que não pode ser alterado.

No caso que deu origem à esse código, a classe possui uma dezena de propriedades públicas string que já estavam atreladas a outras classes e comportamento em outras partes do código. Não havia liberdade de criação de outras classes que incorporassem mais propriedades, então decidi enfeitar cada propriedade com atributos personalizados.

Dessa forma, foi possível no código acessar mais informações com base somente nessa propriedade string.

O código foi usado em uma aplicação de aluguel de móveis para eventos. Segue o código:

Classe do Objeto amarrado (Furniture.cs) com as classes de atributos no mesmo arquivo (para facilitar o entendimento)

using System;
using System.ComponentModel;
using System.Globalization;
using static ADX18.Library.Helpers.ADX18Helper;
 
namespace ADX18.Library.Models
{
    public class Furniture : iTestObject
    {
        [DisplayName("Mode Cupboard"),
            MemberPrice("225.50"),
            NonMemberPrice("247.50")]
        public string ModeCupboard_Quantity { get; set; }
        public string ModeCupboard_Colour { get; set; }
        [DisplayName("Poly Voge Chair"),
            MemberPrice("52.25"),
            NonMemberPrice("55.00")]
        public string PolyVogue_Quantity { get; set; }
        public string PolyVogue_Colour { get; set; }
        [DisplayName("Poly Vogue Stool"),
            MemberPrice("74.25"),
            NonMemberPrice("80.85")]
        public string PolyVogueStool_Quantity { get; set; }
        public string PolyVogueStool_Colour { get; set; }
        [DisplayName("Club Stool"),
            MemberPrice("77.00"),
            NonMemberPrice("82.50")]
        public string ClubStool_Quantity { get; set; }
        public string ClubStool_Colour { get; set; }
        [DisplayName("Ikon Dry Bar"),
            MemberPrice("165.00"),
            NonMemberPrice("177.65")]
        public string IkonDryBar_Quantity { get; set; }
        public string IkonDryBar_Colour { get; set; }
        [DisplayName("Ikon Meeting Table"),
            MemberPrice("143.00"),
            NonMemberPrice("154.00")]
        public string IkonMeetingTable_Quantity { get; set; }
        public string IkonMeetingTable_Colour { get; set; }
        [DisplayName("Ikon Illuminated Dry Bar"),
            MemberPrice("192.50"),
            NonMemberPrice("209.00")]
        public string IkonIlluminatedDryBar_Quantity { get; set; }
        public string IkonIlluminatedDryBar_Colour { get; set; }
        [DisplayName("Mode Dry Bar Table"),
            MemberPrice("104.50"),
            NonMemberPrice("108.90")]
        public string ModeDryBarTable_Quantity { get; set; }
        public string ModeDryBarTable_Colour { get; set; }
        [DisplayName("Mode Meeting Table"),
            MemberPrice("99.00"),
            NonMemberPrice("107.25")]
        public string ModeMeetingTable_Quantity { get; set; }
        public string ModeMeetingTable_Colour { get; set; }
        [DisplayName("Mode Coffee Table"),
            MemberPrice("93.50"),
            NonMemberPrice("100.65")]
        public string ModeCoffeeTable_Quantity { get; set; }
        public string ModeCoffeeTable_Colour { get; set; }
        [DisplayName("Cuba Lounge One Seater"),
            MemberPrice("165.00"),
            NonMemberPrice("189.75")]
        public string CubaLoungeOneSeater_Quantity { get; set; }
        public string CubaLoungeOneSeater_Colour { get; set; }
        [DisplayName("Cuba Lounge One Seater Corner"),
            MemberPrice("165.00"),
            NonMemberPrice("189.75")]
        public string CubaLoungeOneSeaterCorner_Quantity { get; set; }
        public string CubaLoungeOneSeaterCorner_Colour { get; set; }
        [DisplayName("Cuba Lounge One Seater No Arm Rests"),
            MemberPrice("165.00"),
            NonMemberPrice("189.75")]
        public string CubaLoungeOneSeaterNoArms_Quantity { get; set; }
        public string CubaLoungeOneSeaterNoArms_Colour { get; set; }
        [DisplayName("Cuba Lounge"),
            MemberPrice("275.00"),
            NonMemberPrice("308.00")]
        public string CubaLounge_Quantity { get; set; }
        public string CubaLounge_Colour { get; set; }
        [DisplayName("Quattro Display Plinth 600mm"),
            MemberPrice("165.00"),
            NonMemberPrice("178.75")]
        public string QuattroDisplay600_Quantity { get; set; }
        public string QuattroDisplay600_Colour { get; set; }
        [DisplayName("Quattro Display Plinth 900mm"),
            MemberPrice("187.00"),
            NonMemberPrice("207.90")]
        public string QuattroDisplay900_Quantity { get; set; }
        public string QuattroDisplay900_Colour { get; set; }
        [DisplayName("Zig Zag Brochure Stand"),
            MemberPrice("115.50"),
            NonMemberPrice("123.20")]
        public string ZigZagStand_Quantity { get; set; }
        public string ZigZagStand_Colour { get; set; }
        [DisplayName("Carpet Floor Tiles"),
            MemberPrice("247.50"), NonMemberPrice("287.10"),
            NonMemberUnitPrice("31.90"),
            MemberUnitPrice("27.50")]
        public string CarpetFloorTiles_Quantity { get; set; }
        public string CarpetFloorTiles_Colour { get; set; }
        [DisplayName("Raised Floor Tiles"),
            MemberPrice("940.50"),
            NonMemberPrice("1188.00"),
            NonMemberUnitPrice("132.00"),
            MemberUnitPrice("104.50")]
        public string RaisedFloorTiles_Quantity { get; set; }
        public string RaisedFloorTiles_Colour { get; set; }
 
    }
// Classes de Atributos personalizadas
    public class NonMemberUnitPrice : Attribute
    {
        private decimal value;
        public decimal Value => value;
        public NonMemberUnitPrice(string number)
        {
            value = decimal.Parse(number, CultureInfo.InvariantCulture);
        }
    }
    public class NonMemberPrice : Attribute
    {
        private decimal value;
        public decimal Value => value;
        public NonMemberPrice(string number)
        {
            value = decimal.Parse(number, CultureInfo.InvariantCulture);
        }
    }
    public class MemberUnitPrice : Attribute
    {
        private decimal value;
        public decimal Value => value;
        public MemberUnitPrice(string number)
        {
            value = decimal.Parse(number, CultureInfo.InvariantCulture);
        }
    }
    public class MemberPrice : Attribute
    {
        private decimal value;
        public decimal Value => value;
        public MemberPrice(string number)
        {
            value = decimal.Parse(number, CultureInfo.InvariantCulture);
        }
    }
 
 
}

Código de acesso desses atributos listados como um Dictionary<string,object> pois os valores retornados em cada atributo podem variar:

using ADX18.Library.Models;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
 
namespace ADX18.Library.Helpers
{
    public static class ADX18Helper
    {
      #region Furniture Helpers
        public interface iField { }
        public interface iTestObject { }
        //tweaked from https://stackoverflow.com/questions/6637679/reflection-get-attribute-name-and-value-on-property 
        //and https://stackoverflow.com/questions/23329087/get-custom-attribute-from-specific-object-property-field
        public static Dictionary<string, object> GetAttributes<T>(this T source, Expression<Func<object>> field) where T : class
        {
            MemberExpression member = (MemberExpression)field.Body;
            if (member == null) { return null; }

            Dictionary<string, object> _dict = new Dictionary<string, object>();
            object[] propertyAttributes = typeof(T).GetProperty(member.Member.Name)
                .GetCustomAttributes(true);

            foreach (var propertyAttribute in propertyAttributes)
            {
                var attributeAttributes = propertyAttribute.GetType().GetProperties();
                foreach (var attributeAttribute in attributeAttributes)
                {
                    var propName = $"{propertyAttribute.GetType().Name}.{attributeAttribute.Name}";
                    if (!_dict.ContainsKey(propName) && attributeAttribute.Name != "TypeId")
                        _dict.Add(propName, attributeAttribute.GetValue(propertyAttribute, null));
                }
            }
            return _dict;
        }

        #endregion

    }
}

Com essas classes definidas, fica mais fácil acessar os atributos no Razor:

@model Furniture
@using ADX18.Library.Controllers
@using ADX18.Library.Models
@using ADX18.Library.Helpers;

                <div class="col-md-8">
                        @{
                            var modeCupboard = Model.GetAttributes<Furniture>(() => Model.ModeCupboard_Quantity);
                        }
                    <h5>@modeCupboard["DisplayNameAttribute.DisplayName"]</h5>
                    <p>
                        Available in four colours, this includes a lockable cupboard with shelves.<br />
                        L: 1200mm x W: 600mm x H: 1000mm
                    </p>
                    <div>
                        Member: <span class="memberPrice">$@modeCupboard["MemberPrice.Value"] each</span><br />
                        Non-member: <span class="memberPrice">$@modeCupboard["NonMemberPrice.Value"] each</span>
                    </div>
                    Quantity: <input type="text" name="ModeCupboard_Quantity" style="width: 10%;" />
                    <select name="ModeCupboard_Colour">
                        <option value="White">White</option>
                        <option value="Black">Black</option>
                        <option value="Red">Red</option>
                        <option value="Blue">Blue</option>
                    </select>
                </div>

Então, quando não puder mudar uma propriedade para incluir objetos complexos, use atributos personalizados!

Criando um sistema básico de Short Url

Criando um sistema básico de Short Url

por Carlos Casalicchio

Mídias sociais são parte corriqueira da vida de qualquer usuário de computador, especialmente aqueles que são profissionais na área.

Por isso, tenho o costume de inserir links de várias páginas em minhas assinaturas de email. No entanto, serviços como Yahoo e Outlook (online) restringem a quantidade de caracteres, dificultando a criação de assinaturas completas.

Criando um sistema básico de Short Url

Para resolver esse problema, resolvi criar um Webservice simples que fosse capaz de redirecionar qualquer short link para o url completo da assinatura.

Esse artigo mostra como é fácil criar um serviço de short url usando ASP.NET MVC com C#

Inicialmente, criei a lista de short links num xml mesmo (no momento não senti necessidade de um gerenciador de short links):

Xml

<?xml version="1.0" encoding="utf-8" ?>
<forwards>
  <url src="6uzcQsmW" id="1" created="" expires="" userid="">
    https://www.facebook.com/carlos.casalicchio
  </url>
  <url src="g89Tsdxq" id="1" created="" expires="" userid="">
     </url>
  <url src="h9KyWTfu" id="1" created="" expires="" userid="">
    https://plus.google.com/u/0/+CarlosCasalicchio/posts
  </url>
  <url src="4R7oViGj" id="1" created="" expires="" userid="">
    http://www.linkedin.com/in/ccasalicchio
  </url>
  <url src="udVmFUV8" id="1" created="" expires="" userid="">
    http://www.youtube.com/user/casalicchio
  </url>
  <url src="iTSVtpg3" id="1" created="" expires="" userid="">
    https://www.ccasalicchio.com/
  </url>
  <url src="YcbmDf4s" id="1" created="" expires="" userid="">
    http://www.lastfm.com.br/user/c_casalicchio
  </url>
  <url src="HrRbfS7u" id="1" created="" expires="" userid="">
    https://profile.yahoo.com/COUAKGLAHFWMJCPD73Z4EK5PTA/
  </url>
<url src="sx2GkN7N" id="1" created="" expires="" userid="">
    http://www.mellaniealanny.info/
  </url>
</forwards>

Como é possível ver, já deixei preparado para futuros melhoramentos, mas por enquanto só o source e o url de destino são os dados que interessam.

Depois, criei só um método para a página inicial do site, dentro do ForwardController – afinal, quero os url o mais simples possível

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Xml.Linq;

namespace Short_Links.Controllers
{
    public class ForwardController : Controller
    {
        //
        // GET: /Forward/
        public ActionResult Index(string src)
        {
            var url = from e in MvcApplication.Forwards.Root.Descendants("url") where e.Attribute("src").Value == src select e;
            string redirect = "404.html";
            if (url.DescendantNodes().Count() > 0)
             redirect = url.DescendantNodes().Single().ToString().Trim();

            return Redirect(redirect);
        }

    }
}

O próximo problema é fixar o MVC Routing para aceitar links curtos. Para isso, modifiquei o RouteConfig.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Short_Links
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Forward",
                url: "{src}",
                defaults: new { controller = "Forward", action = "Index", src="" }
                );
            //routes.MapRoute(
            //    name: "Default",
            //    url: "{controller}/{action}/{id}",
            //    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            //);
        }
    }
}

Como pode-se ver, os routings originais foram comentados, pois por enquanto o site não tem nenhuma outra função a não ser redirecionar short urls. Mas, para servir páginas normais é preciso ter outros routes.

A Última coisa feita foi definir uma variável global que contenha todos os short urls, que carregue com a aplicação. Dessa forma, evita-se ler o xml toda vez que um novo request entrar. Isso aumenta a performance do redirecionamento.

Note que só isso foi adicionado, o resto do Global.ascx não foi modificado:

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Xml.Linq;

namespace Short_Links
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode,
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static XDocument Forwards {get;set;} 

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //Load xml with all forwards
            string path =
                Server.MapPath("~/App_Data/forwards.xml");
            Forwards = XDocument.Load(path);
        }
    }
}

Mas o que fazer se alguém tentar acessar o site sem um short link? Para mitigar esse problema, criei uma página 404.html simples, que informa o usuário que ele não submeteu nenhum short link:

Html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Url Not Found</title>
    
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0A%20%20%20%20%20%20%20%20body%20%7Bwidth%3A100%25%3Btext-align%3Acenter%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20url('%2FImages%2Fgrumpy-cat-04.jpg')%20no-repeat%20top%20right%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20background-size%3A%20100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A'Segoe%20UI'%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A30px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20margin%3A0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20padding%3A0%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.not-found-body%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3Aabsolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20bottom%3A0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%3Aleft%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background-color%3A%23fff%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20opacity%3A0.6%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20box-shadow%3A%200%20-2px%202px%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-shadow%3A%200%200%202px%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-transform%3Auppercase%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.not-found-body%20span%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A12px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

</head>

<body>
    
<div class="not-found-body">

<h3>Damn you 404!</h3>

    <span>You did not insert any short urls to be redirected...</span></div>

</body>
</html>

Em código é só isso! Subi o site publicado para o provedor e voilá, agora tenho um serviço de short urls.

Baixe o código fonte do projeto Short Links.

Outras sugestões são:

  • Compre um domínio curto. No meu caso, estou usando o http://puul.ga que foi comprado através do http://dot.tk
  • Meu provedor de hosting é o http://www.godaddy.com e apontei o DNS do domínio para o Godaddy.
  • Se for usar imagens, coloque-as numa pasta de nome curto – neste projeto nomeei a pasta como ‘i’ e deixei todas as imagens com nomes curtos também. Assim, se for referenciar suas imagens em outro lugar, o link também fica curto, como por exemplo https://puul.ga/i/face.png .