Showing posts with label EF6. Show all posts
Showing posts with label EF6. Show all posts

Thursday, December 13, 2018

ProjectBase ile Transaction Üzerinden Dış Veri Erişim Yapıları ile Entegrasyon (Entity Framework 6 Örneği)

ProjectBase (PB) kütüphanesi veri erişim mantalitesi olarak ORM (Object Relational Mapping) ile provider seviyesi arasında bir konumda bulunmaktadır. PB dış bir veri erişim yapısı ile beraber kullanılmak istendiğinde ortak transaction kullanım gerekliliği doğabilmektedir. Örneğin ORM olarak Entity Framework 6 (EF) kullanıyoruz ve bazı işlemleri EF ile bazılarını PB ile yapacağız fakat bu işlemlerin aynı transaction üzerinde yapılması gerekmekte. Bu durumda EF üzerinde aşağıdaki kod ile bir transaction başlatabiliriz:

DataBaseContext context = new DataBaseContext();
DbTransaction transaction =  context.Database.BeginTransaction().UnderlyingTransaction

Burada elde ettiğimiz transaction nesnesi veri tabanı bağlantısı açık ve işlem yapmaya hazır olan bir nesnedir. Bu işlemden sonra bu transaction nesnesini PB de kullanabiliriz:

IDatabase2 db = DatabaseFactory.GetDbObject(DbSettings.TransactionMode);
db.UseExternalTransaction(transaction);

Buradan sonra PB ile yaptığımız işlemler EF ile oluşturduğumuz transaction üzerinden yapılacaktır. Normal EF kullanımından farklı olarak EF 6'da transaction bu yazıda bahsettiğimiz yöntemle başlatıldığında Commit edilmesi gerekmektedir:

context.Save();
context.Database.CurrentTransaction.Commit();

Bu işlem sırasında aşağıdaki hususlara dikkat edilmesi gerekir:

1. Dışarıda oluşturulan transaction geçerli bir durumda olmalı ve veri tabanı bağlantısı açık olmalıdır.
2. PB ile Commit veya RollBack yapıldığında PB sıralı yeni işlemleri yeni transaction kullanarak yapacaktır. Yani mevcut transaction geçerliliğini yitirecektir.
3. Bu işlem yapılırken PB transaction modda çalışır durumda olmalıdır.

Saturday, November 11, 2017

FluentGenerator kullanarak Oracle Entity Framework 6.x sınıflarının oluşturulması - Generating Oracle Entity Framework classes with FluentGenerator

FluentGenerator (FG) Entity Framework 6.x (EF) code first örüntüsüne göre sınıfların oluşturulması için yazılmış bir masaüstü uygulamasıdır. Oluşturulan sınıflar "fluent" mimariye göre oluşturulur ve POCO ve bağlantı (mapping) sınıfları ayrı ayrı oluşturulur.

FG, ProjectBase (PB) kütüphanesini kullandığı için uygulamanın çalıştırılması için PB' nin eklenmesi ve provider ayarlamalarının yapılması gerekir. Eğer Oracle unmanaged provider kullanılıyorsa "Oracle Client" kurulmalı ve ilgili ayarlamalar yapılmalıdır.

FG ve PB'yi aşağıdaki adresten indirebilirsiniz:

https://github.com/vyigity

Test veri tabanı olarak Oracle 11g express edition ve HR şeması içinde yer alan ögeler kullanılacaktır.



FG çalıştırıldığında config ayarlamalarına göre bağlantı cümleciği arayüzde görünecektir. Cümlecik değiştirilmek istenirse istenen yeni cümlecik girilir ve "CHANGE" tuşu ile yeni cümlecik geçerli hale getirilir. Namespace ismini kendi projenize göre belirledikten sonra "DIR" tuşu ile EF sınıflarının hangi klasör içinde oluşturulacağı seçilmelidir. Capitalization kısmında sınıf isimlerinin nasıl formatlanacağı seçilebilmektedir. Bu örnekte "Pascal" kullanılacaktır. Üstte yer alan TABLE_NAME kısmında tablo ismine göre, alttaki VIEW_NAME kısmından view ismine göre arama yapılabilir.

Örnek olarak görselde de görüleceği üzere "Employees" tablosuna ilişkin sınıfları oluşturmak istiyoruz. Arama kısmına "EMPL" yazarak istediğimiz tabloya ulaştık ve yön tuşları ile tabloyu sağ tarafta yer alan grid içine alarak bu tablonun oluşturulacağını belirttik. Son olarak "GENERATE" tuşu nu kullandığımızda önceden seçtiğimiz klasör içinde sınıflar aşağıdaki gibi oluşturulacaktır:

.\Generate\Context.cs

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using YourNameSpace.Models.Mapping;

namespace YourNameSpace.Models
{
    public partial class DatabaseContext : DbContext
    {
        static DatabaseContext()
        {
            Database.SetInitializer<DatabaseContext>(null);
        }

        public DatabaseContext()
            : base("Name=Context")
        {
        }

        public DbSet<Employees> Employeess { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new EmployeesMap());
        }
    }
}

Context sınıfı EF'nin temel sınıfıdır ve FG foreign key (FK) ilişkileri oluşturmadığı için bu konuyla ilgili kodlar elden bu sınıf içine eklenmelidir. Map sınıfları "OnModelCreating" fonksiyonu içine yukarıdaki gibi eklenmeli ve DbSet tanımlaması sonraki eklentiler için elden yapılmalıdır.

.\Generate\Employees.cs

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

namespace YourNameSpace.Models
{
    public partial class Employees
    {
        public int EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string PhoneNumber { get; set; }
        public DateTime HireDate { get; set; }
        public string JobId { get; set; }
        public Nullable<decimal> Salary { get; set; }
        public Nullable<decimal> CommissionPct { get; set; }
        public Nullable<int> ManagerId { get; set; }
        public Nullable<short> DepartmentId { get; set; }

    }
}

Yukarıda EF Employees POCO sınıfının nasıl göründüğü verilmiştir.

.\Generate\Mapping\EmployeesMap.cs

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;

namespace YourNameSpace.Models.Mapping
{
    public class EmployeesMap : EntityTypeConfiguration<Employees>
    {
        public EmployeesMap()
        {
            // Primary Key
            this.HasKey(t => new { t.EmployeeId });

            // Properties
            this.Property(t => t.EmployeeId)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

            this.Property(t => t.FirstName)
                .HasMaxLength(20);

            this.Property(t => t.LastName)
                .IsRequired()
                .HasMaxLength(25);

            this.Property(t => t.Email)
                .IsRequired()
                .HasMaxLength(25);

            this.Property(t => t.PhoneNumber)
                .HasMaxLength(20);

            this.Property(t => t.JobId)
                .IsRequired()
                .HasMaxLength(10);

            // Table & Column Mappings
            this.ToTable("EMPLOYEES", "HR");
            this.Property(t => t.EmployeeId).HasColumnName("EMPLOYEE_ID");
            this.Property(t => t.FirstName).HasColumnName("FIRST_NAME");
            this.Property(t => t.LastName).HasColumnName("LAST_NAME");
            this.Property(t => t.Email).HasColumnName("EMAIL");
            this.Property(t => t.PhoneNumber).HasColumnName("PHONE_NUMBER");
            this.Property(t => t.HireDate).HasColumnName("HIRE_DATE");
            this.Property(t => t.JobId).HasColumnName("JOB_ID");
            this.Property(t => t.Salary).HasColumnName("SALARY");
            this.Property(t => t.CommissionPct).HasColumnName("COMMISSION_PCT");
            this.Property(t => t.ManagerId).HasColumnName("MANAGER_ID");
            this.Property(t => t.DepartmentId).HasColumnName("DEPARTMENT_ID");
        }
    }
}

EmployeesMap sınıfı EF haritalama ve bazı validation kodlarını içeren sınıftır. Validation kodlarının burada bulunması zorunlu değildir. İstenmesi durumunda etiket (Attribute) usulü de uygulanabilir. Bütün sınıfları projenize eklediğinizde EF ile ilgili veri tabanı nesneleri üzerinde işlem yapmaya başlayabilirsiniz.

Monday, February 1, 2016

Entity Framework CRUD İşlemleri - CRUD with Entity Framework

Bir yazılımın en önemli kısımlarından biri CRUD (Create, Read, Update, Delete) işlemleridir. CRUD işlemlerinin kalitesi veri kalitesine doğrudan etki eder. Bu yüzden CRUD uygulamaları özentili ve sert protokolle oluşturulmalıdır. Bu yazıda EF ile ilgili fonksiyonların nasıl olması gerektiği anlatılmıştır. Bu fonksiyonlar şunlardır:

public MyObject SelectObj(Decimal? ID)
     {
         if (ID != null)
         {
             DataBaseContext con = new DataBaseContext();
             return con.MyObjects.Include(r => r.MyObjectRelation).Single(r => r.ID == ID);
         }
         else
             return new MyObject();
     }

     public void InsertObj(MyObject Obj)
     {
         DataBaseContext con = new DataBaseContext();
         var a = con.MyObjects.Attach(Obj);
         con.Entry(a).State = EntityState.Added;
         con.SaveChanges();

     }

     public void UpdateObj(MyObject Obj)
     {
         DataBaseContext con = new DataBaseContext();
         var a = con.MyObjects.Attach(Obj);
         con.Entry(a).State = EntityState.Modified;
         con.SaveChanges();

     }

     public void DeleteObj(MyObject Obj)
     {
         DataBaseContext con = new DataBaseContext();
         var a = con.MyObjects.Attach(Obj);
         con.Entry(a).State = EntityState.Deleted;
         con.SaveChanges();
     }

Burada dikkat edilmesi gereken bir unsur ilişkili tablonun verilerinin "eargerly loading" yaklaşımıyla yapılmasıdır. Bunun direk olarak CRUD kalitesine etkisi olmamakta birlikte tarafımca performans kaygısı sebebiyle bu yöntem kullanılmaktadır.

Aşağıdaki kod satırları nesne durumunu EF'ye tanıtmaktadır.

con.Entry(a).State = EntityState.Added;
con.Entry(a).State = EntityState.Modified;
con.Entry(a).State = EntityState.Deleted;

Bu satırlardan Added nesnenin yeni eklendiğini belirtmekte ve INSERT DML (Data Manipulation Language) komutunu ima etmekte, Modified nesnenin güncellendiğini ve UPDATE DML komutunu ima etmekte, Deleted ise nesnenin silindiğini ve DELETE DML komutunu ima etmektedir.

Direk nesne üzerinden yapılan durum belirtimleri nesnenin tüm propertyleri için geçerlidir. Örneğin Modified durumu sonucu oluşan SQL (Structured Query Language) tüm property'leri içerecektir. Eğer ilgili nesnenin belirli property'leri güncellenmek isteniyorsa aşağıdaki kod satırı kullanılmalıdır:

con.Entry(a).Property(r => r.MyProperty).EntityEntry.State = EntityState.Modified;

veya

con.Entry(a).Property(r => r.MyProperty).IsModified = true;

Burada MyProperty güncellenmiş olarak işaretlenmiş ve güncellemede sadece gösterilen property dikkate alınacaktır. 

Bir diğer husus ise "using" kullanımıdır. "Context" nesneleri yüksek maliyetli nesneler oldukları için kaynakların çabuk bırakılması yararlı olacaktır. Bu yüzden "Context" nesnelerinin "using" bloğu kullanılarak kullanılması yararlı olacaktır.

Son olarak yukarıdaki işlemlerde nesne tanımlayıcısının yani "primary key" alanlarının kesinlikle tam olması şarttır.



Monday, January 11, 2016

Entity Framework 6 Veri Tabanı Fonksiyonu Haritalama (Önce Kod Örüntüsü) - Entity Framework 6 Database Function Mapping (Code First Pattern)

Entity Framework 6 (EF) ile beraber yeni genel "Conventions" özelliği ile artık "Code First" örüntüsü kullanarak EF meta bilgisine müdahele edilebilmektedir. Aşağıdaki örnekte Oracle içinde yer alan TO_CHAR ve TO_NUMBER fonksiyonları haritalanmıştır.

public class FunctionConvention : IStoreModelConvention
{
        public void Apply(EdmModel item, DbModel model)
        {
            AddToCharFunc(model);
            AddToNumberFunc(model);
        }

        public void AddToCharFunc(DbModel dbModel)
        {
            var edmType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String);
            var payload =
                new EdmFunctionPayload
                {
                    Schema = "SYS.STANDARD",
                   
                    ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion,
                    IsComposable = true,
                    IsNiladic = false,
                    IsBuiltIn = true,
                    IsAggregate = false,
                    IsFromProviderManifest = true,
                    StoreFunctionName = "TO_CHAR",
                    ReturnParameters =
                        new[]
                    {
                        FunctionParameter.Create("ReturnType", edmType, ParameterMode.ReturnValue)
                    },
                    Parameters =
                new[]
                {
                    FunctionParameter.Create("Val", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32), ParameterMode.In)
                }
                };

            var function = EdmFunction.Create("TO_CHAR", "EntityLayer.Utility", DataSpace.SSpace, payload, null);
            dbModel.StoreModel.AddItem(function);
        }

        public void AddToNumberFunc(DbModel dbModel)
        {
            var edmType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Decimal);
            var payload =
                new EdmFunctionPayload
                {
                    Schema = "SYS.STANDARD",
                    ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion,
                    IsComposable = true,
                    IsNiladic = false,
                    IsBuiltIn = true,
                    IsAggregate = false,
                    IsFromProviderManifest = true,
                    StoreFunctionName = "TO_NUMBER",
                    ReturnParameters =
                        new[]
                    {
                        FunctionParameter.Create("ReturnType", edmType, ParameterMode.ReturnValue)
                    },
                    Parameters =
                new[]
                {
                    FunctionParameter.Create("Val", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.In)
                }
                };

            var function = EdmFunction.Create("TO_NUMBER", "EntityLayer.Utility", DataSpace.SSpace, payload, null);
            dbModel.StoreModel.AddItem(function);
        }  
    }


FunctionConvention sınıfı ilgili fonksiyonların haritalanması için gerekli meta bilgilerin oluşturulmasını sağlayan bir Convention sınıf oluşturulduktan sonra Context sınıfı içinde yer alan "OnModelCreating" fonksiyonu içinde tanımlanmaktadır.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
       modelBuilder.Conventions.Add(new FunctionConvention());
}


Yukarıdaki işlemler yapıldıktan sonra LINQ sorgularında kullanılabilmesi için aşağıdaki gibi bir sınıf içinde ilgili fonksiyonların karşılıkları tanımlanmalıdır:

public class CommonFunctions
{
     [DbFunction("EntityLayer.Utility", "TO_CHAR")]
     public static string TO_CHAR(int Val)
     {
         throw new NotSupportedException();

     }

     [DbFunction("EntityLayer.Utility", "TO_NUMBER")]
     public static decimal TO_NUMBER(string Val)
     {
         throw new NotSupportedException();
     }
}