Banner_Dingus  

SharedCache: hostname en vez de IP

Por: Emilio Torrens  /  En: , ,

SharedCacheSharedCache es un Sistema de cache distribuido de alto rendimiento y un sistema de replicación de caché para aplicaciones. NET que se ejecutan en las granjas de servidores, nosotros lo usamos hace tiempo y la verdad que va muy bien.

Ahora que hemos pasado parte de la aplicación al cloud y levantamos servidores según necesidad nos hemos encontrado que a los clientes del cache no se les podía definir la dirección del servidor por nombre, tenia que ser por IP, eso nos limitaba ya que al levantar un nuevo nodo, o al rehacer un servidor obteníamos otra IP y teníamos que ir a actualizar la configuración de todos los clientes.

La solución pasa por hacer una pequeña modificación en el código del cliente:

1- Busca SharedCache.WinServiceCommon/Sockets/SharedCacheTcpClient.cs.

2- Busca el constructor SharedCacheTcpClient(string host, int port) y modifícalo para que quede así:

public SharedCacheTcpClient(string host, int port)
{
	#region Access Log
	#if TRACE
	{
		Handler.LogHandler.Tracking("Access Method: " + this.GetType().ToString() + "-" +                       ((object)MethodBase.GetCurrentMethod()).ToString() + "; ;";);
	}
	#endif
	#endregion Access Log
 
	this.host = host;
	this.port = port;
 
	//Emilio: Comprobamos si es una IP o es un nombre de host
	IPAddress address;
	bool isIpAdress = (IPAddress.TryParse(host, out address));
 
	this.serverConnection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
	if (isIpAdress)
	{
		var endpoint = new IPEndPoint(IPAddress.Parse(this.host), this.port);
		this.serverConnection.Connect(endpoint);
	}
	else
	{
		this.serverConnection.Connect(host,port);
	}
 
	this.serverConnection.NoDelay = true;
}


3- Compilas la librería SharedCache.WinServiceCommon y la machacas en los clientes.

Nueva versión de MongoMapper.NET

Por: Emilio Torrens  /  En: , , , ,

He subido una nueva versión a master con algunos cambios interesantes:

  • Si esta configurado el SafeMode devuelve la excepción que devuelve el servidor en las operaciones de escritura
  • Se puede definir IdGenerator, incremental o ObjectId, a las listas de clases contenidas en las clases que heredan de MongoMapper. Para eso hay que:
    • Heredar la clase contenida de MongoMapperChild
    • Marcar la propiedad que es colección con el atributo MongoChildCollection
  • Se puede definir si los Id (tanto los de MongoMapper como los de las clases embebidas) son incrementales en el App.config o con el atributo MongoMapperIdIncrementable de la clase.

Aquí dejo un ejemplo de como definir las clases:

    public class Child: MongoMapperChild
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime BirthDate { get; set; }
        public string Country { get; set; }
    }
 
    [MongoKey(KeyFields = "")]
    [MongoIndex(IndexFields = "ID,Country")]
    [MongoIndex(IndexFields =  "Name")]
    [MongoMapperIdIncrementable(IncremenalId = true, ChildsIncremenalId = true)]
    public class Person : MongoMapper
    {
        public Person()
        {
            Childs = new List<Child>();
        }
 
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime BirthDate { get; set; }
        public bool Married { get; set; }
        public decimal BankBalance { get; set; }
        public string Country { get; set; }
 
        [MongoChildCollection]
	public List<Child> Childs { get; set;}
    }

https://github.com/emiliotorrens/MongoMapper.NET

Error “Ambiguous discriminator” en el Driver C# de MongoDB

Por: Emilio Torrens  /  En: , , ,

 

Este error nos lo podemos encontrar en un escenario en el que trabajemos con interfaces y “Dependecy Injection” en el que podemos escribir en la misma colección diferentes clases que implementen el mismo interfaz.

Si escribimos una clase, luego la otra y después pedimos las dos el Driver nos devuelve el error “Ambiguos discriminator MyClass”.

Eso es debido a que al escribir las clases el driver guarda el mapeo de ambas con el mismo nombre así que al leer busca tipos asignables y al encontrar mas de uno lanza esta excepción.

Pienso que debería ser configurable ya que en el siguiente escenario, que pongo a modo de ejemplo, recupero todos los documentos como una lista del interface, lo que debería funcionar bien:

namespace EtoolTech.MongoDB.Mapper.Test.NUnit1
{
    public class MyClass: IMyInterface
    {
        public long _id { get; set; }
        public int Data { get; set; }
    }
}
 
namespace EtoolTech.MongoDB.Mapper.Test.NUnit2
{
    public class MyClass : IMyInterface
    {
        public long _id { get; set; }
        public int Data { get; set; }
    }
}
 
namespace EtoolTech.MongoDB.Mapper.Test.NUnit
{
    public interface IMyInterface
    {
        long _id { get; set; }
        int Data { get; set; }
    }
 
    [TestFixture]
    public class AmbiguousDiscriminatorTest
    {
        [Test]
        //Este test solo funcionara con el driver modificado
        public void Test()
        {
            MongoCollection col = Mapper.Helper.Db("XXX").GetCollection("MyClass");
 
            col.RemoveAll();
 
            NUnit1.MyClass class1 = new NUnit1.MyClass() { _id = 1, Data = 1 };
            NUnit2.MyClass class2 = new NUnit2.MyClass() {_id = 2, Data = 2};
 
            col.Insert(class1);
            col.Insert(class2);
 
            List<IMyInterface> list = col.FindAll().ToList();
        }
    }
}

Mientras lo solucionan puedes modificar el Driver para que no de la excepcion, hay que comentarla en el archivo “Bson\Serialization\BsonDefaulSerialization.cs” es la linea 205.

Extension Methods en MongoMapper.NET

Por: Emilio Torrens  /  En: , , , ,

 

He subido una versión a master con el tema de los extension methods terminado.

Un código que era:

Country c = new Country {Code ="US", Name = "Estados Unidos"};
c.Save<Country>();
 
Country c2 = Country.FindByKey<Country>("US");
 
List<Country> Countries = Country.FindAsList<Country>("Code", "US");

Se puede escribir:

Country c = new Country {Code ="US", Name = "Estados Unidos"};
c.Save();
 
Country c2 = new Country();
c.FillByKey("US");
 
List<Country> Countries = new List<Country>();
Countries.MongoFind("Code", "US");

Puedes ver ejemplos aquí.

La lista de extension methods es:

Para clases que heredan de MongoMapper:

  • Save
  • Delete
  • FindByKey
  • FindByMongoId

Para listas de objetos que hereden de MongoMapper:

  • MongoFind


https://github.com/emiliotorrens/MongoMapper.NET

Tiempos de proceso en .NET

Por: Emilio Torrens  /  En: , , ,

La mejor forma de medir tiempo de proceso en nuestro código es usando StopWatch.

Esta clase es la que nos dará toda la información que necesitamos, la encontraremos en el espacio de nombres System.Diagnostics.

Aquí dejo un ejemplo de uso:

var timer = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
{
 //...
}
timer.Stop();
 
Console.WriteLine(string.Format("Elapsed: {0}", timer.Elapsed));
Console.WriteLine(string.Format("MS: {0}", timer.ElapsedMilliseconds));
Console.WriteLine(string.Format("Ticks: {0}", timer.ElapsedTicks));

Extension Methods en .NET

Por: Emilio Torrens  /  En: , ,

Los ExtensionMethods nos permiten crear metodos que estén disponibles en todos los tipos de objetos que les definamos.

Por ejemplo podemos definir que todas nuestras clases tipo A tengan un método ToJson que devuelva una String con el Json creando el siguiente método:

public static string ToJson(this A o) {return JsonConvert.SerializeObject(o);}

Lo único que hay que hacer es using del namespace donde estén definidos los metodos.

En este ejemplo creamos un ExtensionMethod para que todos los objetos tengan el método ToJson, eso lo hacemos creando el método con Object, y después le añadimos un método al object String para que lo transforme en un Objeto Tipado

public static string ToJson(this Object o) {return JsonConvert.SerializeObject(o);}
public static T FromJson<T>;(this String str) {return JsonConvert.DeserializeObject<T>(str);}

Aquí el Test:

public class MyClass
{
    public string value { get; set; }
    public int value2 { get; set; }        
}
 
[TestClass]
public class ExtensionMethodsTest
{
    [TestMethod]
    public void Test()
    {
        MyClass c = new MyClass {value = "hola",value2 = 50};
        string jsong = c.ToJson();
        MyClass c2 = jsong.FromJson<MyClass>();
    }
}

Personalizando los “cast” en .NET

Por: Emilio Torrens  /  En: , ,

Aquí dejo un ejemplo de como personalizar el cast de nuestras clases a otras con implicit/explicit operator, por si nos interesa controlar la conversión.

Las clases:

public class ClassString
{
    public string value1 { get; set; }
    public string value2 { get; set; }
 
    public static implicit operator ClassInt(ClassString c2)
    {
        ClassInt c = new ClassInt
        {
            value1 = int.Parse(c2.value1),
            value2 = int.Parse(c2.value2)
        };
        return c;
    }
}
 
public class ClassInt
{
    public int value1 { get; set; }
    public int value2 { get; set; }
 
    public static explicit operator ClassString(ClassInt c2)
    {
        ClassString c = new ClassString
        {
            value1 = c2.value1.ToString(), 
            value2 = c2.value2.ToString()
        };
        return c;
    }
}

El test:

[TestMethod]
public void TestMethod1()
{
    ClassInt c1 = new ClassInt {value1 = 100,value2 = 200};
 
    ClassString c2 = (ClassString) c1;
    Assert.AreEqual(c2.value1, &quot;100&quot;);
    Assert.AreEqual(c2.value2, &quot;200&quot;);
 
    ClassInt c3 = c2;
    Assert.AreEqual(c3.value1, 100);
    Assert.AreEqual(c3.value2, 200);
}

AOP y PostSharp

Por: Emilio Torrens  /  En: , , ,

AOP o POA en Español quiere decir Programación Orientada a Aspectos, este paradigma, que es relativamente moderno, no substituye a OOP en realidad la que hace es extenderlo.

Esta es la definición “formal” de AOP:

“El principal objetivo de la POA es la separación de las funcionalidades dentro del sistema:

Por un lado funcionalidades comunes utilizadas a lo largo de la aplicación.
Por otro lado, las funcionalidades propias de cada módulo.

Cada funcionalidad común se encapsulará en una entidad.”

Hay varios frameworks que nos ayudan a programar AOP, yo el que estoy usando es “PostSharp” que es para .net, y con este voy a poner un ejemplo en el que logueamos el usuario y la fecha de modificación en todos nuestros objetos implementando un interface y añadiendo un atributo al método Save.

Creamos el interface:

public interface ILogeable
 {
     string LastModificationUser { get; set; }
     DateTime LastModificationDateTime { get; set; }
 }

Luego creamos el atributo, aquí es donde esta el tema, el atributo lo creamos heredando una clase de OnMethodBoundaryAspect, que es una clase de PostSharp, de esa clase luego sobre escribimos los metodos que nos interese, estos metodos son los que se llamaran desde el método de nuestras clases que tengan este atributo, hay 4 metodos a sobre escribir: OnEntry, OnExit, OnSuccess y OnException como parámetro recibimos unos MethodExecutionArgs en los que encontraremos toda la información necesaria.

En este caso solo utilizo el OnEntry

[Serializable()]
public class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        if (typeof(ILogeable).IsAssignableFrom(args.Instance.GetType()))
        {
            ((ILogeable) args.Instance).LastModificationUser = Thread.CurrentPrincipal.Identity.Name;               
            ((ILogeable) args.Instance).LastModificationDateTime = DateTime.Now;
        }
    }        
}

Cualquier clase que implemente el interfaz y le pongamos el atributo en el método que queramos ejecutara este código, por ejemplo:

public class MyClass: ILogeable
{
    public string LastModificationUser { get; set; }
    public DateTime LastModificationDateTime { get; set; }
 
    [Log]
    public void Save() {}
}

Este es un ejemplo sencillo, las posibilidades son muchísimas, podéis ver muchos ejemplos en la pagina del PostSharp.