Skip to content

6. مطالعه موردی با PostgreSQL 9.2.1

6.1. نصب ابزارها

ابزارهای قابل نصب به شرح زیر هستند:

  • SGBD: [http://www.enterprisedb.com/products-services-training/pgdownload#windows];
  • یک ابزار مدیریتی: EMS، SQL مدیر برای PostgreSQL، نرم‌افزار رایگان [http://www.sqlmanager.net/fr/products/postgresql/manager/download].

در مثال‌های زیر، کاربر 'postgres' رمز عبور 'postgres' را دارد.

ابزار PostgreSQL را اجرا کنیم و سپس ابزار [SQL Manager Lite for PostgreSQL] را که برای مدیریت SGBD استفاده خواهیم کرد.

  • در [1]، ما SGBD و PostgreSQL را از سرویس‌های ویندوز راه‌اندازی می‌کنیم؛
  • در [2]، سرویس راه‌اندازی می‌شود؛

اکنون ابزار [SQL Manager Lite for MySQL] را راه‌اندازی می‌کنیم که از آن برای مدیریت SGBD و [3] استفاده خواهیم کرد.

  • در [4]، یک پایگاه داده جدید ایجاد می‌کنیم؛
  • در [5]، نام پایگاه داده را مشخص می‌کنیم؛
  • در [5]، ما با نام کاربری postgres و رمز عبور postgres وارد می‌شویم؛
  • در [6]، ما برخی اطلاعات را ارائه می‌دهیم؛
  • در [7]، شما فرمان SQL را که قرار است اجرا شود، تأیید می‌کنید؛
  • در [8]، پایگاه داده ایجاد شده است. اکنون باید در [EMS Manager] ثبت شود. جزئیات صحیح هستند. ما [OK] را اجرا می‌کنیم؛
  • در [9]، وارد می‌شویم؛
  • در [10]، [EMS Manager] پایگاه داده را نمایش می‌دهد که در حال حاضر خالی است. توجه داشته باشید که جداول به اسکیمای به نام «public [11]» تعلق خواهند داشت.

اکنون یک پروژهٔ 2012 به نام VS را به این پایگاه داده متصل خواهیم کرد.

6.2. ایجاد پایگاه داده از طریق اشیاء

ما کار را با کپی کردن پوشه پروژه [RdvMedecins-SqlServer-01] در [RdvMedecins-PostgreSQL-01] و [1] آغاز می‌کنیم:

  • به [2]؛ در VS 2012، پروژه [RdvMedecins-SqlServer-01] را از راه‌حل حذف می‌کنیم؛
  • در [3]، پروژه حذف شده است؛
  • در [4]، یکی دیگر اضافه می‌کنیم. این مورد در پوشه [RdvMedecins-PostgreSQL-01] قرار دارد که قبلاً ایجاد کرده بودیم؛
  • در [5]، پروژه‌ای که بارگذاری شده است [RdvMedecins-SqlServer-01] نام دارد؛
  • در [6]، آن را به [RdvMedecins-PostgreSQL-01] تغییر نام می‌دهیم؛
  • در [7]، پروژه دیگری به راه‌حل اضافه می‌شود. این پروژه از پوشه [RdvMedecins-SqlServer-01] پروژه‌ای که قبلاً از راه‌حل حذف کرده بودیم، گرفته شده است؛
  • در [8]، پروژه [RdvMedecins-SqlServer-01] مجدداً به راه‌حل اضافه شده است.

پروژه [RdvMedecins-PostgreSQL-01] با پروژه [RdvMedecins-SqlServer-01] یکسان است. ما باید چند تغییر ایجاد کنیم. در [App.config]، ما رشته اتصال و [DbProviderFactory] را اصلاح خواهیم کرد که باید برای هر SGBD تطبیق داده شود.


<!-- رشته اتصال پایگاه داده -->
  <connectionStrings>
    <add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
  </connectionStrings>
  <!-- ارائه‌دهنده فابریک -->
  <system.data>
    <DbProviderFactories>
      <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
    </DbProviderFactories>
  </system.data>
  • خط ۳: کاربر و رمز عبور او؛
  • خطوط ۷–۹: DbProviderFactory. خط ۸ به DLL و [Npgsql] اشاره دارد که ما نداریم. ما می‌توانیم آن را با استفاده از NuGet و [1] به‌دست آوریم:
  • در [2]، کلمهٔ کلیدی postgresql را در کادر جستجو وارد کنید؛
  • در [3]، بسته [Npgsql] را انتخاب کنید. این یک کانکتور ADO.NET برای PostgreSQL است؛
  • در [4]، دو مرجع اضافه شده‌اند؛
  • در [5]، در [App.config]، نسخه صحیح DLL باید وارد شود. این نسخه را می‌توان در ویژگی‌های آن یافت.

در فایل [Entites.cs]، طرح‌بندی جداول قابل تولید باید تنظیم شود:


  [Table("MEDECINS", Schema = "public")]
  public class Medecin : Personne
  {...}

  [Table("CLIENTS", Schema = "public")]
  public class Client : Personne
  {...}

  [Table("CRENEAUX", Schema = "public")]
  public class Creneau
  {...}

  [Table("RVS", Schema = "public")]
  public class Rv
  {...}

قبلاً دیدیم که هنگام ایجاد پایگاه‌داده PostgreSQL، جداول متعلق به اسکیمای «public» بودند.

اکنون در حال پیکربندی اجرای پروژه هستیم:

  • در [1]، ما اسمبلی‌ای که قرار است تولید شود را نام متفاوتی می‌دهیم؛
  • در [2]، ما همچنین یک فضای نام پیش‌فرض متفاوت را مشخص می‌کنیم؛
  • در [3]، برنامه‌ای را که باید اجرا شود مشخص می‌کنیم.

در این مرحله، هیچ خطای کامپایل وجود ندارد. بیایید برنامه [CreateDB_01] را اجرا کنیم. استثناء زیر را دریافت می‌کنیم:

Exception non gérée : System.Data.MetadataException: Le schéma spécifié n'est pas valide. Erreurs :
(11,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(23,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(33,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(43,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
   à System.Data.Metadata.Edm.StoreItemCollection.Loader.ThrowOnNonWarningErrors
()
   ...
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\d
vp\Entity Framework\RdvMedecins\RdvMedecins-Oracle-01\CreateDB_01.cs:ligne 15

به یاد داریم که با MySQL و Oracle با همین خطا مواجه شده بودیم. این مربوط به نوع فیلد Timestamp در entities است. ما همان تغییری را که در Oracle اعمال کردیم، انجام می‌دهیم. در entities، سه خط را جایگزین می‌کنیم


    [Column("TIMESTAMP")]
    [Timestamp]
    public byte[] Timestamp { get; set; }

با موارد زیر جایگزین می‌کنیم:


    [ConcurrencyCheck]
    [Column("VERSIONING")]
    public int? Versioning { get; set; }

نوع ستون را از byte[] به int? تغییر می‌دهیم. در SGBD، از پروسیجرهای ذخیره‌شده استفاده خواهیم کرد تا هر بار که یک سطر درج یا اصلاح می‌شود، این عدد صحیح را یک واحد افزایش دهیم.

ما این تغییر را در هر چهار موجودیت اعمال می‌کنیم و سپس برنامه را مجدداً اجرا می‌کنیم. سپس خطای زیر را دریافت می‌کنیم:

1
2
3
4
5
6
Exception non gérée : System.Data.DataException: An exception occurred while initializing the database. See the InnerException for details. ---> System.Data.ProviderIncompatibleException: DeleteDatabase n'est pas pris en charge par le fournisseur.
   à System.Data.Common.DbProviderServices.DbDeleteDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
   ...
   à System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\d
vp\Entity Framework\RdvMedecins\RdvMedecins-Oracle-01\CreateDB_01.cs:ligne 15

خط ۱ نشان می‌دهد که کانکتور ADO.NET برای PostgreSQL قادر به حذف پایگاه داده موجود نیست. همانند اوراکل. بنابراین، ما ملزم به ایجاد پایگاه داده [RDVMEDECINS-EF] به صورت دستی با استفاده از ابزار [EMS Manager for PostgreSQL] هستیم. ما تمام مراحل را شرح نمی‌دهیم، بلکه فقط مهم‌ترین آن‌ها را توضیح می‌دهیم.

پایگاه داده PostgreSQL به شرح زیر خواهد بود:

جدول‌ها

  • در [1]، ID کلید اصلی از نوع serial است. این نوع، PostgreSQL، یک عدد صحیح است که به طور خودکار توسط SGBD تولید می‌شود.

جدول‌های مختلف دارای کلیدهای اصلی و خارجی مشابه جدول‌های مثال‌های قبلی هستند. کلیدهای خارجی دارای ویژگی‌های ON، DELETE و CASCADE هستند.

پیوسته‌ها

مانند اوراکل، در اینجا توالی‌ها ایجاد کرده‌ایم. این‌ها تولیدکننده‌های اعداد متوالی هستند. تعداد آن‌ها پنج عدد است: [1].

  • در [2]، می‌توانیم ویژگی‌های دنباله [CLIENTS_ID_SEQ] را مشاهده کنیم. این دنباله اعداد متوالی را با گام ۱، از ۱ شروع کرده و تا یک مقدار بسیار بزرگ تولید می‌کند.

تمام توالی‌ها بر اساس یک مدل یکسان ساخته شده‌اند.

  • [CLIENTS_ID_seq] برای تولید کلید اصلی جدول [CLIENTS] استفاده خواهد شد؛
  • [MEDECINS_ID_seq] برای تولید کلید اصلی جدول [MEDECINS] استفاده خواهد شد؛
  • [CRENEAUX_ID_seq] برای تولید کلید اصلی جدول [CRENEAUX] استفاده خواهد شد؛
  • [RVS_ID_seq] برای تولید کلید اصلی جدول [RVS] استفاده خواهد شد؛
  • [sequence_versions] برای تولید مقادیر ستون‌های [VERSIONING] در تمام جداول استفاده خواهد شد.

تریگرها

یک تله (Trigger) روندی است که توسط SGBD قبل یا بعد از یک رویداد (درج، به‌روزرسانی، حذف) در یک جدول اجرا می‌شود. ما چهار تله داریم: [1]:

بیایید کد تریگر [CLIENTS_tr] را بررسی کنیم که ستون [VERSIONING] را در جدول [CLIENTS] پر می‌کند:

1
2
3
4
CREATE TRIGGER "CLIENTS_tr"
  BEFORE INSERT OR UPDATE 
  ON public."CLIENTS" FOR EACH ROW 
EXECUTE PROCEDURE public.trigger_versions();
  • خطوط ۱–۳: قبل از هر عملیات INSERT یا UPDATE روی جدول [CLIENTS
  • خط ۴: رویه [public.trigger_versions()] اجرا می‌شود.

روال [public.trigger_versions()] به شرح زیر است:

1
2
3
4
BEGIN
NEW."VERSIONING":=nextval('sequence_versions');
return NEW;
END
  • ردیف ۲: NEW نمایانگر ردیفی است که باید وارد یا اصلاح شود. NEW. «VERSIONING» ستون [VERSIONING] برای این ردیف است. برای آن مقدار زیر از تولیدکنندهٔ اعداد تصادفی اختصاص داده می‌شود: «sequence_versions». بدین ترتیب، ستون ["VERSIONING"] هر بار که عملیاتی بر روی INSERT / UPDATE در جدول [CLIENTS] انجام می‌شود، تغییر می‌کند.

تریگرهای [MEDECINS_tr, CRENEAUX_tr, RVS_tr] به روشی مشابه عمل می‌کنند. چهار ستون ["VERSIONING"] مقادیر خود را از همان توالی دریافت می‌کنند.

اسکریپت تولید جداول پایگاه داده PostgreSQL و [RDVMEDECINS-EF] در پوشه [RdvMedecins / databases / postgreSQL] قرار داده شده است. خواننده می‌تواند آن را بارگذاری و اجرا کند تا این جداول را ایجاد نماید.

پس از انجام این کار، می‌توان برنامه‌های مختلف پروژه را اجرا کرد. این برنامه‌ها نتایج مشابهی با سرور SQL تولید می‌کنند، به جز برنامه [ModifyDetachedEntities] که به همان دلیلی که در Oracle از کار می‌افتاد، دچار مشکل می‌شود. این مشکل به همان روش حل می‌شود. به‌سادگی برنامه [ModifyDetachedEntities] را از پروژه [RdvMedecins-Oracle-01] به پروژه [RdvMedecins-PostgreSQL-01] کپی کنید.

برنامه [LazyEagerLoading] با خطای زیر از کار می‌افتد:

1
2
3
4
Exception non gérée : System.Data.EntityCommandExecutionException: Une erreur s'est produite lors de l'exécution de la définition de la commande. Pour plus de détails, consultez l'exception interne. ---> Npgsql.NpgsqlException: ERREUR: 42601: erreur de syntaxe sur ou près de « LEFT »
   à Npgsql.NpgsqlState.<ProcessBackendResponses_Ver_3>d__a.MoveNext()
   ...
   à RdvMedecins_01.LazyEagerLoading.Main(String[] args) dans d:\data\istia-1213\c#\dvp\Entity Framework\RdvMedecins\RdvMedecins-PostgreSQL-01\QZXW2HTMLCTGF6eUVhZ2VyTG9hZGluZy.jcwZQX:خط 23

کد خطا به شرح زیر است:


      using (var context = new RdvMedecinsContext())
      {
        // اسلات شماره 0
        creneau = context.Creneaux.Include("Medecin").Single<Creneau>(c => c.Id == idCreneau);
        Console.WriteLine(creneau.ShortIdentity());
}

خط ۱ استثنا: خطای گزارش‌شده نشان‌دهنده یک پیوند (join) است، زیرا LEFT یک کلمه کلیدی پیوند است. از آنجا که خط ۴ کد بالا، بارگذاری فوری وابستگی [Medecin] را از یک موجودیت [Creneau] درخواست می‌کند، EF یک پیوند بین جداول [CRENEAUX] و [MEDECINS] برقرار کرد. با این حال، به نظر می‌رسد که کانکتور ADO.NET یک ترتیب نادرست SQL تولید کرده است. ما کد را به شرح زیر بازنویسی می‌کنیم:


      using (var context = new RdvMedecinsContext())
      {
        // اسلات شماره 0
        creneau = context.Creneaux.Find(idCreneau);
        Console.WriteLine(creneau.ShortIdentity());
        // پزشک مربوطه مجبور به بارگذاری می‌شود
        // این امکان‌پذیر است زیرا هنوز در یک زمینهٔ باز هستیم
        Medecin medecin = creneau.Medecin;
}
  • خط ۴: اسلات را بدون جویِن بازیابی می‌کنیم؛
  • خط ۸: وابستگی گمشده را بازیابی می‌کنیم.

این کار می‌کند. بار دیگر می‌توانیم ببینیم که تغییر در SGBD بر کد تأثیر گذاشته است. در واقع، مشکل اینجا SGBD نیست، بلکه کانکتور آن ADO.NET است.

6.3. معماری چندلایه مبتنی بر EF 5

به مطالعه موردی خود که در پاراگراف ۲ توصیف شده است بازمی‌گردیم.

ما با ساخت لایه دسترسی به داده [DAO] شروع می‌کنیم. برای این کار، پروژهٔ کنسول VS 2012 [RdvMedecins-SqlServer-02] را در [RdvMedecins-PostgreSQL-02] [1] کپی می‌کنیم:

  • به [2]، و پروژه [RdvMedecins-SqlServer-02] را حذف کنید؛
  • در [3]، یک پروژه موجود به راه‌حل اضافه می‌شود. این پروژه از پوشه [RdvMedecins-PostgreSQL-02] که به تازگی ایجاد شده است، گرفته شده است؛
  • در [4]، پروژه جدید همان نام پروژه‌ای را دارد که حذف شده بود. ما آن را نام‌گذاری مجدد خواهیم کرد؛
  • در [5]، نام پروژه را تغییر داده‌ایم؛
  • در [6]، ما در حال تغییر برخی از ویژگی‌های آن هستیم، مانند نام assembly در اینجا؛
  • به [7]؛ پوشه [Models] حذف شده و با پوشه [Models] از پروژه [RdvMedecins-PostgreSQL-01] جایگزین می‌شود. این به این دلیل است که این دو پروژه از همان قالب‌ها استفاده می‌کنند.
  • در [8]، پروژهٔ جاری ارجاع می‌دهد؛
  • در [9]، کانکتور ADO.NET از PostgreSQL با استفاده از ابزار NuGet اضافه شده است.

در فایل [App.config]، اطلاعات پایگاه داده SQL با اطلاعات پایگاه داده PostgreSQL جایگزین می‌شود. این اطلاعات را می‌توان در فایل [App.config] در پروژه [RdvMedecins-PostgreSQL-01] یافت:


  <!--رشته اتصال به پایگاه داده -->
  <connectionStrings>
    <add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
  </connectionStrings>
  <!-- ارائه‌دهندهٔ کارخانه -->
  <system.data>
    <DbProviderFactories>
      <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
    </DbProviderFactories>
</system.data>

ابجکت‌های مدیریت‌شده توسط Spring نیز در حال تغییر هستند. در حال حاضر، ما داریم:


  <!--پیکربندی Spring -->
  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object id="rdvmedecinsDao" type="RdvMedecins.Dao.Dao,RdvMedecins-SqlServer-02" />
    </objects>
</spring>

خط ۷ به اسمبلی پروژه [RdvMedecins-SqlServer-02] اشاره دارد. اسمبلی اکنون [RdvMedecins-PostgreSQL-02] است.

با انجام این کار، ما آماده‌ایم تا تست لایه [DAO] را اجرا کنیم. قبل از انجام این کار، باید اطمینان حاصل کنیم که پایگاه داده پر شده است (برنامه [Fill] از پروژه [RdvMedecins-PostgreSQL-01]). برنامه تست با خطای زیر از کار می‌افتد:

2012/10/12 13:56:27:188 [INFO]  Spring.Context.Support.XmlApplicationContext - A
pplicationContext Refresh: Completed
Liste des clients :
Client [47,Mr,Jules,Martin,468]
Client [48,Mme,Christine,German,469]
Client [49,Mr,Jules,Jacquard,470]
Client [50,Melle,Brigitte,Bistrou,471]
Liste des médecins :
Medecin [42,Mme,Marie,Pelissier,472]
Medecin [43,Mr,Jacques,Bromard,497]
Medecin [44,Mr,Philippe,Jandot,510]
Medecin [45,Melle,Justine,Jacquemot,511]
L'erreur suivante s'est produite : RdvMedecinsException[3,GetCreneauxMedecin,Une erreur s'est produite lors de l'exécution de la définition de la commande. Pour plus de détails, consultez l'exception interne.]

خط ۱۳: پیام نشان می‌دهد که خطا در متد [GetCreneauxMedecin] از لایه [DAO] رخ داده است. این متد به شرح زیر است:


    // فهرست اسلات‌های قرار ملاقات برای یک پزشک مشخص
    public List<Creneau> GetCreneauxMedecin(int idMedecin)
    {
      // فهرست اسلات‌های قرار ملاقات
      try
      {
        // باز کردن زمینه پایداری
        using (var context = new RdvMedecinsContext())
        {
          // بازیابی پزشک و نوبت‌های وی
          Medecin medecin = context.Medecins.Include("Creneaux").Single(m => m.Id == idMedecin);
          // فهرست نوبت‌های ملاقات پزشک را بازمی‌گرداند
          return medecin.Creneaux.ToList<Creneau>();
        }
      }
      catch (Exception ex)
      {
        throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
      }
}

در خط ۱۱، ما کلیدواژه Include را می‌بینیم که قبلاً باعث از کار افتادن برنامه شده است. کد قبلی را می‌توان با موارد زیر جایگزین کرد:


    // فهرست بازه‌های زمانی برای یک پزشک مشخص
    public List<Creneau> GetCreneauxMedecin(int idMedecin)
    {
      // فهرست بازه‌های زمانی
      try
      {
        // باز کردن زمینه پایداری
        using (var context = new RdvMedecinsContext())
        {
          // فهرست اسلات‌های قرار ملاقات دکتر را بازمی‌گرداند
          return context.Creneaux.Where(c => c.MedecinId == idMedecin).ToList<Creneau>(); 
        }
      }
      catch (Exception ex)
      {
        throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
      }
}

کد جدید حتی منسجم‌تر از کد قدیمی به نظر می‌رسد. در هر صورت، این بار برنامهٔ آزمایشی با موفقیت اجرا می‌شود.

ما فایل DLL پروژه را به همان روشی که برای پروژه [RdvMedecins-SqlServer-02] انجام دادیم، ایجاد می‌کنیم و جمع‌آوری می‌کنیمتمام فایل‌های DLL پروژه را در پوشه‌ای به نام [lib] که در داخل [RdvMedecins-PostgreSQL-02] ایجاد شده است، کامپایل می‌کنیم. این‌ها ارجاعات برای پروژه وب [RdvMedecins-PostgreSQL-03] بعدی خواهند بود.

  

اکنون آماده ساخت لایه [ASP.NET] برنامه خود هستیم:

ما با پروژه [RdvMedecins-SqlServer-03] شروع خواهیم کرد. ما پوشه این پروژه را در [RdvMedecins-PostgreSQL-03] و [1] کپی خواهیم کرد:

  • به [2]، با استفاده از VS 2012 Express for the Web، راه‌حل را در پوشه [RdvMedecins-PostgreSQL-03] باز می‌کنیم؛
  • در [3]، نام راه‌حل و نام پروژه را هر دو تغییر می‌دهیم؛
  • در [4]، پروژه‌های جاری ارجاع داده شده‌اند؛
  • در [5]، آن‌ها را حذف می‌کنیم؛
  • آنها را با ارجاع به فایل‌های DLL که به تازگی در پروژه‌ی [RdvMedecins-PostgreSQL-02] و در پوشه‌ای به نام [lib] ذخیره کرده‌ایم، جایگزین می‌کنیم.

تنها کاری که باقی مانده، ویرایش فایل [Web.config] است. ما محتوای فعلی آن را با محتوای فایل [App.config] از پروژه [RdvMedecins-PostgreSQL-02] جایگزین می‌کنیم. پس از انجام این کار، پروژه وب را اجرا می‌کنیم. کار می‌کند. قبل از اجرای برنامه وب، فراموش نکنید که پایگاه داده را پر کنید.