Tout au long du document, nous choisirons pour exemple:
Données: Une base de données avec un table "Person" contenant les champs "ID" (int), "Name" (string) et "Age" (int)
Besoin: Accéder à différentes fonctions d'interaction avec la table: Add, Delete, Update, Get. Tout ceci en Web Service
I) Le Data Transfer Object pattern
La vue exposée par un tel pattern (littéralement "patron" en francais) décompose notre accès aux données en 3 parties distinctes (elles ne peuvent et ne doivent pas dépendre les unes des autres).
DomainObject <-> Assembler <-> DataTransferObject
url : http://msdn2.microsoft.com/en-us/library/ms978717.aspx
a) Les Classes "DomainObject"
Il s'agit là d'un simple flux de données: un dataSet, un data reader, etc...
Ces classes sont existantes au sein du framework .Net.
b) Les classes DTO (Data Transfer Object)
Les classes DTO sont des conteneurs d'objets définissant leurs structures.
Elles sont toutes "serializable".
Exemple:
[XmlRoot(ElementName="Personne")]
publicclassPerson
{
private int _id = -1;
private string _name = null;
private int _age = -1;
[XmlAttribute(AttributeName="Identifiant")]
public int Id { get { return _id; } set { _id=value;} }
[XmlAttribute(AttributeName="Nom")]
public string Name { get { return _name; } set { _name=value;} }
[XmlAttribute(AttributeName="Age")]
public int Age { get { return _age; } set { _age=value;} }
public Person(int id, string name, int age)
{
_id = id;
_name = name;
_age = age;
}
}
public class PersonList : List<Person> { /* ... */ }
c) La classe Assembler
Cette classe constitue le lien entre les DTO et DomainObject.
Il permet la conversion de l'un vers l'autre et vice-versa.
Exemple:
public class Assembler
{
public DataSet CreatePersonDataSet(PersonList listPerson)
{
DataSet rDataset = new DataSet("Person");
DataTable table = rDataset.Tables.Add();
table.Columns.Add("id", typeof(int));
table.Columns.Add("name", typeof(string));
table.Columns.Add("age", typeof(int));
foreach(Person curPerson in listPerson)
{
DataRow curRow = table.NewRow();
curRow[0] = (int) curPerson.Id;
curRow[1] = (string) curPeron.Name;
curRow[2] = (int) curPerson.Age;
table.Rows.Add(curRow);
}
return rDataSet;
}
public PersonList CreatePersonDTO(DataSet ds)
{
PersonList rList = new PersonList();
foreach (DataRow curRow in ds.Tables[0].Rows)
{
rList.Add(new Person(
(int) curRow["id"],
(string) curRow["name"],
(int) curRow["age"]);
}
return rList;
}
}
II) La couche d'accès aux données
Cette couche comporte la logique métier du projet.
C'est ici que nous implémentons toute l'accès aux données de la base.
Exemple:
public class DataAccess
{
private string _connectionString = null;
public string ConnectionString { get { return _connectionString; } }
public DataAccess(string connectionString)
{
_connectionString = connectionString;
}
public void AddPerson(int id, string name, int age)
{
using (SqlConnection connection = new SqlConnection(_connectionString))
{
connection.Open();
//-- INSERT --
using (SqlCommand command = new SqlCommand("INSERT INTO Person VALUES(@id, @name, @age)", connection))
{
command.Parameters.Add(new SqlParameter("@id", id));
command.Parameters.Add(new SqlParameter("@name", name));
command.Parameters.Add(new SqlParameter("@age", age));
command.ExecuteNonQuery();
}
connection.Close();
}
}
public void UpdatePerson(int id, string name, int age)
{
using (SqlConnection connection = new SqlConnection(_connectionString))
{
connection.Open();
//-- UPDATE --
using (SqlCommand command = new SqlCommand("UPDATE Person SET name=@name, age=@age WHERE id=@id", connection))
{
command.Parameters.Add(new SqlParameter("@name", name));
command.Parameters.Add(new SqlParameter("@age", age));
command.Parameters.Add(new SqlParameter("@id", id));
command.ExecuteNonQuery();
}
connection.Close();
}
}
public void DeletePerson(int id)
{
using (SqlConnection connection = new SqlConnection(_connectionString))
{
connection.Open();
//-- DELETE --
using (SqlCommand command = new SqlCommand("DELETE Person WHERE id=@id", connection))
{
command.Parameters.Add(new SqlParameter("@name", name));
command.Parameters.Add(new SqlParameter("@age", age));
command.Parameters.Add(new SqlParameter("@id", id));
command.ExecuteNonQuery();
}
connection.Close();
}
}
public DataSet GetPersons()
{
DataSet ds = new DataSet("Person");
using (SqlConnection connection = new SqlConnection(_connectionString))
{
connection.Open();
using(SqlDataAdapter da = new SqlDataAdapter( "SELECT id, name, age FROM Person", connection))
{
da.Fill(ds);
}
connection.Close();
}
}
}
III) Le Web Service
Via les Web Methods, la couche Web Service se charge de faire interface entre demande utilisateur et exécution du traitement.
Exemple:
public class WebService : System.Web.Services.WebService
{
public WebService() { }
[WebMethod]
public void AddPerson(int id, string name, int age)
{
DataAccess da = new DataAccess("connection");
return da.AddPerson(id, name, age);
}
[WebMethod]
public void UpdatePerson(int id, string name, int age)
{
DataAccess da = new DataAccess("connection");
return da.UpdatePerson(id, name, age);
}
[WebMethod]
public void DeletePerson(int id)
{
DataAccess da = new DataAccess("connection");
return da.DeletePerson(id, name, age);
}
[WebMethod]
public string GetPersons()
{
DataAccess da = new DataAccess("connection");
DataSet ds = da.GetPersons();
//DTO
Assembler ass = new Assembler();
PersonList personList = ass.CreatePersonDTO(ds);
//Serialize to XML
StringWriter sw = new StringWriter();
XmlSerializer s = new XmlSerializer(typeof(personList));
s.Serialize(sw, personList);
return sw.ToString();
//--
}
}
Remarque:
- L'IDE visual studio propose l'implémentation automatique du code via la mise en place d'un élément DataSet (Add/New Item... -> dataSet).
- Pour le développement, je conseillerais fortement de créer un projet pour chaque point: projet assembly pour les premiers points, I) et II); projet web service pour le dernier, III).