posts - 315, comments - 268, trackbacks - 15

My Links

News

View Pietro Libro's profile on LinkedIn

DomusDotNet
   DomusDotNet

Pietro Libro

Tag Cloud

Article Categories

Archives

Post Categories

Blogs amici

Links

WCF Data Service (CTP2) e EF 4.1

Chi usa i WCF Data Service (vedi ADO.NET Data Service), potrebbe incontrare qualche difficoltà nell’utilizzo di Entity Framework 4.1 utilizzando l’approccio Code First, “a causa” del DbContext, dato che un DataService<T> si aspetta  un T derivato da ObjectContext . “Under the hood” il DbContext utilizza ObjectContext e di conseguenza è facile immaginare una possibile soluzione al problema: eseguire l’ovveride del metodo CreateDataSource del DataService<T> ed utilizzare l’ObjectContext corrente. E’ sufficiente qualche ricerca tramite Bing o Google per trovare del codice di esempio. Per chi ha voglia di sperimentare, può a scaricare la versione WCF Data Service CTP 2 di Marzo 2011, per utilizzare il supporto “nativo” al DbContext. Il pacchetto d’installazione può essere scaricato qui: http://www.microsoft.com/downloads/en/details.aspx?FamilyID=60fb0117-8cea-4359-b392-6b04cdc821be dove tra l’altro è presente un overview delle modifiche apportare rispetto alla versione precedente (altre info si trovano qui: http://blogs.msdn.com/b/writingdata_services/archive/2011/03/09/march-2011-ctp-of-wcf-data-services-for-odata-v3-is-live.aspx).

Per eseguire qualche test, partiamo da un’applicazione ASP.NET vuota, alla quale aggiungiamo una classe C# con la definizione del modello dati, una biblioteca, composta dalle entità “Book” ed “Author”, ogni “Author” ha una relazione one-to-many con “Book”. Tradotto in codice:

public class Book
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength (80)]
public string Title { get; set; }
[Required]
public string Summary { get; set; }
}

public class Author
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }

public virtual ICollection<Book> Books { get; set; }
}

Generalmente preferisco utilizzare le Fluent API rispetto alle DateAnnotations, ma essendo un modello veramente semplice…va bene lo stesso Sorriso. Definiamo il DbContext in questo modo:
public class LibraryContext : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Author> Authors { get; set; }
}
A questo punto aggiungiamo un WCF Data Service al progetto Web:
 
image
 
Rinominiamolo in LibraryService.svc. Per utilizzare la versione CTP 2 è necessario eliminare i riferimenti alle librerie System.Data.Services e System.Data.Services.Client ed aggiungere i riferimenti alle librerie Microsoft.Data.Services e Microsoft.Data.Services.Client presenti nella directory Bin presente nel percorso di installazione della versione di WCF Data Service appena installata. Dovremmo ottenere qualcosa del genere:
image
Oltre alle altre References. Modifichiamo il codice della classe LibraryService in questo modo:
public class LibraryService : DataService<LibraryContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Books", EntitySetRights.All );
config.SetEntitySetAccessRule("Authors", EntitySetRights.All);
config.SetServiceOperationAccessRule("FindBooksByTitle", ServiceOperationRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
}

[WebGet]
public IQueryable<Book> FindBooksByTitle(string title)
{
return (from b in CurrentDataSource.Books where b.Title.Contains(title) select b);
}
}

Dove utilizziamo direttamente la nostra classe LibraryContext senza strane alchimie (override). Tramite l’InitializeService configuriamo il servizio per l’accesso alle entità “Books” ed “Authors”, configuriamo la versione del protocollo da utilizzare (DataServiceProtocolVersion.V3) e l’accesso a FindBooksByTitle  che permette di trovare tutti i libri che contengono un determinato titolo. Se utilizziamo del codice simile al seguente per creare e popolare la base dati sottostante:

Book book1 = new Book()
{
Title = "Programming .Net 4 in C#",
Summary = "Progamming .Net Framework 4 Book (C#)"
};

Book book2 = new Book()
{
Title = "Programming .Net 4 in VB.NET",
Summary = "Progamming .Net Framework 4 Book (VB.NET)"
};

Author author = new Author()
{
Name="Tizio Caio"
};

author.Books = new List<Book>();
author.Books.Add(book1);
author.Books.Add(book2);

cnt.Books.Add(book1);
cnt.Authors.Add(author);

cnt.SaveChanges();

Possiamo avviare il sito tramite il Server Web locale ed utilizzare direttamente il browser per interagire con il servizio. Ad esempio possiamo recuperare la lista dei “Book” presenti digitando: http://localhost:5104/LibraryService.svc/Books o visualizzare i metadata esposti digitando http://localhost:5104/LibraryService.svc/$metadata. Passiamo a qualcosa di più concreto aggiungendo un’applicazione console alla nostra soluzione, creando un riferimento al servizio tramite la voce di menu “Add Service Reference”. Visual Studio crea per noi tutte le classi proxy necessarie ed un file Service.edmx con la definizione del modello. Testiamo il tutto aggiungendo del codice nel Main della classe Program:

LibraryServiceReference.LibraryContext ctx = new LibraryServiceReference.LibraryContext(new Uri("http://localhost:5104/LibraryService.svc/"));

#region Add Book and Author

Book book = new Book { Title = "Programming C# 4", Summary = "aaa" };
Author author = new Author { Name = "Tizio Caio" };

author.Books.Add(book);

ctx.AddToBooks(book);
ctx.AddToAuthors(author);

ctx.AddLink(author, "Books", book);

ctx.SaveChanges();

Console.WriteLine(ctx.Books.Count());

#endregion

#region Update Book Title

book.Title = "Programming .NET 4 in C#";
ctx.UpdateObject(book);
ctx.SaveChanges();

Book storedBook = ctx.Books.Where(p => p.Id == book.Id).Select(p => p).FirstOrDefault();

Console.WriteLine("Book Title={0}", book.Title);

#endregion

#region Delete Book-Author link

Author storedAuthor = (from a in ctx.Authors.Expand("Books")
where a.Name.Equals("Tizio Caio")
select a)
.FirstOrDefault();

Console.WriteLine("Author={0}, Book Title={1}",storedAuthor.Name , storedAuthor.Books[0].Title);

////Delete Link. Set null value in author_id column (Books Table).
ctx.DeleteLink(storedAuthor, "Books", storedAuthor.Books[0]);

ctx.SaveChanges();

#endregion

Il codice interagisce con un’istanza della LibraryContext sottostante per Aggiungere\Modificare\Eliminare i dati presenti nella base dati. Se volessimo interagire tramite HTTPWebRequest, potremmo scrivere del codice simile al seguente:

#region HttpWebRequest

////Get all Books
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://localhost:5104/LibraryService.svc/Books"));
webRequest.Method = "GET";
webRequest.Accept = "application/json";
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
System.IO.StreamReader streamReader = new System.IO.StreamReader(webResponse.GetResponseStream());

string jSONResponse = streamReader.ReadToEnd();
Console.WriteLine(jSONResponse);

////Find Books by title.
webRequest = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://localhost:5104/LibraryService.svc/FindBooksByTitle?title='Programming'"));
webRequest.Method = "GET";
webRequest.Accept = "application/json";
webResponse = (HttpWebResponse)webRequest.GetResponse();
streamReader = new System.IO.StreamReader(webResponse.GetResponseStream());

jSONResponse = streamReader.ReadToEnd();
Console.WriteLine(jSONResponse);

#endregion

#region Delete all objects

////Delete Objects.
ctx.DeleteObject(author);
ctx.DeleteObject(book);
ctx.SaveChanges();

#endregion
In entrambi i casi i dati vengono restituiti in formato JSON, quindi facilmente parsabili. Possiamo verificare come le nostre query LINQ siano tradotte in chiamate HTTP gestendo l’evento SendingRequest del servizio:
static void ctx_SendingRequest(object sender, System.Data.Services.Client.SendingRequestEventArgs e)
{
Console.WriteLine(e.Request.RequestUri.ToString());
}

Print | posted on domenica 15 maggio 2011 22:55 | Filed Under [ C# ASP.NET WCF Entity Framework 4.1 ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET