Skip to content

6. PostgreSQL 9.2.1 ile vaka çalışması

6.1. Araçların kurulumu

Yüklenecek araçlar şunlardır:

  • SGBD: [http://www.enterprisedb.com/products-services-training/pgdownload#windows];
  • bir yönetim aracı: EMS SQL Manager for PostgreSQL Freeware [http://www.sqlmanager.net/fr/products/postgresql/manager/download].

Aşağıdaki örneklerde, postgres kullanıcısının şifresi postgres'tir.

PostgreSQL'i çalıştıralım, ardından SGBD'i yönetmek için kullanacağımız [SQL Manager Lite for PostgreSQL] aracını da çalıştıralım.

  • [1]'te, Windows hizmetleri üzerinden SGBD ve PostgreSQL'i başlatıyoruz;
  • [2]'te hizmet başlatılır;

Şimdi, SGBD ve [3]'i yönetmek için [SQL Manager Lite for MySQL] aracını başlatıyoruz.

  • [4]'te yeni bir veritabanı oluşturuyoruz;
  • [5]'te veritabanının adını belirtiyoruz;
  • [5]'te, postgres / postgres olarak oturum açıyoruz;
  • [6]'te bazı bilgileri giriyoruz;
  • [7]'te, yürütülmek üzere olan SQL emrini onaylıyoruz;
  • [8]'te veritabanı oluşturulmuştur. Şimdi [EMS Manager]'te kaydedilmesi gerekir. Bilgiler doğrudur. [OK] komutunu çalıştırıyoruz;
  • [9]'e bağlanıyoruz;
  • [10]'te, [EMS Manager] veritabanını görüntüler; veritabanı şimdilik boştur. Tabloların public [11] adlı şemaya ait olacağına dikkat edilmelidir.

Şimdi bir VS 2012 projesini bu veritabanına bağlayacağız.

6.2. Varlıklardan veritabanı oluşturma

İlk olarak, [RdvMedecins-SqlServer-01] proje klasörünü [RdvMedecins-PostgreSQL-01] ve [1] klasörlerine kopyalayacağız:

  • [2]'e kopyalıyoruz; VS 2012'de ise [RdvMedecins-SqlServer-01] projesini çözümden siliyoruz;
  • [3]'te proje silinmiştir;
  • [4]'te, başka bir proje daha ekliyoruz. Bu proje, daha önce oluşturduğumuz [RdvMedecins-PostgreSQL-01] klasöründen alınmıştır;
  • [5]'te yüklenen projenin adı [RdvMedecins-SqlServer-01]'tir;
  • [6]'e, adını [RdvMedecins-PostgreSQL-01] olarak değiştiriyoruz;
  • [7]'te, çözüme başka bir proje eklenir. Bu proje, daha önce çözümden sildiğimiz projenin [RdvMedecins-SqlServer-01] klasöründen alınır;
  • [8]'te, [RdvMedecins-SqlServer-01] projesi çözüme yeniden eklendi.

[RdvMedecins-PostgreSQL-01] projesi, [RdvMedecins-SqlServer-01] projesiyle aynıdır. Birkaç değişiklik yapmamız gerekiyor. [App.config]'te bağlantı dizesini değiştireceğiz ve [DbProviderFactory]'i her bir SGBD'e uyarlamamız gerekiyor.


<!-- veritabanı bağlantı dizesi -->
  <connectionStrings>
    <add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
  </connectionStrings>
  <!-- fabrika sağlayıcısı -->
  <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>
  • 3. satır: kullanıcı adı ve şifre;
  • 7-9. satırlar: DbProviderFactory. 8. satır, elimizde olmayan bir DLL [Npgsql] dosyasına atıfta bulunuyor. Bunu NuGet ve [1] ile elde edebiliriz:
  • [2]'e, arama alanına postgresql anahtar kelimesini yazın;
  • [3]'te, [Npgsql] paketini seçin. Bu, ADO.NET için bir bağlayıcıdır;
  • [4]'te iki referans eklenmiştir;
  • [5]'te, [App.config]'te, DLL'in doğru sürümünü girmek gerekir. Bu bilgi, özellikler bölümünde bulunur.

[Entites.cs] dosyasında, oluşturulacak tabloların şemasını uyarlamak gerekir:


  [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
  {...}

Daha önce PostgreSQL veritabanını oluştururken, tabloların public adlı bir şemaya ait olduğunu görmüştük.

Projenin yürütülmesini yapılandırıyoruz:

  • [1]'te, oluşturulacak derlemeye başka bir ad veriyoruz;
  • [2]'te, varsayılan olarak başka bir ad alanı belirledik;
  • [3]'te, çalıştırılacak programı belirliyoruz.

Bu aşamada derleme hatası yok. [CreateDB_01] programını çalıştıralım. Aşağıdaki istisna ile karşılaşılır:

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 ve Oracle ile aynı hatayı aldığımızı hatırlıyoruz. Bu, varlıkların Timestamp alanının türüyle ilgilidir. Oracle'da yaptığımızın aynısını yapıyoruz. Varlıklarda, şu üç satırı değiştiriyoruz:


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

satırlarını şu satırlarla değiştiriyoruz:


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

Sütunun türünü byte[]'den int? olarak değiştiriyoruz. SGBD'te, bir satır eklendiğinde veya değiştirildiğinde bu tamsayıyı bir birim artırmak için depolanmış yordamlar kullanacağız.

Dört varlık için de yukarıdaki değişikliği yapıp uygulamayı yeniden çalıştırıyoruz. Bunun üzerine şu hata mesajıyla karşılaşıyoruz:

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

1. satır, PostgreSQL'in ADO.NET konektörünün mevcut veritabanını silemediğini gösteriyor. Tıpkı Oracle'da olduğu gibi. Bu durumda, [EMS Manager for PostgreSQL] aracını kullanarak [RDVMEDECINS-EF] veritabanını manuel olarak oluşturmamız gerekiyor. Tüm adımları değil, sadece en önemli olanları açıklayacağız.

PostgreSQL veritabanı şu şekilde olacaktır:

Tablolar

  • [1]'te, ID, serial türünde bir birincil anahtardır. Bu PostgreSQL türü, SGBD tarafından otomatik olarak oluşturulan bir tamsayıdır.

Farklı tablolar, önceki örneklerdeki aynı tabloların sahip olduğu birincil ve yabancı anahtarlara sahiptir. Yabancı anahtarlar, ON, DELETE ve CASCADE özniteliklerine sahiptir.

Sıralamalar

Oracle'da olduğu gibi, burada da sıralama dizileri oluşturduk. Bunlar ardışık sayı üreten generatörlerdir. Toplamda 5 tane vardır: [1].

  • [2]'te, [CLIENTS_ID_SEQ] dizisinin özelliklerini görüyoruz. Bu dizi, 1'den başlayarak çok büyük bir değere kadar 1'er 1'er artan ardışık sayılar üretir.

Tüm diziler aynı model üzerine kurulmuştur.

  • [CLIENTS_ID_seq], [CLIENTS] tablosunun birincil anahtarını oluşturmak için kullanılacak;
  • [MEDECINS_ID_seq], [MEDECINS] tablosunun birincil anahtarını oluşturmak için kullanılacaktır;
  • [CRENEAUX_ID_seq], [CRENEAUX] tablosunun birincil anahtarını oluşturmak için kullanılacaktır;
  • [RVS_ID_seq], [RVS] tablosunun birincil anahtarını oluşturmak için kullanılacaktır;
  • [sequence_versions], tüm tabloların [VERSIONING] sütunlarındaki değerleri oluşturmak için kullanılacaktır.

Tetikleyiciler

Tetikleyici, bir tabloda bir olay (Ekleme, Değiştirme, Silme) öncesinde veya sonrasında SGBD tarafından yürütülen bir yordamdır. Elimizde 4 adet [1] tetikleyicisi bulunmaktadır:

[CLIENTS] tablosunun [VERSIONING] sütununu dolduran [CLIENTS_tr] tetikleyicisinin DDL koduna bakalım:

1
2
3
4
CREATE TRIGGER "CLIENTS_tr"
  BEFORE INSERT OR UPDATE 
  ON public."CLIENTS" FOR EACH ROW 
EXECUTE PROCEDURE public.trigger_versions();
  • 1-3. satırlar: [CLIENTS] tablosunda her INSERT veya UPDATE işlemi öncesinde;
  • 4. satır: [public.trigger_versions()] prosedürü yürütülür.

[public.trigger_versions()] prosedürü şu şekildedir:

1
2
3
4
BEGIN
NEW."VERSIONING":=nextval('sequence_versions');
return NEW;
END
  • 2. satır: NEW, eklenecek veya değiştirilecek satırı temsil eder. NEW. "VERSIONING", bu satırın [VERSIONING] sütunudur. Buna, sayı üretecinden "sequence_versions" değeri atanır. Böylece, [CLIENTS] tablosunda her INSERT / UPDATE işlemi yapıldığında ["VERSIONING"] sütunu değişir.

[MEDECINS_tr, CRENEAUX_tr, RVS_tr] tetikleyicileri de benzer şekilde çalışır. Dört ["VERSIONING"] sütunu, değerlerini aynı diziden alır.

PostgreSQL ve [RDVMEDECINS-EF] veritabanı tablolarını oluşturan komut dosyası, [RdvMedecins / databases / postgreSQL] klasörüne yerleştirilmiştir. Kullanıcı, tablolarını oluşturmak için bu dosyayı yükleyip çalıştırabilir.

Bu işlem tamamlandıktan sonra, projenin çeşitli programları çalıştırılabilir. Bu programlar, SQL Sunucusu ile aynı sonuçları verir; ancak [ModifyDetachedEntities] programı, Oracle ile çalışırken çöktüğü aynı nedenden dolayı çöker. Bu sorun da aynı şekilde çözülür. [RdvMedecins-Oracle-01] projesindeki [ModifyDetachedEntities] programını [RdvMedecins-PostgreSQL-01] projesine kopyalamak yeterlidir.

[LazyEagerLoading] programı şu istisna ile çöküyor:

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\LazyEagerLoading.cs:satır 23

Hatalı kod şöyledir:


      using (var context = new RdvMedecinsContext())
      {
        // 0 numaralı zaman aralığı
        creneau = context.Creneaux.Include("Medecin").Single<Creneau>(c => c.Id == idCreneau);
        Console.WriteLine(creneau.ShortIdentity());
}

İstisnanın 1. satırında bildirilen hata, bir birleştirme işlemini akla getiriyor çünkü LEFT, birleştirme işleminin anahtar kelimesidir. Yukarıdaki kodun 4. satırı, [Creneau] varlığının [Medecin] bağımlılığının hemen yüklenmesini talep ettiğinden, EF, [CRENEAUX] ve [MEDECINS] tabloları arasında bir birleştirme işlemi gerçekleştirmiştir. Ancak ADO.NET konektörünün hatalı bir SQL komutu oluşturduğu görülüyor. Kodu şu şekilde yeniden yazıyoruz:


      using (var context = new RdvMedecinsContext())
      {
        // zaman aralığı no. 0
        creneau = context.Creneaux.Find(idCreneau);
        Console.WriteLine(creneau.ShortIdentity());
        // ilişkili doktorun yüklenmesi zorlanıyor
        // bu mümkün çünkü hâlâ açık bir bağlamdayız
        Medecin medecin = creneau.Medecin;
}
  • 4. satır: birleştirme olmadan zaman aralığını arıyoruz;
  • 8. satır: eksik bağımlılığı alıyoruz.

Çalışıyor. Yine, SGBD'teki değişikliğin kod üzerinde bir etkisi olduğunu görüyoruz. Aslında burada sorun olan SGBD değil, onun konektörü ADO.NET'tir.

6.3. EF 5'e dayalı çok katmanlı mimari

2. paragrafta açıklanan vaka çalışmamıza geri dönüyoruz.

Öncelikle, veri erişim katmanı olan [DAO]'i oluşturacağız. Bunu yapmak için, VS 2012 konsol projesini [RdvMedecins-SqlServer-02]'te [RdvMedecins-PostgreSQL-02] ve [1] olarak çoğaltıyoruz:

  • [2]'e kopyaladıktan sonra, [RdvMedecins-SqlServer-02] projesini siliyoruz;
  • [3]'e, çözüme mevcut bir proje eklenir. Bu proje, az önce oluşturulan [RdvMedecins-PostgreSQL-02] klasöründen alınır;
  • [4]'te, yeni projenin adı silinen projenin adını taşıyor. Adını değiştireceğiz;
  • [5]'te, projenin adını değiştirdik;
  • [6]'e, bazı özelliklerini değiştiriyoruz, örneğin burada derleme adını;
  • [7] olarak, [Models] klasörü silinip, [RdvMedecins-PostgreSQL-01] projesindeki [Models] klasörü ile değiştiriliyor. Aslında, iki proje de aynı şablonları paylaşıyor.
  • [8]'te, projenin mevcut referansları;
  • [9]'te, PostgreSQL'in ADO.NET konektörü, NuGet aracı kullanılarak eklenmiştir.

[App.config] dosyasında, SQL Sunucusu veritabanındaki bilgiler, PostgreSQL veritabanındaki bilgilerle değiştirilir. Bu bilgiler, [RdvMedecins-PostgreSQL-01] projesindeki [App.config] dosyasında bulunur:


  <!-- veritabanına bağlantı dizisi -->
  <connectionStrings>
    <add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
  </connectionStrings>
  <!-- fabrika sağlayıcısı -->
  <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 tarafından yönetilen nesneler de değişiyor. Şu anda elimizde şunlar var:


  <!-- Spring yapılandırması -->
  <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>

7. satır, [RdvMedecins-SqlServer-02] projesinin derlemesine atıfta bulunmaktadır. Derleme artık [RdvMedecins-PostgreSQL-02]'tir.

Bu işlem tamamlandıktan sonra, [DAO] katmanının testini çalıştırmaya hazırız. Öncelikle, [RdvMedecins-PostgreSQL-01] projesindeki [Fill] programını kullanarak veritabanını doldurmamız gerekiyor. Test programı aşağıdaki istisna ile çöküyor:

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.]

13. satırda, hatanın [DAO] katmanındaki [GetCreneauxMedecin] yönteminde oluştuğu belirtilmektedir. Bu yöntem şöyledir:


    // belirli bir doktorun randevu saatleri listesi
    public List<Creneau> GetCreneauxMedecin(int idMedecin)
    {
      // randevu aralıkları listesi
      try
      {
        // kalıcılık bağlamının açılması
        using (var context = new RdvMedecinsContext())
        {
          // doktor ve randevu aralıkları alınır
          Medecin medecin = context.Medecins.Include("Creneaux").Single(m => m.Id == idMedecin);
          // doktorun randevu aralıkları listesi döndürülür
          return medecin.Creneaux.ToList<Creneau>();
        }
      }
      catch (Exception ex)
      {
        throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
      }
}

11. satırda, daha önce bir programın çökmesine neden olan Include anahtar sözcüğü görülmektedir. Önceki kod, aşağıdakiyle değiştirilebilir:


    // belirli bir doktorun randevu aralıkları listesi
    public List<Creneau> GetCreneauxMedecin(int idMedecin)
    {
      // randevu aralıkları listesi
      try
      {
        // kalıcılık bağlamı açılır
        using (var context = new RdvMedecinsContext())
        {
          // doktorun randevu aralıklarının listesi döndürülür
          return context.Creneaux.Where(c => c.MedecinId == idMedecin).ToList<Creneau>(); 
        }
      }
      catch (Exception ex)
      {
        throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
      }
}

Yeni kod, eskisinden daha tutarlı görünüyor. Her neyse, bu sefer test programı başarılı oldu.

[RdvMedecins-SqlServer-02] projesi için yapıldığı gibi, projenin DLL dosyasını oluşturuyoruz veprojenin tüm DLL dosyalarını, [RdvMedecins-PostgreSQL-02] içinde oluşturulan [lib] klasöründe topluyoruz. Bunlar, takip edecek olan [RdvMedecins-PostgreSQL-03] web projesinin referansları olacak.

  

Artık uygulamamızın [ASP.NET] katmanını oluşturmaya hazırız:

[RdvMedecins-SqlServer-03] projesinden başlayacağız. Bu projenin klasörünü [RdvMedecins-PostgreSQL-03] ve [1] olarak çoğaltıyoruz:

  • [2]'e kopyalayacağız; VS 2012 Express for the Web ile [RdvMedecins-PostgreSQL-03] klasöründeki çözümü açacağız;
  • [3]'te hem çözümün adını hem de projenin adını değiştiriyoruz;
  • [4]'te, projenin mevcut referanslarını;
  • [5]'te bunları siliyoruz;
  • [6] olarak değiştirip, bunları az önce [RdvMedecins-PostgreSQL-02] projesinin [lib] klasörüne kaydettiğimiz DLL dosyasına yapılan referanslarla değiştiriyoruz.

Geriye sadece [Web.config] dosyasını değiştirmek kalıyor. Mevcut içeriğini, [RdvMedecins-PostgreSQL-02] projesindeki [App.config] dosyasının içeriğiyle değiştiriyoruz. Bu işlem tamamlandıktan sonra web projesini çalıştırıyoruz. Çalışıyor. Web uygulamasını çalıştırmadan önce veritabanını doldurmayı unutmayalım.