Wilfried Woivré & .Net

Utiliser RIA Services & le Table Storage d’Azure

MAI29

Lors d’un Azure Camp organisé par ZeCloud, j’ai montré comment exposer le Table Storage de Windows Azure via un WCF Data Services, cela nous permettait d’avoir une exposition de nos données via OData. Vous pouvez retrouver la démonstration sur le codeplex de ZeCloud, et me demander plus d’infos au prochain Azure Camp

 

Dans la même idée, je me suis aperçu que la dernière version de RIA Services proposait quelque chose du même genre, via son toolkit, on va donc voir comment le mettre en place !

 

Commençons déjà par créer un projet de type Cloud, ainsi qu’une application Silverlight avec un site web et WCF RIA Services. Il nous faut ensuite ajouter les références, par NuGet c’est plus facile

image

 

 

Maintenant, il nous faut créer notre Model, pour cela, on va prendre un cas très simple :

 

public class Person : TableEntity
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    /// <summary>
    /// The property is set to be mentioned explicitly in the DataForm ...
    /// ONLY FOR THE DEMO
    /// </summary>
    public string MyPartitionKey
    {
        get
        {
            return base.PartitionKey;
        }
        set
        {
            base.PartitionKey = value;
        }
    }
}

On peut voir déjà quelques différences, premièrement on n’hérite pas de TableStorageEntity, mais de TableEntity qui hérite lui même de TableServiceEntity, et la deuxième c’est que pour le cas de la démo, j’ai voulu tester plusieurs PartitionKey, j’ai donc réexposé via une autre propriété celle ci afin qu’elle apparaisse dans mon DataForm Silverlight.

 

Maintenant, voyons notre contexte de données pour notre Table Storage

public class AzureServiceContext : TableEntityContext
{      
    public AzureServiceContext() : 
        base(RoleEnvironment
            .GetConfigurationSettingValue("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"))
    {
    }

    public TableEntitySet<Person> People
    {
        get { return base.GetEntitySet<Person>(); }
    }
}

 

Donc de même ici, on peut voir quelques différences, déjà au niveau de l’héritage, ici on hérite de TableEntityContext qui hérite lui même de TableServiceEntity.

De plus, on peut voir que l’on ne gère pas non plus la création des tables dans le Table Storage, vu que le toolkit de RIA Services s’en occupe pour nous.

Il ne vous reste plus qu’à créer votre Domain Service de façon classique, il faut juste renseigner aucun contexte.

image

 

Maintenant, implémentons notre DomainService

[EnableClientAccess()]
public class TSDomainService : TableDomainService<AzureServiceContext>
{

    protected override string PartitionKey
    {
        get
        {
            return null;
        }
    }

    public IQueryable<Person> GetPeople()
    {
        return EntityContext.People;
    }

    public void AddPerson(Person person)
    {
        EntityContext.People.Add(person);
    }

    public void DeletePerson(Person person)
    {
        EntityContext.People.Delete(person);
    }

    public void UpdatePerson(Person person)
    {
        EntityContext.People.Update(person);
    }
}

On a dorénavant la possibilité de faire un TableDomainService pour englober notre contexte Azure, de même les méthodes standards de CRUD sont facilitées.

Voyons maintenant la PartitionKey, par défaut  le toolkit met la PartitionKey à la valeur du nom du Domain Service, pour éviter qu’elle soit définit ainsi, il suffit de surcharger la PartitionKey, cependant cela veut dire qu’il vous faudra la spécifier à chaque fois, ce qui est mieux si vous voulez une bonne structure de donnée dans votre Table Storage

 

Et voilà le résultat dans un DataForm Silverlight

image

 

Vous pouvez retrouver les sources de la solution ici

Remonter

WCF RIA Services et les Complex Type d’Entity Framework

MAI1

Actuellement, je suis en train de réaliser un projet pour une formation Silverlight, le but de ce projet est de réaliser un mini site eCommerce totalement en Silverlight. Il s’articule autour des technologies Entity Framework et RIA Services ainsi que Silverlight 4 avec un design pattern de type MVVM. J’utilise comme base de données AdventureWorksLT2008. Je donnerais tout le code de cette démonstration sur mon blog un autre jour, avec une explication un peu plus conséquente !

Enfin passons, pour ma vie, dans cet article je vais vous montrer comment renvoyer un Complex Type crée depuis EntityFramework et renvoyer dans notre Silverlight.

Je commence donc par créer un projet de type Silverlight Business Application, ensuite je crée un modèle Entity Framework, ici, on se basera uniquement sur la table Employees de NorthWind.

Sur cette table, je vais vouloir récupérer uniquement les noms et les prénoms des employés, afin de les renvoyer à mon application Silverlight, je vais donc créer un ComplexType contenant deux chaines de caractères.

J’en suis donc arrivé à un diagramme ressemblant à ceci :

image

Maintenant créons notre Domain Service, et notre requête Linq qui ressemble à cela :

[EnableClientAccess()]
public class NortwindDomainService : LinqToEntitiesDomainService<NorthwindEntities>
{

// TODO: Consider
// 1. Adding parameters to this method and constraining returned results, and/or
// 2. Adding query methods taking different parameters.
public IQueryable<EmployeeInformation> GetEmployees()
{
return from e in ObjectContext.Employees
select new EmployeeInformation() { FirstName = e.FirstName, LastName = e.LastName };
}
}

On s’apprête donc à créer une interface pour afficher les données en Silverlight, sauf qu’en compilant on obtient une erreur :

image

Il manque donc une clef à notre complex type, qu’à cela ne tienne, pour en rajouter une, il suffit de créer une classe partielle à notre type et d’y ajouter une clé.

public partial class EmployeeInformation
{
[Key]
[DataMember]
public int EmployeeInformationId { get; set; }
}

Maintenant que ce problème est réglé et qu’on a bien entendu ajouter la déclaration de notre clé dans notre requête, il faut passer au front, on va donc créer un liste qui affiche les noms et les prénoms des employés

On obtient un code de ce style :

public IEnumerable<Entity> Employees
{
get { return (IEnumerable<Entity>)GetValue(EmployeesProperty); }
set { SetValue(EmployeesProperty, value); }
}

// Using a DependencyProperty as the backing store for Employees.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty EmployeesProperty =
DependencyProperty.Register("Employees", typeof(IEnumerable<Entity>), typeof(MainPage), new PropertyMetadata(null));

public MainPage()
{
InitializeComponent();
this.DataContext = this;
this.Loaded += (sender, e) => LoadData();
}

private void LoadData()
{
NortwindDomainContext context = new NortwindDomainContext();
LoadOperation loadOp = context.Load(context.GetEmployeesQuery());
Employees = loadOp.Entities;
}

On charge donc le tout dans une liste que l’on expose à notre interface, le tout compile sans aucun problème on s’attend donc à voir un résultat, cependant on obtient encore une erreur :

image

On a donc une exception au niveau de notre DomainService, la requête quand à elle est bonne, le seul problème est en fait une erreur d’Entity Framework, on ne peut pas créer de Complex Type dans une requête Linq To Entities.

Il nous faut donc passer via un objet anonyme pour pouvoir ensuite instancier notre complex Type.

public IQueryable<EmployeeInformation> GetEmployees()
{
var employees = from e in ObjectContext.Employees
select new { e.EmployeeID, e.FirstName, e.LastName };

return employees.ToList().ConvertAll(e => new EmployeeInformation()
{
EmployeeInformationId = e.EmployeeID,
FirstName = e.FirstName,
LastName = e.LastName
}).AsQueryable();

}

On obtient donc par la suite nos différentes données :

image

Et voilà en espérant que cette technique vous sera utile dans vos futurs développements avec RIA Services ! En plus je vous fournis tout le code source !

image

Remonter

Intégrer des images via RIA Services

AVRI2

 

Il m’est récemment arrivé lors d’une démonstration de vouloir sortir les images de NorthWind pour les inclure dans un composant Silverlight.

Donc naïvement, je me suis dis qu’un simple “converter” binaire suffira pour afficher mon image. “Converter” que voici :

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    BitmapImage image = new BitmapImage();

    using(MemoryStream stream = new MemoryStream(value as byte[]))
    {
        image.SetSource(stream);
    }

    return image;
}

Et bien entendu lors de l’exécution on a donc une petite erreur qui n’aide pas trop en fait ….

image

Bon après quelques réflexions, on se dit que le format de l’image n’est pas correct, où alors qu’il y a un problème avec le flux de données.

La solution que j’ai trouvé est d’enregistrer l’image sur un serveur de medias, et de passer l’url de celle-ci à Silverlight. Comme ça on gagne au niveau du Converter, et de plus on m’a toujours déconseillé de stocker mes images en bases de données pour la place qu’elles utilisent.

Le plus simplement possible, en faisant cela directement dans mon fichier DomainService,

private IQueryable<Category> CategoryWithPictureUrl(IQueryable<Category> categories)
{
    foreach (var category in categories)
    {
        if (category.Picture != null)
        {
            TypeConverter tc = TypeDescriptor.GetConverter(typeof(System.Drawing.Bitmap));
            System.Drawing.Bitmap b = (System.Drawing.Bitmap)tc.ConvertFrom(category.Picture);
            if (b != null)
            {
                String path = String.Format(@"D:\Medias\Category{0}.jpg", category.CategoryID);
                if (!File.Exists(path))
                {
                    b.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
                    b.Dispose();
                }
                category.PictureUrl = String.Format("http://localhost/Media/Category{0}.jpg", category.CategoryID);
            }
        }
    }
    return categories.AsQueryable();
}

La propriété PictureUrl est ajouté dans une classe partielle de l’entité Category de la façon suivante :

public partial class Category
{
    [DataMemberAttribute]
    public string PictureUrl { get; set; }
}

Et voilà, comme ça on peut voir nos images directement depuis Silverlight !

Pensez aussi à ne pas envoyer votre binaire histoire d’alléger un peu votre service.

Bien entendu, ce fonctionnement marche très bien sans RIA Services !

Remonter

Partager une propriété “partielle” via WCF RIA Services

AVRI2

 

J’ai présenté dans un précédent article la méthode pour ajouter une propriété à une entité issue d’un modèle Entity Framework (ou Linq To Sql) –> http://wilfriedwoivre.wordpress.com/2009/03/21/entity-framework-trucs-et-astuces/

Pour le résumé, on ajoute une propriété Total via une classe partielle sur la classe Order.

public partial class Order
{
    public decimal Total
    {
      get { return Order_Details.Sum(n => n.Quantity * n.UnitPrice); }
    }
}

Maintenant si on essaye de faire la même chose, afin de récupérer notre Total via RIA Services on s’aperçoit qu’on a un léger problème lorsqu’on regarde les différentes sources de données disponibles dans Silverlight via RIA Services.

image

Afin de rajouter notre propriété personnalisée dans notre composant Silverlight, il suffit simplement de rajouter un attribut DataMemberAttribute sur Total.

public partial class Order
{
    [DataMemberAttribute]
    public decimal Total
    {
      get { return Order_Details.Sum(n => n.Quantity * n.UnitPrice); }
    }
}

 

En fait, il suffit simplement d’ajouter cet attribut, puis que WCF RIA Services est comme son nom l’indique basé sur un service WCF.

Remonter