miércoles, 24 de agosto de 2011

Keypressed to null

private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
{
      e.KeyChar = Convert.ToChar(0);

      //equivalent expression
      e.Handle = false;
}

jueves, 18 de agosto de 2011

Pagemaker script in SQL

IF OBJECT_ID('SPS_PAGEMAKER') IS NOT NULL
DROP PROCEDURE SPS_PAGEMAKER
GO

CREATE PROCEDURE SPS_PAGEMAKER
@ID NVARCHAR(50),
@TABLE NVARCHAR(50),
@ROWSxPAGE INT,
@CURRENTPAGE INT,
@FIELDS NVARCHAR(1000)

AS
BEGIN

/*
SUMMARY: SCRIPT PAGINADOR
CALL: SPS_PAGEMAKER('IDBHTransaction', 'BHTransaction', 5, 1)
MADE: AGO 18, 2011

EXAMPLE CODE:

DECLARE
@ID NVARCHAR(50),
@TABLE NVARCHAR(50),
@ROWSxPAGE INT,
@CURRENTPAGE INT,
@FIELDS NVARCHAR(1000)

SET @ID = 'IDBHTransaction'
SET @TABLE = 'BHTransaction'
SET @ROWSxPAGE = 5
SET @CURRENTPAGE = 2
SET @FIELDS = 'IDBHTransaction, f0, f2, f3, f4'
*/

--DYNAMIC SQL (SET @ROWS)

DECLARE @SQL NVARCHAR(1000)
DECLARE @PARAM NVARCHAR(1000)
DECLARE @pTABLE NVARCHAR(50)
DECLARE @ROWS INT

SET @SQL = N'SELECT @pROWS = COUNT(@pID) FROM ' + @TABLE;
SET @PARAM = N'@pID VARCHAR(25), @TABLE VARCHAR(25), @pROWS INT OUTPUT';
EXECUTE sp_executesql @SQL, @PARAM, @pID = @ID, @TABLE = @pTABLE, @pROWS = @ROWS OUTPUT;

--/DYNAMIC SQL

DECLARE @REST INT
DECLARE @PAGES INT

SET @REST = (SELECT @ROWS % @ROWSxPAGE)
SET @PAGES = (SELECT @ROWS / @ROWSxPAGE) IF (@REST > 0) SET @PAGES = @PAGES+1

--SELECT @ROWS AS ROWS, @ROWSxPAGE AS ROWSxPAGE, @PAGES AS PAGES, @REST AS REST

IF @CURRENTPAGE = 1
BEGIN
SET @SQL = N'SELECT ' + @FIELDS +
' FROM ' + @TABLE +
' WHERE ((' + @ID + ' >= 1) AND (' + @ID + ' < (1 + ' + CONVERT(NVARCHAR(5),@ROWSxPAGE) + ')))';
SET @PARAM = N'@TABLE VARCHAR(25)';
EXECUTE sp_executesql @SQL, @PARAM, @TABLE = @pTABLE;
END
ELSE
IF (@CURRENTPAGE = @PAGES) AND (@REST > 0)
BEGIN
SET @SQL = N'SELECT ' + @FIELDS +
' FROM ' + @TABLE +
' WHERE (' + @ID + ' >= ((' + CONVERT(NVARCHAR(5),@CURRENTPAGE) + ' - 1) * ' + CONVERT(NVARCHAR(5),@ROWSxPAGE) + ') + 1)';
SET @PARAM = N'@TABLE VARCHAR(25)';
EXECUTE sp_executesql @SQL, @PARAM, @TABLE = @pTABLE;
END
ELSE
BEGIN
SET @SQL = N'SELECT ' + @FIELDS +
' FROM ' + @TABLE +
' WHERE ((' + @ID + ' >= (((' + CONVERT(NVARCHAR(5),@CURRENTPAGE) + ' - 1) * ' + CONVERT(NVARCHAR(5),@ROWSxPAGE) + ') + 1)) AND '+
'(' + @ID + ' < ((((' + CONVERT(NVARCHAR(5),@CURRENTPAGE) + ' - 1) * ' + CONVERT(NVARCHAR(5),@ROWSxPAGE) + ') + 1) + ' + CONVERT(NVARCHAR(5),@ROWSxPAGE) + ')))';
SET @PARAM = N'@TABLE VARCHAR(25)';
EXECUTE sp_executesql @SQL, @PARAM, @TABLE = @pTABLE;
END
END
GO

Who to read an excel file using C#

Paso 1: Descargar el setup de Access Database Engine 2010 Redistributable

    http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=13255

Paso 2: Ocupar “Microsoft Excel Driver” con las clases de Odbc indicando la ruta del archivo.

    Importamos la libreria System.Data.Odbc

Código:

string CadenaConexion = @"driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};driverid=1046;dbq=C:\Users\v-edisga\Desktop\Libro1.xlsx;";

DataSet ds = null;

try
{
   using (OdbcConnection conn = new OdbcConnection(CadenaConexion))
  {
      conn.Open();
      using (OdbcCommand command = conn.CreateCommand())
     {
         command.CommandText = "SELECT * FROM [Hoja1$]";
         OdbcDataAdapter ad = new OdbcDataAdapter(command);
         ds = new DataSet();
         ad.Fill(ds);
         GridView1.DataSource = ds.Tables[0];
         GridView1.DataBind();
     }
   }
}
catch (Exception ex)
{
     Response.Write(ex.Message.ToString());
}

Source: http://ewebmx.com/2011/08/como-leer-un-archivo-de-excel-2010-en-c/

miércoles, 17 de agosto de 2011

Config file for application in C#

//Application Global Variables
int app_owner = 0;
Dictionary() operators;

...

operators = new Dictionary();

using (StreamReader str = new StreamReader(@"app.cfg"))
{
string strline;
strline = str.ReadLine();
string value = "";

while (!str.EndOfStream)
{
while (strline != "[END]")
{
if (strline.StartsWith("["))
{
value = strline;
}
else
{
operators.Add(int.Parse(strline), value);
if (value.IndexOf("*") > 0)
owner = int.Parse(strline);
}

strline = str.ReadLine();
}
}

...

//file content: app.cfg

[JOHN]
1 -------------(#) IS THE ID FOR JOHN
[THOMAS]* ------(*) MARK FOR THE OWNER OR ADMINISTRATOR
2
[END] ---------- MARK FOR THE END

Log file for application in C#

StreamWriter log;
log = File.AppendText(@"app.log");
log.WriteLine(System.DateTime.Now.ToString() + " Read DB Data.");
log.Close();

jueves, 11 de agosto de 2011

Linq & Lamda syntaxis example

var query = from q in model.Marca
select q;
foreach (var q in query)
Console.WriteLine(q.Descripcion);


var query = model.Marca;
foreach (var q in query)
Console.WriteLine(q.Descripcion);


var query = from q in model.Marca
where q.ClaveMarca == "005"
select q;
foreach (var q in query)
Console.WriteLine(q.Descripcion);


var query = model.Marca.Where(q => q.ClaveMarca == "005");
foreach (var q in query)
Console.WriteLine(q.Descripcion);


var query = from q in model.Marca
where q.ClaveMarca == "005"
select q.Descripcion;
foreach (var q in query)
Console.WriteLine(q);


var query = model.Marca.Where(q => q.ClaveMarca == "005").Select(q => q.Descripcion).Distinct();
foreach (var q in query)
Console.WriteLine(q);


var query = model.Marca.Where(q => q.ClaveMarca == "005").Distinct();
foreach (var q in query)
Console.WriteLine(q.Descripcion);


var query = model.Marca.FirstOrDefault(q => q.ClaveMarca == "005");
Console.WriteLine(query.Descripcion);


var query = model.Marca.Where(q => q.ClaveEntidad == 0).FirstOrDefault();
Console.WriteLine(query.Descripcion);


var query = (from q in model.Marca
where q.ClaveMarca == "005"
select q).FirstOrDefault() ;
Console.WriteLine(query.Descripcion);


var query = from q in model.Marca
where q.ClaveMarca == "005"
select new {
q.ClaveMarca,
q.Descripcion
};
foreach(var q in query)
Console.WriteLine(q);


var query = from q in model.ClaveVehicularSFA
where q.ClaveEntidad == 85
select new
{
cv = q.ClaveVehicular,
m = q.Marca.Descripcion,
l = q.Linea.Descripcion
};
foreach (var q in query)
Console.WriteLine(q);


var query = from q in model.ClaveVehicularSFA
where q.Marca.Descripcion == "NISSAN"
select new
{
q.ClaveVehicular,
q.Marca.Descripcion
};
foreach (var q in query)
Console.WriteLine(q);


var query = model.ClaveVehicularSFA.Where(q => q.Marca.Descripcion == "NISSAN").Select(q => new { q.ClaveVehicular, q.Marca.Descripcion });
foreach (var q in query)
Console.WriteLine("{0} {1}", q.ClaveVehicular, q.Descripcion);


var query = from q in model.ClaveVehicularSFA.Include("Marca").Include("Linea")
where q.ClaveVehicular == "0040702"
select q;
foreach (var q in query)
Console.WriteLine("{0} {1} {2}", q.ClaveVehicular, q.Marca.Descripcion, q.Linea.Descripcion);


var query = model.ClaveVehicularSFA.Include("Marca").Include("Linea").Where(q => q.ClaveVehicular == "0040702");
foreach (var q in query)
Console.WriteLine("{0} {1} {2}", q.ClaveVehicular, q.Marca.Descripcion, q.Linea.Descripcion);


var query = model.ClaveVehicularSFA.Where(q => q.ClaveVehicular == "0040702");
foreach (var q in query)
{
if (!q.MarcaReference.IsLoaded)
q.MarcaReference.Load();
Console.WriteLine("{0} {1}", q.ClaveVehicular, q.Marca.Descripcion);
}


var query = model.ClaveVehicularSFA.Include("Marca").Include("Linea").Include("Version").Where(q => q.ClaveVehicular == "0040702");
foreach (var q in query)
Console.WriteLine("{0} {1} {2} {3}", q.ClaveVehicular, q.Marca.Descripcion, q.Linea.Descripcion, q.Version.Descripcion);


var query = from q in model.ClaveVehicularSFA
where q.ClaveVehicular == "0040702"
select new
{
cv = q.ClaveVehicular,
m = q.Marca.Descripcion,
l = q.Linea.Descripcion,
v = q.Version.Descripcion
};
foreach (var q in query)
Console.WriteLine("{0} {1} {2} {3}", q.cv, q.m, q.l, q.v);


var query = (from q in model.ClaveVehicularSFA
where q.ClaveVehicular == "0040702"
select new
{
cv = q.ClaveVehicular,
m = q.Marca.Descripcion,
l = q.Linea.Descripcion,
v = q.Version.Descripcion
}).FirstOrDefault();
Console.WriteLine("{0} {1} {2} {3}", query.cv, query.m, query.l, query.v);


var query = from q in model.ClaveVehicularSFA
where q.Marca.ClaveMarca == "004"
select new
{
m = q.Marca.Descripcion,
l = q.Linea.Descripcion,
};
foreach(var q in query)
Console.WriteLine("{0} {1}", q.m, q.l);


var query = from c in model.ClaveVehicularSFA
from t in c.TipoFormato
where c.ClaveVehicular == "0040702" && t.NumeroFormato == 9
select c;
foreach (var q in query)
foreach(var t in q)
Console.WriteLine(t.Descripcion);


var query = from c in model.ClaveVehicularSFA
from t in c.TipoFormato
where c.ClaveVehicular == "0040702" && t.NumeroFormato == 9
select c;
if (query.Count() > 0)
Console.WriteLine("estan relacionadas");
else
Console.WriteLine("No estan relacionadas");


//INSERT
var p = new Puerta();
p.ClaveEstadoEntidad = 1;
p.Comentarios = "";
p.CveEntOficinaCambio = 2;
p.CveEntUsuarioCambio = 2;
p.Descripcion = "Descripcion";
p.FechaCambio = DateTime.Now;
p.IPCambio = "127.0.0.1";
model.AddToPuerta(p);
model.SaveChanges();
var query = from q in model.Puerta
select q;
foreach (var q in query)
Console.WriteLine("{0} {1}", q.ClaveEntidad, q.Descripcion);
var query = (from q in model.Puerta
where q.ClaveEntidad == 50
select q).FirstOrDefault();
//Console.WriteLine("{0} {1}", query.ClaveEntidad, query.Descripcion);


//OBJECT 1
var ph = new PuertaHist();
ph.ClaveEstadoEntidad = 1;
ph.Comentarios = "comentarios";
ph.CveEntOficinaCambio = 2;
ph.CveEntUsuarioCambio = 2;
ph.Descripcion = "ph-descripcion";
ph.FechaCambio = DateTime.Now;
ph.IPCambio = "127.0.0.1";
ph.Puerta = query;
model.AddToPuertaHist(ph);
model.SaveChanges();
var query2 = from q2 in model.PuertaHist.Include("Puerta")
select q2;
foreach(var q2 in query2)
Console.WriteLine("{0} {1} {2}", q2.ClaveEntidad, q2.Descripcion, q2.Puerta.ClaveEntidad);

//OBJECT 2
var ph = new PuertaHist();
ph.ClaveEstadoEntidad = 1;
ph.Comentarios = "comentarios";
ph.CveEntOficinaCambio = 2;
ph.CveEntUsuarioCambio = 2;
ph.Descripcion = "ph-descripcion";
ph.FechaCambio = DateTime.Now;
ph.IPCambio = "127.0.0.1";
ph.PuertaReference.EntityKey = new EntityKey(model.DefaultContainerName + ".Puerta", "ClaveEntidad", (byte)49);

model.AddToPuertaHist(ph);
model.SaveChanges();
var query2 = from q2 in model.PuertaHist.Include("Puerta")
select q2;
foreach (var q2 in query2)
Console.WriteLine("{0} {1} {2}", q2.ClaveEntidad, q2.Descripcion, q2.Puerta.ClaveEntidad);


// OBJECT 1 & 2
var p = new Puerta();
p.ClaveEstadoEntidad = 1;
p.Comentarios = "";
p.CveEntOficinaCambio = 2;
p.CveEntUsuarioCambio = 2;
p.Descripcion = "Descripcion";
p.FechaCambio = DateTime.Now;
p.IPCambio = "127.0.0.1";

var ph = new PuertaHist();
ph.ClaveEstadoEntidad = 1;
ph.Comentarios = "comentarios";
ph.CveEntOficinaCambio = 2;
ph.CveEntUsuarioCambio = 2;
ph.Descripcion = "ph-descripcion";
ph.FechaCambio = DateTime.Now;
ph.IPCambio = "127.0.0.1";
ph.Puerta = p;

model.AddToPuertaHist(ph);
model.SaveChanges();
var query = from q in model.PuertaHist.Include("Puerta")
select q;
foreach (var q in query)
Console.WriteLine("{0} {1} {2} {3}", q.ClaveEntidad, q.Descripcion, q.Puerta.ClaveEntidad, q.Puerta.Descripcion);


//OTRA SINTAXIS
var querypersona = from p in personas
where p.Edad>5
orderby p.Nombre, p.Apellido1, p.Apellido2
select new
{
p.Nombre,
p.Apellido1,
p.Apellido2,
p.Edad,
NombreCompleto =
String.Format("{0} {1} {2}",
p.Nombre,
p.Apellido1,
p.Apellido2)
};


//UPDATE
var query = (from q in model.Puerta
where q.ClaveEntidad == 51
select q).FirstOrDefault();
query.Descripcion = "Nueva descrip.";
model.SaveChanges();
var query2 = (from q2 in model.Puerta
where q2.ClaveEntidad == 51
select q2).FirstOrDefault();
Console.WriteLine(query2.Descripcion);

More examples: 101 Linq Examples

viernes, 20 de mayo de 2011

Convert vs Parse

La diferencia que existe entre Int32.Parse y Convert.Int32 no está en su funcionamiento. Estas clases hacen exactamente lo mismo es el mismo, o sea, transforman un objeto en un Int32, la diferencia estriba en su reacción cuando reciben un valor (null):

En este caso, el Int32.Parse nos devolverá una excepción, notificándonos que está recibiendo un dato (null).

Por otro lado, la clase Convert.Int32 nos convertirá el valor (null) a 0, manejando la excepción.

domingo, 15 de mayo de 2011

The SQL Site Map Provider

MSDN The SQL Site Map Provider

WCF rest services

MSDN A Developer's Guide to...

Lambda Expressions

Desde la primer versión de C# existe la posibilidad de usar delegates, objetos que representan una invocación a un método; en esencia un puntero a función fuertemente tipado. Los delegates son utilizados fundamentalmente como callback en eventos, pero pueden ser pasados a funciones para ser ejecutados por las mismas. De esta forma, permite definir contratos en función a firmas de métodos en lugar de requerir el uso (un poco más fuerte) de interfaces.

La declaración de un delegate define la firma de los métodos que pueden ser aceptados por el mismo. Por ejemplo, un delegate que retorna un entero y recibe un string sería definido así:

delegate int TestDelegate(string param1);

y podría instanciarse de esta manera:

class Test
{
int ParseInt(string param1)
{
return int.Parse(param1);
}
static void Main()
{
Test test = new Test();
TestDelegate del = new TestDelegate(test.ParseInt);
Console.WriteLine(del("12") + 1);
}
}

De ejecutarse, este código mostraría el resultado 13.

En este caso, del mantiene una referencia a test y ParseInt se ejecuta en el contexto de dicha referencia.

Está era la única forma que teníamos disponible con C# 1. Ya en la segunda versión de la especificación, se agregó la alternativa de los anonymous delegates. Usando la nueva sintaxis, aumenta la cantidad de cosas que pueden hacerse con los delegates, ya que no es necesario referir a un método el cual será llamado al invocar el delegate, sino que simplemente se puede definir un bloque de código a ejecutarse.

Para el mismo delegate definido arriba, un ejemplo sería:

class Test2
{
static void Main()
{
TestDelegate del = delegate(string param1) {
return int.Parse(param1);
};
Console.WriteLine(del("12") + 1);
}
}

Una característica interesante de los delegates anónimos, es el hecho de que pueden hacer referencia a cualquier variable del contexto en el que son definidos:

class Test3
{
static void Main()
{
int integer = 0;
TestDelegate del = delegate(string param1) {
return int.Parse(param1) + integer;
};
Console.WriteLine(del("12") + 1);
integer++;
Console.WriteLine(del("12") + 1);
}
}

La salida de este programa es 13 y 14, ya que del hace referencia a la variable integer, la cual es modificada luego de la primer invocación. Lo que se vé aquí, es que el delegate está creando una closure, donde el scope actual define el conjunto de variables que son visibles por el delegate.

Esto le da gran poder a los delegates, ya que les permite ser usados en forma más general y sin escribir demasiado código. Un ejemplo, usando iteración:

int[] integers = { 1, 2, 3};
int sum = 0;
Array.ForEach(integers, delegate(int i) { sum += i; }
Console.WriteLine(sum);

Esto nos lleva a la tercer versión del lenguage C#, donde se definen las lambda expressions, que refinan el concepto de anonymous delegate y utilizan las nuevas capacidades de inferencia de tipos del lenguaje para que la sintaxis sea mucho más limpia. El término lambda expression viene del cálculo lambda, la teoría matemática detrás del desarrollo de los lenguajes funcionales (tema para otro día).

De esta manera, el ejemplo anterior se podría reescribir así:

int[] integers = { 1, 2, 3};
int sum = 0;
Array.ForEach(integers, i => sum += i);
Console.WriteLine(sum);

Básicamente, la nueva sintaxis permite indicar el conjunto de parámetros que recibirá el nuevo delegate, sin necesidad de indicar su tipo (hay algunas excepciones). En el caso de delegates simples que retornan un valor, se puede prescindir de usar el keyword return e indicar simplementa la expresión del resultado.

El tipo de delegate generado por una lambda expressión depende fundamentalmente de cómo se va a aplicar. Veamos un ejemplo:

class Test4
{
delegate void TestDelegate2(int num);
static void Main()
{
Func1(i => i + 1);
Func2(i => i + 1);
}
void Func1(Func f1)
{
Console.WriteLine(f1(1));
}
void Func2(TestDelegate2 f2)
{
Console.WriteLine(f2(1));
}
}

Si bien los tipos de f1 y de f2 son diferentes, viendo que el código definido en la expresión es compatible con el tipo de ambos delegates, el compilador permite que utilicemos el mismo código sin necesidad de hacer nada extraño.

Hay otro feature de las lambda expressions que las hace particularmente interesantes y novedosas con respecto a los delegates. Existe un subconjunto de lambada expressions que pueden ser transformadas automáticamente en árboles de expresión, que pueden ser manipulados por el programador.

Extension Methods

Imaginemos que tenemos una clase que contiene datos de una Persona.

class Person
{
public string Name { get;set; }
public string Surname { get;set; }
}

Si dada una persona queremos obtener el nombre completo utilizando un método, hay varias opciones. Agregar un método a la clase es lo más simple:

public string GetFullName()
{
return Name + " " + Surname;
}

También podemos agregar un método en una clase de utilidades que reciba un objeto de tipo Person y realice el trabajo:

namespace Helpers
{
static class PersonHelper
{
public static string GetFullName(Person person)
{
return person.Name + " " + person.Surname;
}
}
}

Ambas opciones son válidas pero se utilizan en forma diferente:

using Helpers;
class Program
{
public static void Main()
{
var person = new Person { Name = "Juan", Surname = "Pérez" };
Console.WriteLine(person.GetFullName()); // En el caso del método miembro.
Console.WriteLine(PersonHelper.GetFullName(person)); // En el caso del método estático.
}
}

La ventaja de escribir el método miembro es que permite utilizarlo en forma sencilla usando el operador punto, pero sólo es una alternativa viable cuando tenemos acceso al código fuente de nuestros objetos, ya que lo que estamos haciendo es cambiando la definición de nuestra clase. El hecho de definir un método, además, trae aparejada la desventaja de que el código que estamos agregando está fuertemente acoplado con la definición del tipo, ya que podemos ver los campos privados y protegidos del mismo, lo cual no siempre es deseable.

Por otra parte, la definición del método estático, tiene acceso únicamente a las propiedades y métodos expuestos públicamente, lo que fuerza un nivel menor de acoplamiento, además puede definirse para tipos que están fuera de nuestro control, cómo pueden ser los tipos del Framework. La principal desventaja de este método, es que es mucho menos conciso (más verbose), y en caso de querer utilizar más de un método en forma encadenada tendremos cosas así:

StringHelpers.Capitalize(PersonHelper.GetFullName(person)); // Feo de leer.

Con el advenimiento de la tercer versión de C#, se incorporaron al standard los llamados Extension Methods, los cuales permiten usar una sintaxis más amigable para el uso de los métodos estáticos que hemos discutido.

Básicamente, si marcamos el primer parámetro de un método estático con el modificador this, dicho método podrá ser invocado de la misma manera que lo son los miembros de un tipo.

Redefinamos la clase PersonHelper:

namespace Helpers
{
public static class PersonHelper
{
public static string GetFullName(this Person person)
{
return person.Name + " " + person.Surname;
}
}
}

Como podemos ver, la única diferencia con la definición anterior es el hecho de que incorporamos el modificador this. El haber hecho esto, nos permite escribir el siguiente código:

view source
print?

using Helpers;
class Person
{
public string Name { get;set; }
public string Surname { get;set; }
}
class Program
{
public static void Main()
{
var p = new Person { Name = "José", Surname = "Gómez" };
Console.WriteLine(p.GetFullName()); // Se invoca cómo un método de instancia.
Console.WriteLine(PersonHelper.GetFullName(p)); // Se puede seguir invocando como método estático.
}
}

El código generado por el compilador es idéntico al de una invocación de un método estático normal, la única diferencia es el agregado (automático) del atributo ExtensionAttribute en la definición del método.

Otro ejemplo:

var fn1 = StringHelpers.Capitalize(PersonHelper.GetFullName(person));
var fn2 = person.GetFullName().Capitalize();
Console.WriteLine(fn1 == fn2); // Retorna true.

Para poder invocar un extension method, primero es necesario que su definición entre en el scope de resolución del archivo actual, para lo que se utiliza el keyword using junto con el namespace en el que está definido el extension method. Aún así, de existir un miembro con la misma firma en el tipo “extendido” dicho método tiene preferencia.

Si bien en esencia es un cambio menor, los extension methods mejoran mucho la legibilidad del código y habilitan la existencia de algo de gran importancia, LINQ, que será el tema de mi próximo post.

Partial classes en C#

Si estamos escribiendo clases utilizando un generador de código, muchas veces se nos presenta la necesidad de de modificar algunos aspectos de las clases generadas. Si bien es posible modificar el código generado, dicha práctica no es recomendable, ya que cualquier cambio que realicemos sería reemplazado en el momento en que regeneremos el código.

Es ahí donde entra el uso del keyword partial. Cuando definimos una clase como partial (e.g. partial class Test), estamos indicando al compilador de C#, que la definición de la misma se encuentra en múltiples archivos.

Esto nos permite generar el código de nuestra clase en un archivo (e.g. Test.g.cs) y permitir al usuario agregar métodos, propiedades o campos en otro archivo (e.g. Test.cs). De esta manera, cuando el generador cambie, o cuando cambiemos algún parámetro de generación, la parte que fue personalizada por el usuario de nuestro generador permanece incambiada.

Veamos un ejemplo sencillo. Generaremos clases con una propiedad con el mismo nombre de la clase y el sufijo ‘Member’ usando T4 (Text Template Transformation Toolkit) disponible en Visual Studio 2008

[Test.g.tt:]

&lt;#@ template language=&quot;C#&quot; #&gt;
&lt;#
string[] names = { &quot;Class1&quot;, &quot;Class2&quot; };
foreach(string name in names) {
#&gt;
public partial class &lt;#=name#&gt;
{
public int &lt;#=name#&gt;Member { get;set; }
}
&lt;#
}
#&gt;

Si todo fue bien, debajo de Test.g.tt deberíamos tener un archivo llamado Test.g.cs.

[Test.g.cs:]

public partial class Class1
{
public int Class1Member { get;set; }
}
public partial class Class2
{
public int Class2Member { get;set; }
}

Ahora supongamos que queremos que la clase Class1 tenga un miembro llamado NewMember. Podemos crear un archivo llamado Class1.cs.

[Class1.cs:]

partial class Class1
{
public string NewMember { get;set; }
}

Con esto, Class1 tiene ahora dos miembros y Class2 tiene uno solo. Si cambiamos Test.g.tt, el archivo Test.g.cs será reemplazado, pero nuestro código existente en Class1.cs no se verá afectado.

Partial Methods en C#

En el post anterior, vimos como habilitar la modificación de la estructura de una clase generada (o interfaz, o estructura) utilizando el keyword partial. De esta manera, podemos agregar nuevos métodos, campos, propiedades y eventos a una clase definida en otros archivos. Siguiendo con el tema, voy a presentar otro uso, que nos permite definir métodos opcionales.

Los partial methods funcionan en forma similar a los events, o a los delegates, pero sin tener costo en tiempo de ejecución en caso de no recibir una implementación, ya que el compilador elimina todas las referencias existentes al método en caso de no recibir una implementación.

Un partial method consta de dos partes: la definición, donde se indica el nombre y los parámetros que recibe el método (siempre retorna void); y la implementación, donde se repiten los mismos datos, pero se incluye un cuerpo que implementa el método.

Veamos un ejemplo. Supongamos que tenemos un generador que produce el siguiente código:

partial class Person
{
string name;
string surname;
public string Name
{
get { return name; }
set { name = value; }
}
public string Surname
{
get { return surname; }
set { surname = value; }
}
}

Con esta clase así generada, es difícil permitir a nuestros usuarios saber cuando cambió el nombre o el apellido de nuestra persona. Una posibilidad que tenemos es la de cambiar el generador para que agregue un evento por cada propiedad:

partial class Person
{
string name;
string surname;
public event Action<string> NameChanged = delegate {};
public event Action<string> SurnameChanged = delegate {};
public string Name
{
get { return name; }
set { name = value; NameChanged(name);}
}
public string Surname
{
get { return surname; }
set { surname = value; SurnameChanged(name); }
}
}

Esta versión es correcta, pero en el caso en que no requiramos monitorear todas las propiedades, hemos agregado eventos en forma innecesaria. Además, es una solución demasiado flexible, ya que puede haber múltiples suscriptores al evento, lo cual puede ser un problema.

La alternativa con partial methods sería esta:

partial class Person
{
string name;
string surname;
partial void NameChanged(string name);
partial void SurnameChanged(string surname);
public string Name
{
get { return name; }
set { name = value; NameChanged(name); }
}
public string Surname
{
get { return surname; }
set { surname = value; SurnameChanged(name); }
}
}

Si queremos saber cuando cambió la propiedad Name, podemos definir en otro archivo:

partial class Person
{
partial void NameChanged(string name)
{
Console.WriteLine("Name changed: '{0}'", name);
}
}

En este caso, la implementación de Person, tiene dos propiedades, Name y Surname, y únicamente un método, NameChanged. Cómo no definimos una implementación para SurnameChanged, el compilador omite todas las llamadas al método y lo elimina de la clase.

La generación de código es algo muy útil, que nos permite automatizar varias tareas mundanas, lo que mejora nuestra productividad. El uso de partial classes y partial methods nos permite mejorar las posibilidades de reúso de nuestros generadores.

Difference between Abstract Class & Interfase

Las clases abstractas pueden tener o no implementaciones de los métodos miembros de la clase, y las interfaces no tienen ninguna implementación solo las declaraciones de los miembros que deben constituir la clase que implemente dicha interfaz.

Los miembros de la interfaz deben ser públicos y sin implementación.

interface iMiInterfaz
{
int Sumar(int Num1, int Num2);
int Multiplicar(int Num1, int Num2);
}

Las clases abstractas pueden tener miembros abstractos y no abstractos (es decir sobrecargables en las clases que hereden de ella). pero en las interfaces todos los miembros son implicitamente abstractos, y todos los miembros de la interfaz deben ser reimplementados en la clase que derive de esta.

abstract class MiClaseAbstracta
{
public int Suma(int Num1, int Num2) { return Num1 + Num2; }
public abstract int Multiplicacion(int Num1, int Num2);
}

sábado, 30 de abril de 2011

Basic Membership Authentication

Ejemplo de como implementarlo

Crear un nuevo proyecto en Visual Studio >
File / New website /
click derecho > project / Add new Folder / [content]
click derecho > folder content / Add new web form / Default.aspx
Seleccione esta página como StartPage.
click derecho > project / Add new web form / Login.aspx

Eliminar la página Default.aspx del directorio raiz del project








Crear nuevo usuario y roles

En ASP.NET Web Site Administration Tool >
Security / Use the security Setup Wizard to configure security step by step./
[x] from the internet
[x] Enable roles for this Web site
Create New Role: [admin] Add role
user name: [usuario] [password*] [...] Create user
[x] active user
Continue NEXT>

Ahora para restringir el acceso no autorizado a los contenidos configurar los roles así



Agregar este contenido al web form Login.aspx



y se verá así



Agregar este contenido al web form Default.aspx



y se verá así

System.Diagnostics, Debug Class & Trace Class

MSDN Debug
MSDN Trace
MSDN System.Diagnostics

C# Programming Guide

MSDN C# Programming Guide

viernes, 29 de abril de 2011

Threading Tutorial

MSDN System.Threadings (MSDN Monitor Class) (MSDN ThreadPool Class) (MSDN Thread Class)
MSDN Threading Tutorial

Covariance and Contravariance in Generics


MSDN Covariance and Contravariance in Generics

Variance in Delegates


MSDN Variance in Delegates

Diferencia entre los parámetros PARAM, OUT y REF

PARAM es un argumento de parámetro de un método que toma un número variable de argumentos.

public class MyClass
{
public static void UseParams(params int[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}

public static void UseParams2(params object[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}

static void Main()
{
UseParams(1, 2, 3, 4);
UseParams2(1, 'a', "test");

UseParams2();

int[] myIntArray = { 5, 6, 7, 8, 9 };
UseParams(myIntArray);

object[] myObjArray = { 2, 'b', "test", "again" };
UseParams2(myObjArray);


// The following call causes a compiler error because the object
// array cannot be converted into an integer array.
//UseParams(myObjArray);

// The following call does not cause an error, but the entire
// integer array becomes the first element of the params array.
UseParams2(myIntArray);
}
}

/*
Output:
1 2 3 4
1 a test

5 6 7 8 9
2 b test again
System.Int32[]
*/

OUT y REF son parámetros de métodos que hacen referencia al parámetro pasado como argumento.

OUT no tiene valor al ser declarado como parámetro, sino que el método tiene que asignarle un valor antes de ser devuelto. En caso contrario dará error. Este argumento de parámetro es muy útil cuando se necesita un método que devuelva varios valores.

REF necesita tener valor al ser declarado como parámetro, el método, al ser invocado puede modificar o no, este valor del parámetro.

* Es un error sobrecargar métodos con la única diferencia de REF o OUT. Otro error es pasar una propiedad de la clase (como si fuera una variable) como argumento out de un método.

//valid
class MyClass
{
public void MyMethod(int i) {i = 10;}
public void MyMethod(out int i) {i = 10;}
}

public static void MyMethod(out int[] arr)
{
arr = new int[10];
}

public static void MyMethod(ref int[] arr)
{
arr = new int[10];
}

//invalid
class MyClass
{
public void MyMethod(ref int i) {i = 10;}
public void MyMethod(out int i) {i = 10;}
}



MSDN out
MSDN ref
MSDN params
MSDN Method Parameters