jueves, 29 de enero de 2009

ADO.NET Entity Framework: ¿Cómo actualizar un objeto desconectado?

Un escenario común es el siguiente:

"Al enviar un formulario web, creo una entidad Pedido con los datos proporcionados por el usuario y lo entrego a mi capa de negocios o servicios para que se valide y persista en el almacén de datos."

En este escenario la entidad Pedido enviada desde la página web (o enviada desde un Web Service), se encuentra en estado Deattached. Para que podamos persistir sus cambios necesitamos asociarla a un contexto y luego llamar al método SaveChanges().

Si la entidad aún no existe en el almacén de datos, debemos agregarla. Esto es muy simple, usando los métodos AddTo* generados en el modelo:

context.AddToOrders(order);
context.SaveChanges();

En cambio si la entidad ya existe en el almacén de datos, entonces debemos asociarla al contexto y guardar los cambios. Esta tarea parece ser simple y podríamos intentar resolverla de la siguiente manera:

context.AttachTo("Orders", order);
context.SaveChanges();

Sin embargo, esto no funciona, pues el objeto order se asocia exitosamente al contexto pero en el estado Unchanged. Luego el método SaveChanges() no persiste ningún cambio.

La forma de resolver este problema es recuperando la entidad original desde el almacén de datos, aplicar los cambios, y volver a guardarla.

Buscando en la web encontré diferentes enfoques para resolver este problema. Muchos utilizan Reflection para recorrer todas las propiedades y asignar las que han cambiado. La que más me ha interesado la encontré en el blog de Cesar de la Torre.

Se trata de un extension method llamado AttachUpdated() aplicado al ObjectContext. Me he tomado el atrevimiento de renombrar le método AttacheUpdated() por UpdateObject(), pues me pareció más consistente con otros métodos que ya existen en el ObjectContext como AddObject() y DeleteObject(). El código es el siguiente:

public static void UpdateObject(this ObjectContext context, string entitySetName, EntityObject entity)
{
    var key = context.CreateEntityKey(entitySetName, entity);

    object originalEntity;
    if (context.TryGetObjectByKey(key, out originalEntity))
    {
        context.ApplyPropertyChanges(entitySetName, entity);
        context.ApplyReferencePropertyChanges(entity, (IEntityWithRelationships)originalEntity);
    }
    else
    {
        throw new ObjectNotFoundException();
    }
}

Este método primero crea la EntityKey asociada al objeto usando el método CreateEntityKey() y luego intenta recuperar el objeto desde el almacén de datos usando el método TryGetObjectByKey(). A continuación aplica los cambios usando el método ApplyPropertyChanges() y luego llama al método ApplyReferencePropertyChanges() que se encarga de aplicar los cambios a las entidades relacionadas. Este último método se trata de otro extension method:

private static void ApplyReferencePropertyChanges(this ObjectContext context, IEntityWithRelationships newEntity, IEntityWithRelationships oldEntity)
{
    foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds())
    {
        var oldRef = relatedEnd as EntityReference;

        if (oldRef != null)
        {
            var newRef = newEntity.RelationshipManager.GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName) as EntityReference;
            if (newRef != null) oldRef.EntityKey = newRef.EntityKey;
        }
    }
}

Aquí no solo se resuelve el problema de aplicar los cambios a las propiedades del objeto en cuestión sino también los cambios en sus objetos relacionados.

Luego de crear estos extensions methods, el código final para actualizar una entidad desconectada quedaría como:

context.UpdateObject("Orders", order); //Extension method
context.SaveChanges();

He probado esto y funcionó para mí. Espero que también les sea de utilidad.

Saludos, Gus

miércoles, 28 de enero de 2009

ADO.NET Entity Framework: Nuevos proveedores de datos

En el blog del equipo de ADO.NET Entity Framework se anuncia que están disponibles nuevos proveedores para ADO.NET EF, incluyendo versiones Beta.

Pueden leer más y descargar estos providers desde el siguiente link:
http://msdn.microsoft.com/en-us/data/dd363565.aspx

image

Saludos, Gus

ADO.NET Entity Framework: ¿Cómo cambiar la ForeignKey de un objeto?

Tengo un formulario de edición de un Producto, y entre muchos otros datos, se debe seleccionar, por ejemplo, la Categoría desde un DropDownList. Al guardar el producto, se debe establecer el ID de la categoría asociada.

La intuición nos llevaría a escribir el siguiente código:

product.Category.Id = Convert.ToInt32(CategoryDropDownList.SelectedValue);

Desafortunadamente, esto provocará un error, porque la propiedad Category es null, o bien porque no se puede cambiar la propiedad Id.

Para resolver el problema podemos escribir lo siguiente:

var categoryId = Convert.ToInt32(CategoryDropDownList.SelectedValue);
product.CategoryReference.EntityKey = new EntityKey("ContosoEntities.Categories", "Id", categoryId);

En lugar de la propiedad Category estamos usando la propiedad CategoryReference y estamos asignando una nueva EntityKey. Esto permitirá que cuando ejecutemos el método SaveChanges, los cambios de la categoría asociada al producto se persistan en el almacén de datos.

Particularmente esta forma de resolver el problema me parece poco intuitiva y elegante. Algo tan simple como cambiar una clave externa implica escribir todo ese código. Sin embargo, hasta ahora, parece ser la única forma de resolver el problema.

Si nos interesa mejorar un poco nuestro código, y escribir algo más simple, podríamos crear una clase Partial de Product, y agregar una propiedad CategoryId:

public int CategoryId
{
   get
   {
       return Convert.ToInt32(CategoryReference.EntityKey.EntityKeyValues[0].Value);
   }

   set
   {
       var qualifiedEntitySet = "ContosoEntities.Categories";
       CategoryReference.EntityKey = new EntityKey(qualifiedEntitySet, "Id", value);
   }
}

Esta propiedad adicional, permitirá escribir algo como:

product.CategoryId = Convert.ToInt32(CategoryDropDownList.SelectedValue);

Muy similar a los que habíamos intentado al principio!

Antes de terminar, alguien podrá decir:

"Usando LINQ TO SQL, estas propiedades se generan automáticamente cuando se crea el modelo."

Es cierto! Pueden dirigir sus comentarios y sugerencias al Blog del Equipo de ADO.NET Entity Framework, y quizás tengamos suerte para la siguiente versión.

Hasta la próxima! Espero que sirva.
Saludos, Gus

ASP.NET Performance Tip: Eliminar HttpModules innecesarios

En cada solicitud de ASP.NET se ejecutan una serie de módulos para completar diferentes tareas. Por ejemplo, el módulo SessionStateModule intercepta cada solicitud, recupera la cookie de sesión  y carga los datos de sesión en el HttpContext. No siempre son necesarios todos los módulos, por ejemplo, si estás usando FormsAuthentication entonces quizás no necesites los módulos de WindowsAuthentication o PassportAuthenticacion.

De forma predeterminada, el archivo machine.config define los siguientes módulos:

<httpModules>

<add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />

<add name="WindowsAuthentication"
type="System.Web.Security.WindowsAuthenticationModule" />

<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />

<add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />

<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />

<add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

</httpModules>

Los módulos que no utilizamos en nuestra aplicación ASP.NET, podemos removerlos desde el web.config:

<httpModules>
   <remove name="WindowsAuthentication" />
   <remove name="PassportAuthentication" />
   <remove name="AnonymousIdentification" />
   <remove name="UrlAuthorization" />
   <remove name="FileAuthorization" />
</httpModules>

Esto reducirá carga de trabajo innecesaria en cada solicitud ASP.NET, y nuestras aplicaciones estarán un poco más optimizadas.

Pueden encontrar este y otros tips de performance en el siguiente enlace:
http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspx

En futuros posts, continuaré sumando más tips de performance, como Compresión Http, Optimización del ViewState, Combinación de Scripts y Css, etc. Incluiré código fuente y herramientas para hacer que nuestras aplicaciones ASP.NET se ejecuten cada vez más rápido.

Espero que sirva.
Saludos, Gus

lunes, 26 de enero de 2009

Silverlight y PhotoSynth en la presidencia de Obama

Como lo dice Scott G. en su blog:

"El día de la toma de posesión de Barack Obama fue un evento histórico. Silverlight se ha usado como la tecnología que ha permitido asistir a tal evento online."

Me resultó interesante el trabajo armado con PhotoSynth que nos permite tener una vista 3D del acto de inauguración a partir de fotos 2D tomadas por miles de personas que asistieron al evento. Pueden apreciarlo en el siguiente enlace:
http://edition.cnn.com/SPECIALS/2009/44.president/inauguration/themoment/ 

image
Enjoy It!
Gus

ADO.NET Entity Framework: ¿Cómo incluir los objetos relacionados?

Una tarea común en el desarrollo con ADO.NET Entity Framework es escribir una consulta que incluya una o más entidades relacionadas. Por ejemplo, quiero obtener todas las fotos de un autor incluyendo los datos del autor:

var photos = from p in db.Photos.Include("Author")
             where (p.AuthorId == authorId)
             select p;

El método Include especifica que se incluya la entidad Author asociada a cada Photo.

Si además del autor, quisiera incluir otra entidad relacionada a la foto como su Categoría, podría agregar otro include:

var photos = from p in db.Photos.Include("Author").Include("Category")
             where (p.AuthorId == authorId)
             select p;

En este caso se incluirá también la categoría asociada a cada Photo.

Otro método que también permite cargar entidades relacionadas es el método Load(). Supongamos que deseo obtener los datos de una foto y sus palabras claves asociadas, podría escribir algo como:

var photo = db.Photos.First(p => p.Id == photoId);
photo.Tags.Load();

En otro escenario, podría interesarme verificar primero, antes de ejecutar el método Load():

var photo = service.GetPhoto(id);
if (!photo.Tags.IsLoaded) photo.Tags.Load();

La propiedad IsLoaded permite comprobar si se han cargado o no los objetos relacionados en la colección de Tags.

Para simplificar este trabajo, he creado un extension method EnsureLoad() que aplica a EntityCollection, así el código anterior se reduce a:

var photo = service.GetPhoto(id);
photo.Tags.EnsureLoad();

El código del extension method es el siguiente:

public static void EnsureLoad<T>(this EntityCollection<T> entityCollection)
where T: EntityObject
{
    if (!entityCollection.IsLoaded)
    {
        entityCollection.Load();
    }
}

Bueno, espero que sirva.
Saludos, Gus

Session Timeout vs FormsAuthentication Timeout

Muchas veces me preguntan: ¿Cómo hago para aumentar el tiempo de sesión en una aplicación ASP.NET, y evitar que los usuarios vuelvan a la página de login luego de cierto período de inactividad?. Que ya han probado seteando el session timeout en el web.config, setarlo por código, etc. y ASP.NET simplemente ignora esos valores.

Se trata de un tema que ya tiene unos años, pero aún es bastante recurrente y muchas veces confuso. Por ello, he decido postear un poco al respecto.

Para comprenderlo mejor hay que considerar al menos 2 timeout independientes:

  1. Session Timeout
  2. Forms Authentication Timeout

Session Timeout define cuanto tiempo debe durar la cookie de sesión en el cliente. Cada vez que el usuario navega en el sitio se guarda una cookie de sesión, para identificar las solicitudes del cliente. Esto es independiente de si el usuario se autenticó o no. El valor predeterminado es de 20 minutos, pero puede modificarse con el elemento system.web/sessionState en el web.config:

    <sessionState mode="InProc" timeout="60" />

Forms Authentication Timeout define cuanto tiempo debe durar la cookie de autenticación en el cliente. Expirada la cookie de authenticación el usuario deberá autenticarse nuevamente. El valor predeterminado es de 30 minutos, pero puede modificarse con el elemento system.web/authentication en el web.config:

<authentication mode="Forms">
      <forms loginUrl="~/Security/Login.aspx" timeout="60" />
</authentication>

Siempre complican estas cosas porque uno confunde la sesión con la autenticación. Son dos cosas independientes. Uno puede crear un sitio basado en Forms Authentication sin usar variables de Session, o viceversa, o combinando ambos (lo más común).

Normalmente, no hay problemas con los valores predeterminados de session y authentication, pero en aplicaciones donde el usuario puede pasarse bastante tiempo leyendo o escribiendo hasta finalmente presionar un link o botón, estos valores predeterminados pueden expirar y entonces necesitaremos aumentarlos.

Finalmente, otro timeout que a veces se quiere ajustar es el executionTimeout del elemento HttpRuntine. Este atributo define el tiempo máximo que puede durar la ejecución de una solicitud. El valor predeterminado es 90 segundos, tiempo suficiente para la mayoría de las aplicaciones. Sin embargo, existen escenarios donde se necesita un tiempo mayor, por ejemplo, para subir archivos de gran tamaño. En estos escenarios, seguramente también habrá que aumentar el atributo maxRequestLength. Pueden leer más sobre el tema en Carga de archivos ASP.NET.

Ahora que hay un poco más de luz en el asunto, pueden continuar investigando y profundizar más sobre estos temas.
Espero que sirva.

Saludos, Gus.

martes, 20 de enero de 2009

Control Extensions

Continuando con los mini-aportes sobre extensions methods, en esta oportunidad quiero compartir algunos que simplifican la carga de datos en controles de lista como DropDownList, CheckBoxList o RadioButtonList.

El código habitual para llenar una lista desplegable con datos obtenidos desde algún origen de datos es el siguiente:

var countries = GlobalServices.GetCountries();
CountryDropDownList.DataValueField = "Id";
CountryDropDownList.DataTextField = "Name";
CountryDropDownList.DataSource = countries;
CountryDropDownList.DataBind();

Habitualmente solemos agregar un item más al principio que diga, por ejemplo: (Seleccione). Entonces, al código anterior podemos agregar:

CountryDropDownList.Items.Insert(0, new ListItem("(Seleccione)", String.Empty)); 

Si bien esto no es muy complejo, se vuelve tedioso cuando hay que repetirlo para llenar otras listas adicionales. Un simple extension method puede reducir todo esto a solo 1 línea de código:

CountryDropDownList.Fill(countries, "Id", "Name");

Si quisieramos agregar el item adicional de (Seleccione), podríamos usar una sobrecarga de este método:

CountryDropDownList.Fill(countries, "Id", "Name", "(Seleccione)");

El código del extension method Fill() es muy simple:

public static void Fill(this ListControl list, object dataSource, string valueField, string textField) 
{
if (list == null) return;

list.DataValueField = valueField;
list.DataTextField = textField;
list.DataSource = dataSource;
list.DataBind();
}

Como se puede observar, el extension method se aplica a los controles de tipo ListControl, esto incluye, además del control DropDownList, los controles CheckBoxList, RadioButtonList, BulletedList, y cualquier otro control que herede de ListControl.

Es un pequeño aporte que puede reducir, de una forma sencilla y elegante, una cantidad considerable de código y el consecuente ahorro de tiempo.

Pueden descargar la clase ControlExtensions.cs con estos métodos desde el siguiente link. Más adelante intentaré agregar nuevos métodos aplicables a otros controles.

Espero que sirva.

Saludos, Gus

lunes, 15 de diciembre de 2008

Propiedades y métodos públicos en la MasterPage

Este truco ya es un poco viejo, sin embargo, aún puede ser útil para muchos.

En muchas situaciones deseamos exponer propiedades y métodos públicos en la página maestra o página principal para accederlas desde las páginas de contenido. Sin embargo, al hacerlo vemos que no son accesibles. No aparecen en la lista de Intellisense.

Existe una directiva poco conocida llamada MasterType que podemos agregar fácilmente en nuestras páginas de contenido y así poder acceder a las propiedades y métodos públicos definidos en nuestra página maestra.

image

Una vez definida esta directiva podemos acceder en el código por detrás de nuestras páginas de contenido, a las propiedades y métodos públicos de la MasterPage.

image

Enjoy it!

¿Cómo recuperar el objeto DataItem del control ListView?

Cada elemento o fila de los controles de datos como GridView, DataList o Repeater disponen de una propiedad DataItem que permite recuperar el registro de datos enlazado a la lista. Esta propiedad es muy útil para procesar los elementos y controles durante el evento de enlace de datos.

Sin embargo, al trabajar con el control ListView incluído en ASP.NET 3.5 habrán notado que no existe una propiedad similar o equivalente para recuperar los datos del ítem enlazado para procesarlos durante el evento ItemDataBound.

Investigando un poco sobre cómo resolver esta ausencia, encontré una forma poco intuitiva:

image

El truco es que el elemento e.Item de tipo ListViewItem se puede castear a ListViewDataItem y entonces ahí podemos acceder a la propiedad DataItem para luego castearlo nuevamente a la clase de datos que estamos enlazando.

Este doble casting no me agrada mucho, pero es la única forma que encontré para recuperar el elemento de datos que está siendo enlazado al control ListView.

Bueno, espero que sirva y ahorre algunos dolores de cabeza.

Saludos, Gus

domingo, 30 de noviembre de 2008

String Extensions

Como sabemos, los Extensions Methods son una de las nuevas características de C# 3.0. Podemos verlos como métodos estáticos que permiten agregar nuevas funcionalidades a clases ya existentes. No es mi intención explicar cómo crear Extensions Methods (pueden aprender más aquí y aquí), sino compartir varios extensions methods que uso habitualmente en mis proyectos de desarrollo.

Convirtiendo Strings

Convertir cadenas de texto a un número o fecha es una necesidad habitual en todos los proyectos. El código típico es el siguiente:

int number = int.Parse(NumberTextBox.Text);
DateTime date = DateTime.Parse(DateTextBox.Text);

El mismo resultado podríamos obtener usando la clase Convert:

int number = Convert.ToInt32(NumberTextBox.Text);
DateTime date = Convert.ToDateTime(DateTextBox.Text);

En este caso tenemos la ventaja de que si el argumento es null, el valor devuelto es cero. Esto puede comprobarse con Reflector.

image

Ninguno de los métodos anteriores resuelve el problema de conversión de datos cuando el usuario ingresa letras donde se esperaba números o fechas con formatos no válidos.

image

Para evitar esta situación podemos usar validators de ASP.NET, controles de máscara o bien el método TryParse:

int number;
if (int.TryParse(NumberTextBox.Text, out number))
{
    //TODO: Do something
}

El único problema con esta solución es que implica escribir mucho más código, generando rutinas más extensas y difíciles de leer.

La situación se complica un poco más con los Guid, pues no disponen de un método TryParse, entonces habría que usar Try/Catch, lo que complica aún más el código resultante.

String Extensions

Es aquí donde los Extensions Métodos pueden proporcionar una solución efectiva, elegante e intuitiva. La siguiente figura muestra cómo convertir una cadena a un número usando un extension method:

image 

La implementación del extension method es muy simple:

image

He creado una clase StringExtensions.cs con varios extensions methods para convertir a byte, short, long, float, double, decimal, boolean, datetime, guid, etc. Incluí también versiones compatibles con tipos nullables. Seguiré agregando nuevos métodos en la medida que encuentre aplicación práctica y aporten simplicidad en el código.

A continuación les dejo el link para descargar la clase StringExtensions.cs. En próximos post, publicaré otros extensions methods aplicables a controles de ASP.NET. Espero que sirva.

Saludos, Gus

jueves, 27 de noviembre de 2008

Nuevo control de Chart en ASP.NET

La gente de Microsoft ha liberado un nuevo control para ASP.NET 3.5 SP1 y Windows Forms para resolver nuestras necesidades de charting. Thanks!!!

Pueden leer el artículo de Scott Guthrie (en inglés) dando la noticia o si lo prefieren pueden leer la traducción en español de Juan María Laó Ramos.

Pueden arrancar con los siguientes links:

  1. Descargar los controles.
  2. Descargar el soporte para VS 2008 para estos controles.
  3. Descargar los ejemplos.
  4. Descargar la documentación.
  5. Visitar el foro.

Finalmente pueden descargar los recursos en español para estos controles: Paquete de idioma de Microsoft Chart Control para Microsoft .NET Framework 3.5

Y aquí les dejo unos screenshots de los ejemplos. Se ven muy bien!

image

image

image

image

Saludos, Gus

jueves, 20 de noviembre de 2008

Descubriendo Live Mesh

Como comentaba en un post anterior, Live Mesh es una de las aplicaciones de Microsoft que, basado en los servicios de Live Framework, permite en principio una sincronización de archivos simple, segura y transparente entre múltiples dispositivos. Desde computadoras personales, portátiles y dispositivos móviles a cámaras digitales e impresoras, entre otras. Pero Mesh es mucho más que sincronización de datos e iré comentando más en la medida que vaya probando y descubriendo sus bondades.

Por el momento estoy empezando a usar las capacidades de sincronización de archivos de Mesh con amigos y compañeros de proyecto y realmente es muy útil, si bien hoy no es una herramienta de control de versiones con capacidades de merge, etc., la simple posibilidad de tener archivos y carpetas distribuidos y sincronizados entre diferentes dispositivos ya tiene un gran valor.

Instalando Live Mesh

El primer paso consiste en ingresar al sitio https://www.mesh.com y conectarse a Live Mesh usando alguna cuenta de Live ID (por ejemplo una cuenta de Hotmail).

image

Esto significa que no necesito registrarme, completar formularios, etc. Simplemente utilizo mi cuenta de Live ID para Live Mesh.

image

Después del login en Windows Live, veré la página de dispositivos de Mesh. Desde aquí podré agregar mis dispositivos a Mesh. De forma predeterminada, ya existe un Live Desktop listo para usar. Se trata de un escritorio virtual online, donde podré acceder y administrar mis archivos y carpetas disponibles a través de Mesh.

image

Para agregar mi equipo a Mesh, selecciono Add device, y podré descargar e instalar el cliente de Mesh.

image

El cliente de Mesh está disponible para Windows XP y Windows Vista; existe una versión para Mac y, si bien en la página oficial no encontré referencias, me enteré que ya existe una versión para Windows Mobile que pueden descargarla desde https://m.mesh.com

Continuando con la instalación, debemos seleccionar la plataforma correcta y al presionar el botón Install, iniciaremos la descarga de LiveMesh.exe. Su instalación no tiene inconvenientes y dura unos 3 a 5 minutos.

image 

Finalizada la instalación encontraremos en el área de notificaciones el icono de Mesh y deberemos iniciar una sesión con nuestra cuenta Live ID para agregar nuestro dispositivo a la red.

image

El siguiente paso es darle un nombre a nuestro equipo para conectarlo a la red de Mesh.

image

En pocos segundos tendremos nuestro equipo conectado a Mesh y podremos verlo junto a los demás equipos y dispositivos. En este caso solo encuentro mi equipo portatil y el Live Desktop.

image

Y si volvemos a la página de dispositivos de Mesh en la web (https://www.mesh.com/Web/Devices.aspx), veremos nuestro dispositivo agregado al anillo de Mesh.

image

Compartiendo archivos y carpetas

Ahora quiero crear una carpeta y compartirla entre mis equipos de Mesh. Para lograr esto podría crear una carpeta en Live Desktop y luego sincronizarla con mi equipo portatil y viceversa.

A modo de ejemplo, voy a crear una carpeta Test en mi escritorio local y agregarla a Live Mesh.

image 

Como se muestra en la imagen es muy simple, gracias a la integración de Live Mesh con el Escritorio, Mi PC, el Explorador de Windows, etc.

Al seleccionar Add folder to Live Mesh veremos una ventana de diálogo donde podremos indicar el nombre de la carpeta a compartir y cuando debe sincronizarse con los demás dispositivos. Presionamos Ok y listo!.

image

Ahora nuestra carpeta Live Mesh cambió a un color azulado y ya tenemos disponible la misma carpeta en el Live Desktop (y otros dispositivos conectados). Observen también que el sitio de Mesh usa un protocolo HTTPS que nos proporciona seguridad de encriptación de datos en la transferencia de nuestros bytes.

image

Para terminar las pruebas, abrimos la carpeta Test de nuestro equipo local y creamos un simple archivo de texto Prueba.txt.

image

Esperamos unos segundos y listo! Este archivo Prueba.txt ahora también esta replicado en nuestro Live Desktop (y otros dispositivos, si existiesen). Podemos verificarlo ingresando a nuestro Live Desktop, haciendo doble clic en la carpeta Test.

image

Conclusiones

Es muy simple el uso de Live Mesh para compartir y sincronizar archivos y carpetas entre diferentes equipos y dispositivos. Su integración con el Shell de Windows lo hace muy amigable. Además podemos invitar a amigos y colaboradores para compartir archivos o documentos de trabajo y mantenernos sincronizados. Basta de diferentes versiones de archivo en diferentes lugares, tendremos una sola versión en todos los lugares que necesitemos.

En futuros post comentaré mi experiencia con Remote Desktop de Live Mesh.
Hasta la próxima!