Cuando la gente escribre sin saber lo que dice…

Leo textualmente en El País (Y copio y pego porque suelen cambiar una noticia a lo largo del día entre 4 y 5 veces):

Windows 7, una beta RC para olvidar Vista

El 5 de mayo se podrá descargar la versión de pruebas la Release Candidate del sistema operativo (A día de hoy se puede descargar la RC para suscriptores a MSDN y Technet 🙂 )

http://www.elpais.com/articulo/tecnologia/Windows/beta/olvidar/Vista/elpeputec/20090430elpeputec_1/Tes

Salu2

[REST WCF] Haciendo nuestros servicios RESTful – 201 (Created)

Cuando un servicio se diseña basandose en los principios de REST se denominan RESTful y uno de estos principios es que cuando se crea un recurso el servicio debe responder con un código 201 (Created) además de retornar la Url del recurso creado.

Si creamos un servicio REST con WCF y creamos un recurso, esta es la respuesta por defecto que nos dá el servicio:

rest201

rest201_2

 

Como podéis observar, el servicio nos retorna un código 200 (OK) y no es que este mal, pero siguiendo el estilo o los principios de REST debe comportase como se citó anteriormente.

Para modificar las cabeceras HTTP, disponemos una clase WebOperationContext, que es una wrapper con el que vamos a poder modificar las cabeceras HTTP:

Bb515723.pubproperty(es-es,VS.90).gifBb515723.static(es-es,VS.90).gif
Current
Obtiene el contexto de operación web actual.

Usar en Cliente:

Bb515723.pubproperty(es-es,VS.90).gif
IncomingResponse
Obtiene el contexto de respuesta web para la solicitud que se está recibiendo.

Bb515723.pubproperty(es-es,VS.90).gif
OutgoingRequest
Obtiene el contexto de solicitud web para la solicitud que se está enviando.

Usar en Servidor:

Bb515723.pubproperty(es-es,VS.90).gif
IncomingRequest
Obtiene el contexto de solicitud web para la solicitud que se está recibiendo

Bb515723.pubproperty(es-es,VS.90).gif
OutgoingResponse
Obtiene el contexto de respuesta web para la respuesta que se está enviando.

 

En nuestro caso vamos a utilizar Current, para acceder al contexto y OutgoingResponse, para obtener el contexto de respuesta y modificar la cabecera HTTP:

/// <summary>
/// Adds the product.
/// </summary>
/// <param name="product">The product.</param>
/// <returns></returns>
public Product AddProduct(Product product)
{
    product.Id = Guid.NewGuid().ToString();
    product.LastModified = DateTime.Now;
    _products.Add(product);
 
    UriTemplate ut = new UriTemplate("/products/{id}");
    Uri baseUri = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.BaseUri;
    Uri ret = ut.BindByPosition(baseUri, product.Id);
 
    OutgoingWebResponseContext owrc = WebOperationContext.Current.OutgoingResponse;
    owrc.SetStatusAsCreated(ret);
 
    return product;
}

 

Con este código conseguimos nuestro proposito, retornar el 201 (Created) y la Url del recurso creado:

rest201_3

 

Esto es todo!!!

[REST WCF] – Conditional GET – Cuidado con la comprobación de If-Modified-Since

Leyendo la entrada de Unai http://geeks.ms/blogs/unai/archive/2009/04/21/wcf-rest-conditional-get-save-bandwidth.aspx (Muy recomendable para la gente que quiera aprender REST) tengo que comentar algo acerca de la comprobación de If-Modified-Since en el condicional GET.

Aclaro desde ya, que todo lo que voy a comentar en esta entrada está en el libro de la persona que para mí más sabe de RESTful .NET del mundo, Jon Flanders, sí quieres aprender de verdad no dejes de visitar su blog y leer su libro RESTful .NET

Al tajo:

El problema esta en el valor date/time que se envía en la cabecera Last-Modified y la perdida de precisión que se produce, de tan sólo un segundo. Como dice Unai, vamos a verlo en código, que todo se explica mucho mejor:

[DataContract(Name = "product", Namespace = "")]
public class Product
{
    [DataMember(Name = "id", Order = 1)]
    public string Id;
    [DataMember(Name = "description", Order = 2)]
    public string Description;
    [DataMember(Name = "price", Order = 3)]
    public decimal Price;
    [DataMember(Name = "lastModified", Order = 4)]
    public DateTime LastModified;
}
/// <summary>
/// Gets the product by id.
/// </summary>
/// <param name="id">The id.</param>
/// <returns></returns>
public Product GetProductById(string id)
{
    var product = _products.SingleOrDefault(p => p.Id == id);
 
    if (CheckLastModified(product))
    {
        return null;
    }
 
    if (product == null)
    {
        OutgoingWebResponseContext ctx = WebOperationContext.Current.OutgoingResponse;
        ctx.SetStatusAsNotFound();
        ctx.SuppressEntityBody = true;
    }
 
    SetLastModified(product);
 
    return product;
}

 

/// <summary>
/// Checks the last modified.
/// </summary>
/// <param name="p">The p.</param>
/// <returns>bool</returns>
private bool CheckLastModified(Product p)
{
    IncomingWebRequestContext ctx = WebOperationContext.Current.IncomingRequest;
 
    string lastModified =
        ctx.Headers[HttpRequestHeader.IfModifiedSince];
 
    if (lastModified != null)
    {
        DateTime dt = DateTime.Parse(lastModified);
        if (p.LastModified == dt)
        {
            SetNotModified();
            return true;
        }
    }
    return false;
}

 

/// <summary>
/// Sets the last modified.
/// </summary>
/// <param name="p">The p.</param>
private void SetLastModified(Product p)
{
    OutgoingWebResponseContext ctx =
        WebOperationContext.Current.OutgoingResponse;
    ctx.LastModified = p.LastModified;
}

 

Consultamos un producto:

rest1

Nos devuelve un 200 y la cabecera Last-Modified viene informada.

rest2

Recuperamos otra vez el mismo producto, pero ahora le informamos el If-Modified-Since y…

rest3

Ehhh??? Un 200??? Si estabamos esperando un 304???

Pues ahí está el problema del que habla Jon, la perdida de precisión en Last-Modified. Así que vamos a modificar el código de nuestro servicio para que nos devuelva un 304:

Lo primero cambiamos el tipo de dato de la propiedad LastModified a DateTimeOffset

[DataContract(Name = "product", Namespace = "")]
public class Product
{
    [DataMember(Name = "id", Order = 1)]
    public string Id;
    [DataMember(Name = "description", Order = 2)]
    public string Description;
    [DataMember(Name = "price", Order = 3)]
    public decimal Price;
    [DataMember(Name = "lastModified", Order = 4)]
    public DateTimeOffset LastModified;
}

 

Modificamos la función CheckLastModified:

/// <summary>
/// Checks the last modified.
/// </summary>
/// <param name="p">The p.</param>
/// <returns>bool</returns>
private bool CheckLastModified(Product p)
{
    IncomingWebRequestContext ctx = WebOperationContext.Current.IncomingRequest;
 
    string lastModified =
        ctx.Headers[HttpRequestHeader.IfModifiedSince];
 
    if (lastModified != null)
    {
        DateTimeOffset dt = DateTimeOffset.Parse(lastModified);
        if (InternalDateTimeCompare(p.LastModified.UtcDateTime, dt))
        {
            SetNotModified();
            return true;
        }
    }
    return false;
}

Modificamos también SetLastModified:

/// <summary>
/// Sets the last modified.
/// </summary>
/// <param name="p">The p.</param>
private void SetLastModified(Product p)
{
    OutgoingWebResponseContext ctx =
        WebOperationContext.Current.OutgoingResponse;
    ctx.LastModified = p.LastModified.DateTime;
}

Y por último añadimos la siguiente función que comparará las fechas al nivel de precisión necesario para poder enviar ese 304 que tanto deseamos:

/// <summary>
/// Internals the date time compare.
/// </summary>
/// <param name="dateTime">The date time.</param>
/// <param name="dt">The dt.</param>
/// <returns>bool</returns>
private bool InternalDateTimeCompare(DateTime dateTime, DateTimeOffset dt)
{
    DateTime nd1 =
        new DateTime(dateTime.Year, dateTime.Month,
            dateTime.Day, dateTime.Hour,
            dateTime.Minute, dateTime.Second);
    DateTime nd2 =
        new DateTime(dt.Year, dt.Month,
            dt.Day, dt.Hour,
            dt.Minute, dt.Second);
    return nd1 == nd2;
}

Sí probamos nuestro código ahora:

rest4

Ahora sí nos muestra el 304 indicandonos que el recurso no ha sido modificado:

rest5

Y en el body, no viene nada:

rest6

A parte de esta comprobación, tendríamos que utilizar la cabecera HTTP ETag par lograr un buen conditional GET

Por cierto, aquí tenéis el enlace al vídeo del MIX 09 en el Jon habla acerca de la caché en REST y WCF:

http://videos.visitmix.com/MIX09/T64M

Pues esto es todo, espero que os sirva como a mí y no os de quebraderos de cabeza 🙂

Thanks Jon!!!

The custom tool ‘MSLinqToSQLGenerator’ failed. Unspecified error

Modificando un diagrama de Clases de LINQ to SQL me he encontrado a la hora de regenerarlo el error del título del post:

The custom tool ‘MSLinqToSQLGenerator’ failed. Unspecified error

Por lo visto se debe al SP1 de VS 2008, cuando añades una clase parcial sobre el objeto de contexto e intentas usar la herramienta MSLinqToSQLGenerator.

Para solucionarlo, basta con poner los using de los namespaces de la clase parcial dentro de la declaración del namespace principal:

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=361577

Salu2