Disegnare un'applicazione a servizi con Entity Framework e WCF

di Stefano Mostarda, in LINQ,

Realizzare un'applicazione a servizi con WCF è decisamente banale. Tuttavia gestire l'accesso ai dati tramite Entity Framework può non essere altrettanto banale o quantomeno scontato. Infatti ci sono diverse strade che si possono percorrere. Innanzitutto la gestione del contesto: conviene creare un nuovo contesto per ogni query sul database, oppure creare un contesto per ogni metodo del webservice? Come ultima scelta, ai client conviene restituire dei DTO o le entity di business? In questo articolo vedremo come creare servizi efficienti sfruttando le nuove API (DbContext) di Entity Framework e sfruttando le basi dell'articolo.

Introduzione all'esempio

Quello che realizzeremo nel corso dell'articolo è un semplice servizio che ci permette di manipolare i dati dei clienti colloquiando con il database tramite Entity Framework 4.3.1. Creeremo dei repository per gestire lal logica di accesso ai dati e li maschereremo dietro un'interfaccia. Per rendere l'applicazione più realistica, sfrutteremo Unity per iniettare un repository concreto e Automapper per mappare le entity verso i DTO che vengono restituiti dal servizio. Inoltre, realizzeremo una Unit Of Work che ci permetterà di accentrare in un unico punto il contesto di scrittura sul database. Il tutto sfruttando al massimo le potenzialità di WCF.

Il modello di Entity Framework

Il modello che viene persistito sul database è molto semplice in quanto contiene i dati di un cliente più il PDF del suo contratto. Quando il client del servizio recupera i dati del cliente, il client non necessita del PDF del contratto e quindi non c'è bisogno ne che il web service lo restituisca al client ne che il web service li estragga dal database. Nonostante questo vogliamo che il PDF del contratto risieda nella tabella dei clienti. Per fare questo dobbiamo ricorrere alla tecnica del Table-Splitting che prevede che più classi siano mappate su un'unica tabella. Creiamo una classe Company e una classe CompanyContract. La prima contiene i dati del cliente, mentre la seconda contiene i dati del contratto e un riferimento al cliente. Entrambe le classi sono mostrate nel seguente codice.

public class Company
{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Address { get; set; }
  public string City { get; set; }
  public string Country { get; set; }
  public string ZipCode { get; set; }
  public CompanyContract Contract { get; set; }
}


public class CompanyContract
{
  public int Id { get; set; }
  public byte[] Contract { get; set; }
  public Company Company { get; set; }
}

A questo punto dobbiamo creare il mapping per persistere queste classi sul database.

Il contesto di Entity Framework

Se non scrivessimo alcun codice di mapping, Entity Framework Code-First creerebbe due tabelle mettendole in relazione tra di loro. Con la tecnica del Table-Splitting, possiamo dire ad Entity Framework che il campo Contract della classe CompanyContract viene mappato sulla colonna Contract della tabella dei clienti. Per fare questo dobbiamo creare la classe che eredita da DbContext nel seguente modo.

public class SVCContext : DbContext
{
  public DbSet<Company> Companies { get; set; }

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Company>().ToTable("Companies");
    modelBuilder.Entity<CompanyContract>().ToTable("Companies");

    modelBuilder.Entity<Company>().Property(p => p.Id)
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

    modelBuilder.Entity<Company>()
      .HasRequired(p => p.Contract)
      .WithRequiredPrincipal(p => p.Company);
  }
}

Innanzitutto abbiamo un solo DbSet: Companies. Il vero codice importante però sta nel metodo OnModelCreating. Qui dentro specifichiamo che le classi Company e CompanyContract sono mappate sulla stessa tabella (Companies) e successivamente specifichiamo come sono collegate le classi tra di loro tramite i metodi HasRequired e WithRequiredPrincipal. Quest'ultima specifica è fondamentale. Senza di questa infatti Entity Framework non saprebbe stabilire qual'è la classe principale (Company) e qual'è quella secondaria (CompanyContract) e solleverebbe un'eccezione.

Grazie a questo mapping, quando eseguiamo una query sui clienti, la proprietà Contract non viene recuperata dal database e quindi otteniamo un grosso risparmio di risorse visto che un file PDF può essere estremamente grande.

Ora che abbiamo stabilito il nostro modello ed il suo mapping, vediamo com'è strutturato il servizio.

Il servizio

Il servizio che gestisce i dati è molto semplice. C'è un metodo per recuperare i dati del cliente (con l'eccezione del contratto), un metodo per creare/aggiornare un cliente, e un metodo per recuperare il PDF del contratto. Il codice dell'interfaccia del servizio è mostrato nell'esempio seguente.

[ServiceContract]
public interface IEFService
{
  [OperationContract]
  CompanyDTO ReadCompany(int id);

  [OperationContract]
  void WriteCompany(CompanyDTO company, byte[] contract);

  [OperationContract]
  byte[] ReadContract(int id);
}

Dalle firme dei metodi possiamo vedere che i metodi non espongono le classi Company e CompanyContract, ma una classe CompanyDTO che ha lo scopo di disaccoppiare le classi di business dal servizio.

public class CompanyDTO
{
  public string Id { get; set; }
  public string Name { get; set; }
  public string Address { get; set; }
  public string City { get; set; }
  public string Country { get; set; }
  public string ZipCode { get; set; }
}

In questo caso la differenza tra la classe classe CompanyDTO e la classe Company è che la prima non contiene nessun riferimento al PDF perchè questo è esposto dal servizio tramite il metodo ReadContract.

Ora che abbiamo capito cosa deve fare la nostra applicazione, passiamo al primo passo e vediamo come creare un repository che wrappi la logica di accesso ai dati.

4 pagine in totale: 1 2 3 4

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

Top Ten Articoli

Articoli via e-mail

Iscriviti alla nostra newsletter nuoviarticoli per ricevere via e-mail le notifiche!

In primo piano

I più letti di oggi

In evidenza

Misc