Tutoriel WCF Partie 1


microsoft

Dans le cadre de ma dernière mission, j’ai eu l’occasion de travailler avec WCF et Windows Services. Dans cette série de posts,  je vais vous présenter les principes de base WCF et Windows services par l’exemple.

WCF (Windows Communication Foundation) est la plateforme Windows qui gère la communication entre les applications interopérables et distribuées, ce qui veut dire que WCF permet l’interaction entre deux applications distantes qui peuvent être définies dans des plateformes différentes, cette communication se base sur les web services.

Wcf_Pres_Image01

 

WCF a énormément facilité la définition des web services, nous n'avons pas besoin de connaitre la syntaxe du SOAP, ou de créer les fichiers de configuration qui enveloppent les messages,  il suffit juste de créer le service, et de configurer les points de terminaison  et le web service serait prêt à l’utilisation.

C’est quoi les points de terminaison ? Ce sont les points d’entrées ou d’utilisation du service, en effet chaque service web peut avoir plusieurs points de terminaison définis avec différents protocoles comme HTTP, HTTPS, TCP …

Wcf_Pres_Image02

Dans ce qui suit je vais vous montrer comment créer un service WCF, via un exemple d’un service web qui récupère des données d'une base de données et les propose à ses clients.

Création d’un projet Class Librairie 

  • Dans l’exemple j’utilise la base données SchoolData, dont voici le schéma :

Course (CourseId int, Title string, Credits int)

Students(ID, LastName string , FirstMidName string   , EnrollementDate Date)

Enrollement(EnrollementId int , studentId int , courseId int , Grade int)

  • Je commence par créer une solution vide, et un projet de type class Library qui aura la structure suivante :

Wcf_Pres_Image1

 

  • J’ajoute ensuite dans le dossier Data l'Entity FrameWork 6 pour la base de données SchoolData.
  • Dans le dossier Service je crée mes classes de services qui communiquent avec l’Entity FrameWork.

Classe ServiceBase.cs

using WCFExampleLibrary.Data;

namespace WCFExampleLibrary.Services
{
   public class ServiceBase
    {
       protected SchoolDataEntities SchoolDataEntities { get; set; }
       
       public ServiceBase()
       {
          SchoolDataEntities = new SchoolDataEntities();
         
       }
    }
}

Interface/ classe  ICourseRepository / CourseRepository

using System.Collections.Generic;
using WCFExampleLibrary.Data;

namespace WCFExampleLibrary.Services
{
    public interface  ICourseRepository
    {
        IEnumerable<Course> GetCourses();
        Course GetCourseById(int courseId);
    }
}
using System.Collections.Generic;
using System.Linq;

namespace WCFExampleLibrary.Services
{
    public class CourseRepository : ServiceBase, ICourseRepository
    {
        public IEnumerable<Data.Course> GetCourses()
        {
            return SchoolDataEntities.Courses;
        }

        public Data.Course GetCourseById(int courseId)
        {
            return SchoolDataEntities.Courses.FirstOrDefault(t => t.CourseID == courseId);
        }
    }
}

Interface IStudentRepository / StudentRepository

using System.Collections.Generic;
using WCFExampleLibrary.Data;

namespace WCFExampleLibrary.Services
{
    public interface IStudentRepository
    {
        IEnumerable<Student> GetStudents();
        Student GetStudentById(int Id);
    }
}

 

using System.Collections.Generic;
using System.Linq;

namespace WCFExampleLibrary.Services
{
    public class StudentRepository : ServiceBase, IStudentRepository
    {
        public IEnumerable<Data.Student> GetStudents()
        {
            return SchoolDataEntities.Students;
        }

        public Data.Student GetStudentById(int Id)
        {
            return SchoolDataEntities.Students.FirstOrDefault(t => t.ID == Id);
        }

    }
}
  • Interface  IEnrollementRepository / EnrollementRepository
using System.Collections.Generic;
using WCFExampleLibrary.Data;
using WCFExampleLibrary.Model;

namespace WCFExampleLibrary.Services
{
    public interface  IEnrollementRepository
    {
        IEnumerable<Enrollment> GetEnrollements();
        Enrollment GetEnrollementtByCourseAndStudent(int courseId, int studentId);
        IEnumerable<Student> GetAllStudentsOfCourse(int courseId);

        IEnumerable<Student> GetAllStudentsWithoutCourse();
         IEnumerable<Student> GetAllStudentsOfCourseFinance();
        IEnumerable<StudentVm> GetAllStudentsVMOfCourseFinance();
        IEnumerable<StudentVm> GetAllStudentsVMWithoutCourse();


    }
}
using System.Collections.Generic;
using System.Linq;
using WCFExampleLibrary.Data;
using WCFExampleLibrary.Model;

namespace WCFExampleLibrary.Services
{
  public class EnrollementRepository : ServiceBase, IEnrollementRepository
    {
      public IEnumerable<Data.Course> GetCourses()
      {
        return SchoolDataEntities.Courses;
      }

        public Data.Course GetCourseById(int courseId)
        {
         return SchoolDataEntities.Courses.FirstOrDefault(t => t.CourseID == courseId);
        }

        public IEnumerable<Enrollment> GetEnrollements()
        {
            return SchoolDataEntities.Enrollments;
        }

        public Enrollment GetEnrollementtByCourseAndStudent(int courseId, int studentId)
        {
           return SchoolDataEntities.Enrollments.FirstOrDefault(
                  t => t.CourseID == courseId && t.StudentID == studentId);
        }

        public IEnumerable<Student> GetAllStudentsOfCourse(int courseId)
        {
            return SchoolDataEntities.Enrollments.Where(t => t.CourseID == courseId).Select(t=>t.Student);
        }
        public IEnumerable<Student> GetAllStudentsOfCourseFinance()
        {
            return SchoolDataEntities.Enrollments.Where(
                  t => t.Course.Title == "finance").Select(t => t.Student);
        }
        public IEnumerable<StudentVm> GetAllStudentsVMOfCourseFinance()
        {
            return GetAllStudentsOfCourseFinance().Select(t => new StudentVm
            {
                EnrollmentDate = t.EnrollmentDate,
                FirstMidName = t.FirstMidName,
                ID = t.ID,
                LastName = t.LastName

            }).AsEnumerable();
        }
        public IEnumerable<Student> GetAllStudentsWithoutCourse()
        {
           return SchoolDataEntities.Enrollments.Select(t => t.Student);
        }

    public IEnumerable<StudentVm> GetAllStudentsVMWithoutCourse()
    {
        return GetAllStudentsWithoutCourse().Select(t => new StudentVm
            {
                EnrollmentDate = t.EnrollmentDate,
                FirstMidName = t.FirstMidName,
                ID = t.ID,
                LastName = t.LastName

            }).AsEnumerable();
        }

    }
}

Gestion des Injections de dépendance

Pour gérer l’injection de dépendances, j’utilise Ninject. Il existe plusieurs versions de Ninject qui sont spécifiques au type de projet, comme par exemple Ninject pour le web, Ninject pour WCF …

J'utilise la version basic.

Pour utiliser Ninject, il faut suivre les étapes suivantes :

  • L’installer via NGuet ou autre,
  • Il faut créer une classe qui hérite de la classe abstraite NinjectModule et implémenter la methode Load();
using Ninject.Modules;

namespace WCFExampleLibrary.Services
{
    public class IocServices : NinjectModule
    {
        public override void Load()
        {
            Bind<ICourseRepository>().To<CourseRepository>();
            Bind<IStudentRepository>().To<StudentRepository>();
            Bind<IEnrollementRepository>().To<EnrollementRepository>();
        }
    }
}
  • Ensuite je crée une classe qui se charge d'instancier mes objets à partir du Noyau de Ninject.
using System.Reflection;
using Ninject;
namespace WCFExampleLibrary.Services
{
   public static class FactoryBuilder
    {
       private static IKernel _Kernal { get; set; }
       public static T GetServices<T> ()
       {
           _Kernal = new StandardKernel();
           _Kernal.Load(Assembly.GetExecutingAssembly());
           return _Kernal.Get<T>();
       }
    }
}

Création du service WCF 

Je crée le service WCF dans le dossier WCFServices et  je l’ajoute via le menu add Item comme suit :

Wcf_Pres_Image3

Visual studio crée les deux fichiers : ISchoolWcfServices et SchoolWcfServices

Le premier correspond à l’interface du WCF services qui contient le service contrat, dans laquelle il faut déclarer les opérations à publier par le service WCF. L’interface doit être précédée par l’attribut [ServiceContract], et pour que chaque Opération soit visible par les consommateurs ou les clients du web service, elle doit être précédée par l’attribut [OperationContract]

Dans notre exemple je propose deux services :

  • Liste des étudiants qui ont suivi le cours de Finance
  • Liste des étudiants qui n’ont pas encore suivi de cours

Ce qui se traduira par deux méthodes GetAllStudentsOfCourseFinancial, GetAllStudentsWithoutCourse précédées par l’attribut [OperationContract]

using System.Collections.Generic;
using System.ServiceModel;
using WCFExampleLibrary.Model;

namespace WCFExampleLibrary.WCFServices
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ISchoolWcfServices" in both code and config file together.
    [ServiceContract]
    public interface ISchoolWcfServices
    {
        /// <summary>
        /// ce web service expose les étudiants qui 
        /// ont suivit la formation de finance 
        /// 
        /// </summary>

        [OperationContract]
        IList<StudentVm> GetAllStudentsOfCourseFinancial();

        /// <summary>
        /// ce web service expose les étudiants qui n'ont suivi 
        /// aucune formation
        /// 
        /// 
        /// </summary>

        [OperationContract]
        IList<StudentVm> GetAllStudentsWithoutCourse();
    }
}
using System.Collections.Generic;
using System.Linq;
using WCFExampleLibrary.Model;
using WCFExampleLibrary.Services;

namespace WCFExampleLibrary.WCFServices
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "SchoolWcfServices" in both code and config file together.
    public class SchoolWcfServices : ISchoolWcfServices
    {
        private IEnrollementRepository enrollementRepository;
        public SchoolWcfServices()
        {
            enrollementRepository = FactoryBuilder.GetServices<IEnrollementRepository>();
        }
        public IList<StudentVm> GetAllStudentsOfCourseFinancial()
        {
            return enrollementRepository.GetAllStudentsVMOfCourseFinance().ToList();
           
        }

        public IList<StudentVm> GetAllStudentsWithoutCourse()
        {
            return enrollementRepository.GetAllStudentsVMWithoutCourse().ToList();
        }
    }
}

Partie Configuration du service WCF

Lors de la génération du WCF, Visual studio a ajouté du code entre les deux balises <system.serviceModel> </system.serviceModel> dans le fichier App.config,

Supprimer ce code généré, et remplacer le par le code suivant :

<system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicTextBinding" messageEncoding="Text" textEncoding="utf-8">
        </binding>
      </basicHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="WCFExampleLibrary.WCFServices.SchoolWcfServices">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="BasicTextBinding" contract="WCFExampleLibrary.WCFServices.ISchoolWcfServices">
         </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8733/WCFExampleLibrary.WCFServices/SchoolWcfServices/" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel> 

C’est la configuration nécessaire pour publier un service Web WCF :

  • Le nom du service web est le nom complet de la classe SchoolWcfServices.
  • Le comportement du service (behaviours):
    • Il décrit le type des protocoles utilisés pour l’échange.
    • Les informations de débogage comme par exemple:  inclure les détails des exceptions dans le message échangé.
    • Le type d’encodage des messages échangés …
  • Le point de terminaison de notre service : contient le type de bindings (par défaut c’est le basicHttpBinding), si on veut un bindings personnalisé il faut le spécifier entre les deux éléments < bindings> </bindings>. Comme c’est fait pour BasicTextBinding .
  • Dans les prochains blogs je vais vous présenter cet aspect plus en détail pour envoyer des fichiers via WCF.
  • Le contrat du service : c’est l’interface visible au client du service web, elle référence le chemin complet de l’interface services.
  • L’adresse du service déclarée dans la balise <host> comme base adresse, dans notre cas c’est localhost avec le port 8733.

A ce stade, j’ai terminé la création de mon service WCF. Pour l’exploiter par les clients il faut l’héberger (hosting), pour cela il existe plusieurs types d’hébergement que je vais présenter dans l’article suivant.

 

 

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.