Aklımda Kalası Kelimeler

* давайте работать вместе
* Zarf ve Mazruf, Zerafet(xHoyratlık) ile aynı kökten(za-ra-fe) gelir
* Bedesten
* Suç subuta ermiştir - Suç sabit olmuştur
Entity Framework etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Entity Framework etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

10 Şubat 2013 Pazar

Fluent API - HasRequired - One To Zero Or One ilişkisi

Ref: Julia Lerman
"Bir makale daima bir Blog'a ait olmalıdır" tanımını gerçeklemek için Blog ve Post sınıfları arasında şöyle bir ilişki tanımlanmalıdır:
public class Blog
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string BloggerName { get; set; }
        public virtual ICollection<Post> Posts { get; set; }
    }
    public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public DateTime DateCreated { get; set; }
        public string Content { get; set; }
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
Buna göre şu DB oluşur. Dikkat etmeniz gereken yer BlogId alanının Not Null olarak Post tablosunda oluşmasıdır. Bunu yapanın public int BlogId { get; set; } özelliği olduğuna dikkat ediniz.


Eğer Post içindeki BlogId özelliğini "EF zaten public Blog Blog { get; set; } özelliğini görür görmez oluşturacak" derseniz
O zamanda Null bırakılabilir bir makale girişi yapabiliyor olacaksınız. One - To - [Zero Or One](1-0..1) ilişkisini solda Blog olacak şekilde kurdunuz demektir.

Peki public int BlogId { get; set; } özelliğini sınıf içinde tanımlamadan Fluent API ile OnModelCreating metodunda tanımlamak isterseniz:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
}
Post sınıfındaki Blog özelliğinin gerekli olduğunu belirmek için:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>().HasRequired(p => p.Blog);
}

Son şekliyle kodumuz:
using System;
using System.Collections.Generic;
using System.Data.Entity;

namespace caFluentAPI {
    class Program {
        static void Main(string[] args) {
            var ctx = new Ctx_Blog();
            var v = new Blog() {
                    BloggerName = "CemT",
                    Title = "Notlarımdan",
                };
            ctx.Blogs.Add(v);
            ctx.SaveChanges();
        }
    }

    public class Ctx_Blog : DbContext {
        public Ctx_Blog() : base("name=cnn") {
            Database.SetInitializer<Ctx_Blog>(new DropCreateDatabaseAlways<Ctx_Blog>());
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            modelBuilder.Entity<Post>().HasRequired(p => p.Blog);
        }
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }

    public class Blog
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string BloggerName { get; set; }
        public virtual ICollection<Post> Posts { get; set; }
    }
    public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public DateTime DateCreated { get; set; }
        public string Content { get; set; }
        //public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
}

31 Ocak 2013 Perşembe

DbContext ile Code First VT Tasarımı

Bununla ilgili tonla video ve yazı var. Benim gözüme çarpan özel noktaları buraya not alacağım. Aralarda gördüklerim arasındaki ilişkilerden bahsedeceğim.

AutoDetectChangesEnabled = true MSDN der ki; Ayarladaki bir değişimi otomatik olarak algılar.
Patrick Desjardin'in blogunda der ki; DbSet'in Add, Attach, Find, Local, Remove metotlarını çağırırken aynı zamanda DbContext'in GetValidationErrors, Entry, SaveChanges metotlarında da çağırılır. DbSet metotlarının çağırlıdığı anda nesnenin veritabanına gitmeden önce değişim geçirip geçirmediği kontrolleri yapılan

public virtual void DetectChanges(bool force = false)
{
    if (this.AutoDetectChangesEnabled || force)
    {
        this.ObjectContext.DetectChanges ();
    }
}
metoduna gidilir. Bu da yüklü VT işlemi yaparken gereksiz bir performans kaybına neden olacağı için AutoDetectChangesEnabled özelliği false yapılır. Patrick tecrübesine dayanarak 100den az nesne için yapılan işlemlerde performans kaybı çok değilken 100'ün üstündeki işlemlerde hissedilir olacağıdır.
Arthur'un dediğine göre ise:
When using most POCO("plain-old" CLR objects) entities the determination of how an entity has changed (and therefore which updates need to be sent to the database) is made by detecting the differences between the current property values of the entity and the original property values that are stored in a snapshot when the entity was queried or attached.
POCO nesneleri VT ye gitmeden önce değişimlere bakılarak hangi nesnenin VT'ye gidip gitmeyeceğine karar verildiği bu DetectChanges metodunu şu kodla (bir dizi[bulk] işlem yaparken) verimli hale getirebilirsiniz:
using (var context = new UnicornsContext())
{
    try
    {
        context.Configuration.AutoDetectChangesEnabled = false;

        // Make many calls in a loop
        foreach (var unicorn in myUnicorns)
        {
            context.Unicorns.Add(unicorn);
        }
    }
    finally
    {
        context.Configuration.AutoDetectChangesEnabled = true;
    }
}

Arthur'un makalesinden yine döndüm dolaştım MSDN0'e geldim. Çünkü alternatif bir yol önermiş:

An alternative to disabling and re-enabling is to leave automatic detection of changes turned off at all times and either call context.ChangeTracker.DetectChanges explicitly or use change tracking proxies diligently. Both of these options are advanced and can easily introduce subtle bugs into your application so use them with care.

Devam etmeden önce Lazy Loading nedir?
Lazy loading is the ability for an entity to perform a query for related entities whenever a navigation property is accessed.
Yani siz nesneyi oluşturduğunuzda tüm bağlantılı diğer nesneleri bind olmasın ve sadece bağlantılı özelliğini çağırdığınızda değerleri bağlansın isterseniz "tembel yükleme" etkin olmalı. Elbette tüm context içinde bunu açıp kapatmanız mümkünken sadece sınıf bazlıda yapabilirsiniz. Aşağıda detaylarından bahsedeceğim.

MSDN derki: Eğer Lazy Loading ve POCO'larının değişimlerini takip etmek isterseniz, EF POCO'larınız için çalışma zamanında PROXY oluşturacaktır. Eğer Lazy Loading proxy istersen değişim takibine sahip olmadan yapabilirsin. Ama değişim takibi istersen Lazy Loading ister istemez proxylerinde gelecektir.
Proxy yaratılması için:
  1. Data sınıfı public erişimde olmalı.
  2. Data sınıfı sealed veya abstract tanımlanmamalı
  3. Data sınıfı parametresiz public veya protected yapıcı metoda sahip olmalı. Parametresiz protected yapıcı metodu CreateObject metodu kullanarak POCO'larınız için proxy oluşturmak istediğinizde kullanılabilir. CreateObject metodu proxy oluşturmayı garantilemez.
  4. Sınıfınız IEntityWithChangeTracker veya IEntityWithRelationships arayüzlerini uygulamaz çünkü PROXY sınıfları bu arayüzleri uygulayacaklardır.
  5. The ProxyCreationEnabled option must be set to true.(E yani.. Şöyleki: context.ContextOptions.ProxyCreationEnabled = true; )
Siz POCO'nuzu yukarıdaki kaidelere göre yaratırsanız aşağıdaki gibi bir yapı oluşur:
public class Kisi
{
    public virtual ICollection Adresler { get; set; }
}
ve bunun peşine dinamik olarak proxy sınıfınız aşağıdaki gibi üretilir:
public class EFKisiProxy : Kisi
{
    public override ICollection Adresler
    {
        get
        {
            DoLazyLoad();
            return base.Adresler;
        }
        set
        {
            base.Adresler= value;
        }
    }
}

Lazy Loading için:
Her bağlantı özelliği ("Her kurumun bir adresi vardır" demek Kurum sınıfı Adres tipinde bir özelliğe sahiptir demektir. Kurum içinde Adres sınıfına bir bağlantı verilecektir) public ve virtual tanımlanmalı ve get metodu mühürlenmiş olmamalı.

Değişim Takibi için:
  1. Bağlantı olarak kullanılan her özellik Data Model içinde mühürlenmiş olmamalı, public ve virtual olmalı.
  2. Eğer birden çok adresi olacaksa bir kurum nesnesinin o halde ICollection<Adres> olarak bu ilişki gösterilmeli.
  3. Eğer nesnen ile birlikte proxy tipini yaratmak istersen ObjectContext içindeki CreateObject metodunu her yeni nesne oluştururken "new" operatörü yerine kullanmalısın.

1:32(am) ve bu arada şunu dinliyorum ;)


Ne diyordu MSDN? Hah, eğer senin CreateObject metot çağrın proxy oluşturamazsa doğrudan POCO'ndan bir nesne yaratır ve sana verir. Bunu anlamak için üretilen nesnenin tipine bakarsın. Tipler aynı değilse bu proxy nesnesidir ve en azından Lazy Loading özelliği vardır. Detaylar How to: Identify that a POCO Entity is a Proxy adresinde.

Yatma saatini geçirdim ama konu konu açtı ve gördümki bu da güzel bir bağlantı olacak CreateObject ya da Create metodu kullanılarak yaratılmış nesnelerin hem alt ilişkileri(public virtual ... ile tanımlı özellikleri için) hemde bu nesnelerin VT ye işlenmeleri ile ilgili. Kaynak stackoverflow ve soru: Create<object> vs new <object> in Entity Framework. Cevaplar güzel ve derlerki: Eğer CreateObject ya da Create metotları ile bir nesne yaratırsan bu nesneler ObjectContext içinde olmayacaklardır. Bunları ObjectContext içine almak istersen Attach etmelisin.
using (var ctx = new MyDbContext())
{
    var user = ctx.Users.Create();
    user.Id = 1;
    ctx.Users.Attach(user);

    var roles = user.Roles;
}
Create ile yarattığın ve Attach ile contexte iliştirdiğin için user.Roles gelecektir. Ama sen new operatörü ile yapsaydın
using (var ctx = new MyDbContext())
{
    var user = new User { Id = 1 };
    ctx.Users.Attach(user);

    var roles = user.Roles;
}
Roles özelliğine asla erişemeyecektin. Tam bu noktada Attaching and Detaching Objects başlıklı MSDN makalesine baktım ve özetle şunları gördüm: Sen bu hariçten nesne yaratma ihtiyacını çeşitli hallerde isteyebilirsin. Mesela normal bir sorgu ile elde edeceğin nesneler object context içindedirler ve yönetilmeleri ve izlenmeleri object context tarafından yapılırken harici bir sorguyla elde ettiğin nesneler context harici oldukları için yönetimleri senin elinde olacaklar. Ya da senin ASP.NET viewstate içinde sakladığın, remote metot veya web servisi çağrısıyla elde ettiğin nesnelerin olabilir. Tüm bunları
  1. System.Data.Objects.ObjectSet.AddObject(...) veya System.Data.Objects.ObjectContext.AddObject(...)
  2. System.Data.Objects.ObjectSet.Attach(...) veya System.Data.Objects.ObjectContext.Attach(System.Data.Objects.DataClasses.IEntityWithKey)ve AttachTo
metotlarıyla ekleyebilirsin. 1 ve 2 farkı primary key değerlerinin olması veya çakışma halinde istisna fırlatılmasıyla ilgili.

İkinci resmimde altı kırmızıyla çizili satırları biraz izah etmiş biraz çevrisiyle kendime notumu almış oldum.
Devamı MSDN'de...