Önsel Akın'ın çiziktirmeleri.
  • ana sayfa
  • hakkımda
  • postalar
rss powered by umbraco
  • SharePoint [3]
  • Sharp Architecture [1]
  • Asp.Net Mvc [2]
  • MongoDB [1]
  • NoSQL [1]
  • Office Communications Server [1]
  • NHibernate [1]
  • Lync Server 2010 [1]
  • Kasım 2010  (1)
  • Ekim 2010  (2)
  • Ağustos 2010  (2)
  • Temmuz 2010  (1)
  • Nisan 2010  (3)

Lync Server 2010 Dokümantasyonu Yayınlandı

Önsel Akın, 12 Kasım 2010 Cuma

Microsoft Lync Server 2010 Archiving Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=ff9f1066-c92c-4e74-9639-2f09f0bfaae9

Microsoft Lync Server 2010 Monitoring Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=8ad1bdf1-c43a-4dd4-af07-52027a4bcaeb

Microsoft Lync Server 2010 Documentation Help File:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=9720c3f1-ddd4-426b-b98a-f1205561ce00

Migrating from Office Communications Server 2007 R2 to Lync Server 2010:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=cae132e3-84ab-4110-885c-5bab0e64657e

Microsoft Lync Server 2010 Enterprise Edition Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=4cf4bed4-2f76-4b99-adcb-60653521cc70

Enabling Quality of Service with Microsoft Lync Server 2010:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=1d27de6c-a21a-498c-9cda-7eb5aae42f23

Microsoft Lync Server 2010 Enterprise Voice Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=af7cced8-e110-434e-97de-300b8a064e16

Microsoft Lync Server 2010 Response Group Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=14f32cca-1ef2-4283-8a80-3e444248c065

Microsoft Lync Server 2010 Standard Edition Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=787d6b9f-cbb3-4d6d-b292-34fe43e9afc3

Microsoft Lync Server 2010 Device Management and Troubleshooting Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=ca28982c-fb5c-4b79-b197-a3e25eb72323

Microsoft Lync Server 2010 Edge Server Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=85c62b26-e509-435e-811f-71c8c89dca83

Microsoft Lync Server 2010 Client and Device Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=39d317af-3b6e-43cd-a53c-a0ef561e3355

Microsoft Lync Server 2010 Active Directory Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=513c46db-adc3-4d62-8acc-5f0ee27f1f9c

Microsoft Lync Server 2010 Call Park Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=e1cfd1dc-7ae0-4096-895b-101422146878

Microsoft Lync Server 2010 Reference: Call Data Recording and Quality of Experience Database Schema:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=de71b0a5-f164-46a1-b706-b6018dc7ccc4

Microsoft Lync Server 2010 Supportability Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=73d7e282-bc2d-4c10-82f2-838d9c0332d6

Microsoft Lync Server 2010 Announcement Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=9bb9cdf3-6a75-4700-a91c-6286bd3f778b

Microsoft Lync Server 2010 Group Chat Administration Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=7af3e109-38ec-4f04-bf2f-3d35b6454a77

Microsoft Lync Server 2010 Group Chat Deployment Guide:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=cd79d0a0-b7fb-4ed1-83e8-61acf5a69f78

Lync Server 2010
|

NHibernate

Önsel Akın, 22 Ekim 2010 Cuma

Sanırım derginin bu sayısındaki en kısa başlık bu. Sadece NHibernate. Başlık kısa ancak konu oldukça uzun. 2000'lerin başından beri var olmasına rağmen Türkiye'deki .Net camiasında yurtdışındaki kadar popüler olmayan ancak en güçlü O/RM seçeneklerinden biri olan NHibernate ile ilgili hızlı bir tanıtım yazısı okumak üzeresiniz.

O/RM (Object Relational Mapping) mi?

Kaçınız şimdiye veri depolama ihtiyacı olmayan bir uygulama geliştirdi? Eğer yazılım geçmişiniz benim gibi Commodore ve Amstrad dönemlerine kadar uzanmıyorsa bu soruya "ben" şeklinde cevap vermenizin düşük bir ihtimal olduğunu düşünüyorum.

Geliştirdiğimiz her yazılımda bir şekilde bir yerde veri depolama ihtiyacı duyuyoruz. XML dosyalarından, ilişkisel veritabanlarına kadar veri depolamak için birçok alternatif mevcut. Bu birçok alternatife erişip veri üzerinde işlem yapmak için yine birçok farklı teknoloji mevcut. .Net dünyasında olan birçok yazılım ekibi genelde bu alternatifler içinden Ado.Net'i, dolayısıyla DataSet, DataReader vb. nesneleri kullanmayı tercih ediyor. Tabi ki bunda yanlış olan hiçbir şey yok. Ancak durum biraz daha farklı. Şöyle ki;

Herşeyi nesnelerle düşünür, nesne olarak değerlendiririz.

Yani bir topluluk portali uygulaması geliştiriyorsak, "Üye"lerden, "Paylaşım"lardan, "Yorum"lardan vb. birçok nesneden / varlıktan bahsediyoruz demektir. Ya da bir bankacılık uygulaması ise geliştirdiğimiz, Hesap, Müşteri, Para Transferi, Kredi Kartı gibi nesnelerdir uygulamamızın kapsamını oluşturan. Bu kapsama ilgi alanı (domain), bu kapsam içinde yukarıda bahsettiğimiz her nesneye de ilgi alanı modeli (domain model) ismi verilmektedir. Çözüm gerektiren her problemin adreslendiği yer ilgi alanı modeli olmalıdır. Kredi Kartı numaralarının doğruluğunun kontrolü, hesap bakiyesi belli bir miktarın üstünde olan müşterinin "gold" müşteri olarak değerlendirilmesi gibi "iş" ile ilgili her durum bu ilgi alanı içerisinde modellenir.

Bu modelleme süreci, modellerin veri depolarında saklanma aşamasına gelindiğinde başka bir ihtiyacın ortaya çıkmasına neden olur. .Net üzerinden ilerleyecek olursak, programlama dilinde "sınıf" olan bu modeller, veri saklama ortamlarına aktarılırken tablolara, daha doğrusu tabloları oluşturan kayıtlara dönüştürülmelidirler. Yani kendi içinde her tür durumun (ya da daha gerçekçi olması açısından birçok durumun diye düzeltelim) değerlendirildiği bir sınıfın depoladığı verinin alınması, ilişkisel veritabanının anlayabileceği, saklayabileceği formata dönüştürülmesi ve depolanması gereklidir. Eğer şimdiye kadar anlattığım gibi bir ilgi alanı modeli geliştiriyorsanız ve ilgi alanı içindeki modellerinizin verisini örneğin SQL Server'da saklamak için gerekli olan dönüşüm kodlarını yazıyorsanız tebrik ederim, siz bir O/RM geliştiricisi olmuşsunuz bile.

Wikipedia'dan alınmış tam tanımı ile, O/RM, nesne yönelimli programlama dillerinde uygulanan, birbirleriyle uyumsuz tip sistemleri arasında, verinin uyumlu hale getirilmesi tekniğidir. Bu tanımı bizim anlatımımıza uyarlayacak olursak; .Net ile SQL Server tabloları birbirleriyle uyumsuz tip sistemleridir ve bizim, nesnelerden aldığımız her veriyi SQL Server uyumlu hale getirmek için yazdığımız her satır kod O/RM tekniğine işaret eden bir koddur diyebiliriz.

NHibernate'in yukarıdaki anlatımda tam olarak nereye oturduğu sanırım anlaşılmıştır. NHibernate bir O/RM çatısı. .Net ile yazdığımız sınıfların, ilişkisel veritabanı sistemleri ile eşleştirmesini (dönüştürülmesini) gerçekleştiriyor. Daha güzel bir ifadeyle, biz ilgi alanımıza ve iş kurallarımıza yoğunlaşırken, ilgi alanı modelimizin veritabanı ile ilgili olan kısmındaki tüm ağır işi NHibernate yükleniyor.

O/RM'nin M'si

NHibernate, sınıfların ilişkisel veritabanı ile eşleştirilmesini sağlıyor demiştik. O halde bu eşleştirmenin nasıl yapılması gerektiğini de NHibernate'e göstermek gerekiyor. Modellerin hangi özellikleri, veritabanında hangi kolon ile eşleşecek, modelin hangi özelliği eşlenen tablodaki anahtar kolon olacak, modeller arasındaki ilişkiler veritabanına ne şekilde yansıtılacak vb. gibi tüm eşleme kurallarının NHibernate tarafından bilinmesi gerekiyor.

Eşleme kurallarının tanımlanması için kullanılabilecek iki yöntem mevcut. Birinci yöntem -ki bu en eski ve yaygın olan yöntemdir- hbm.xml uzantılı bir xml konfigürasyon dosyası ile eşleme kurallarını belirlemek. İkinci yöntem NHibernate ile kardeş proje olan Fluent NHibernate kullanarak gerçekleştirilen eşleme gerçekleştirmek. Xml konfigürasyon dosyalarını her zaman sıkıcı ve itici bulmuşumdur, dolayısıyla bu yazıda Fluent kullanarak eşlemeleri gerçekleştireceğim.

İlgi Alanı Modeli

Fluent NHibernate içinde hazır gelen örnek proje üzerinden ilerleyeceğiz. Yazının sonunda projeyi indirebileceğiniz web adresini paylaştım. Yazı içerisinde yapacağım küçük değişiklikleri kolayca projeye uyarlayabilirsiniz. Projedeki modellerin yapısı şu şekilde:

image

İlgi alanı içinde dört model mevcut. Özellikleri şemada gördüğünüz gibi. Modelde üzerinde durulması gereken şey sınıflar arasındaki ilişkiler. Store ve Product sınıfları arasında çoka-çok (many-to-many) ilişki mevcut. Yani bir mağazada birden çok ürün bulunabilir ve bir ürün birçok mağaza stoğunda mevcut olabilir. Ayrıca Store ve Employee sınıfları arasında bire-çok (one-to-many) ilişki mevcut. Yani bir mağazada birden çok çalışan bulunabilir. Product ve Location sınıfları arasında ise biraz daha farklı bir ilişki mevcut. Bu iki sınıf arasında bir bileşim (composition) ilişkisi tanımlı. Yani ürünün depodaki koridor ve raf numarasını göstermekte olan Location sınıfı her ürünün mutlaka sahip olması gereken bir bileşen. Modeli kullanırken koridor ve raftan oluşan bu lokasyon nesnesine tek başına bir varlıkmış gibi erişebileceğiz ancak ilişkisel veritabanı açısından bakıldığında Location nesneleri için ayrı bir veritabanı tablosu oluşturulmayacak. Bunun yerine, Location sınıfına ait olan Aisle ve Shelf özellikleri, sanki Product sınıfının doğrudan özellikleriymiş gibi Product tablosunda iki ayrı kolon olarak tanımlanacak ve bu kolonlarda bilgi depolanacak.

Fluent NH ile Eşleme

Eşlemenin nasıl kodlandığına geçmeden önce model içinden bir sınıf örneği göstererek NHibernate'in bir özelliğinden bahsetmek istiyorum. Store sınıfının tanımına bakalım:

public class Store
{
  public virtual int Id { get; private set; }
  public virtual string Name { get; set; }
  public virtual IList<Product> Products { get; set; }
  public virtual IList<Employee> Staff { get; set; }

  public Store()
  {
    Products = new List<Product>();
    Staff = new List<Employee>();
  }
  
  public virtual void AddProduct(Product product)
  {
    product.StoresStockedIn.Add(this);
    Products.Add(product);
  }

  public virtual void AddEmployee(Employee employee)
  {
    employee.Store = this;
    Staff.Add(employee);
  }
}

Özelliklerin ve metodların virtual tanımlandığını farketmişsinizdir. Bunun nedeni lazy-loading adı verilen ve birbiriyle bağlantılı olan nesnelerin tek seferde değil ihtiyaç oldukça veritabanından çekilmesini sağlayan NHibernate özelliğinin aktif olmasıdır. Bir mağazamız (Store) ve bu mağaza içinde kayıt edilmiş 10 bin adet ürün (Product) olduğunu düşünün. Lazy-loading özelliğinin aktif olmadığı durumda, veritabanından mağazayı sorgulayıp çektiğimizde NHibernate 10 bin adet ürünü de ihtiyacımız olsa da olmasa da çekip getirecektir.

Peki neden virtual?

NHibernate lazy-loading özelliğini devreye sokmak için, NHibernate altyapısının inşası sırasında model sınıflarından türeyen, model sınıflarının sahip olduğu ve olmadığı bir takım ek özellikleri içinde bulunduran "proxy" sınıflar oluşturur ve oturum açık olduğu sürece model sınıfları yerine bu proxy sınıflar üzerinden işlem gerçekleştirir. Model sınıflarındaki özelliklerin virtual olması da, model sınıfın sahip olduğu özelliklerin proxy tarafından override edilebilmesini ve lazy-loading için gerekli olan algoritmanın proxy tarafından uygulanabilmesini sağlar.

Sınıf tanımında üzerinde durulması gereken bir diğer nokta, bire-çok ve çoka-çok ilişkiler için yapılmış olan tanımlamalar. Bir mağaza stoğunda birden çok ürün bulunabilir demiştik. Bunun karşılığı;

public virtual IList<Product> Products { get; set; }

olarak tanımlanıyor. Store sınıfının yapılandırıcısı içinde de Products listesinin oluşturulduğunu görebilirsiniz. Store ve Employee sınıfları arasında bire-çok ilişki olacak demiştik, bunun Employee sınıfındaki karşılığı ise;

public virtual Store Store { get; set; }

olarak tanımlanıyor. NHibernate, doğru eşlemeler yapıldıktan sonra tüm bu ilişkiler dahilinde kayıtların veritabanına gönderilmesini / çekilmesini gerçekleştirecek.

Şimdi eşlemelerin nasıl yapıldığına StoreMap üzerinden bakalım.

public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Products)
.Cascade.All()
.Table("StoreProduct");
HasMany(x => x.Staff)
.Cascade.All()
.Inverse();
}

}

Her model için bir eşleme sınıfı tanımlıyoruz (tamamen otomatik olarak eşlemeleri gerçekleştirmek de mümkün, belki başka bir yazıda bu özelliği inceleyebiliriz). Eşleme sınıfı ClassMap sınıfından türetiliyor ve eşleme kuralları hangi sınıf için belirleniyorsa tip parametresi olarak bu sınıf kullanılıyor. Eşleme sınınıfının yapılandırıcısı içinde ise lambda ifadeleri ile sınıfın özelliklerinin ilişkisel veritabanı karşılığının ne olacağı gösteriliyor.

Id(x => x.Id);

ifadesi ile, Store sınıfının Id özelliğinin veritabanında kimlik (identity) kolon olarak oluşturulacağı belirleniyor.

Map(x => x.Name);

ifadesi ile, Store sınıfının Name özelliğinin veritabanında da varsayılan özellikleri ile karşılığı olması gerektiği gösteriliyor.

HasManyToMany(x => x.Products).Cascade.All().Table("StoreProduct");

ile, Store ve Product arasındaki çoka-çok ilişki gösterilmiş oluyor. Mağazaya bağlı ürünlerin, Store nesnesi içindeki Products listesinde saklanacağı anlaşılıyor. Cascade.All() ile de, Store üzerinde yapılan değişikliklerden (Id güncelleme ya da Store nesnesini silme), Store nesnesine bağlı olan Product nesnelerinin de etkileneceği (güncelleme ya da bağlı ürünlerin tamamının otomatik olarak silinmesi) kurala bağlanıyor. Çoka-çok ilişkilerde birbiriyle ilişkili olan kayıtların depolanması için bir üçüncü tabloya ihtiyaç var. İfadenin en sonundaki Table() metodu ile bu tablonun adının ne olacağı belirtiliyor. HasManyToMany metodunun ilişkinin diğer tarafındaki Product nesnesinde de karşılığı olması gerekli. ProductMap içindeki ifade de şu şekilde;

HasManyToMany(x => x.StoresStockedIn).Cascade.All().Inverse().Table("StoreProduct");

Bir ürünün ilişkili olduğu mağazalar, Product içindeki StoresStockedIn listesi ile tutuluyor. Tanımın bu tarafındaki tek fark Inverse() metodu. Bu metod ilişkinin hangi tarafının ilişkinin sahibi olacağını gösteren bir metod. Bu örnekte, ilişkinin depolanması sırasında ilişkinin sahibi Store tarafı. Bu durumda NHibernate Store nesnesini depoluyor, depoladığı Store nesnesinin veritabanı tarafından gelen Id değerini kullanarak Store nesnesinin Products listesindeki nesneleri depoluyor. Inverse kullanılmadığı durumda ise NHibernate, tüm Product nesnelerini Store ile ilişkili değilmiş gibi depolar, ardından Store nesnesini depolar ve Store nesnesinin Id değeri ile daha önce depoladığı Product nesnelerini teker teker günceller.

HasMany(x => x.Staff).Cascade.All().Inverse();

Yukarıdaki eşleme direktifi ile, Store ve Employee arasındaki bire-çok ilişki tanımı yapılıyor. Aynı şekilde Employee sınıfında da bunun karşılığını bulmak mümkün:

References(x => x.Store);

Model içinde bulunmayan ancak sıkça kullanılan bir özellik olan enumerasyonların nasıl eşlenebileceğini göstermek amacıyla Product sınıfına Status adında bir özellik ekledim. Enumerasyonun ve Product sınıfının kısmi tanımı şu şekilde:

public class Product
{
  //...

  public virtual InventoryStatus Status { get; set; }

  //...
}

public enum InventoryStatus
{
  OnOrder          = 1,
  AvailableForSale = 2,
  InTransit        = 3
}

Aksini belirtmediğiniz sürece NHibernate, enumerasyonları string karşılıkları ile saklayacaktır. Yani eğer Status kolonu için eşleme kuralımız şu şekilde olsaydı:

Map(x => x.Status);

Product tablosundaki kayıtları şu şekilde görecektik:

image

Ancak Status için eşlemeyi;

Map(x => x.Status).CustomType<int>();

şeklinde yaparak enumerasyon değerlerini, her bir değerin karşılığı olan int ile depolamış oluyoruz:

image

Product tablosunda gördüğünüz Aisle ve Shelf kolonlarının Product nesnesinden değil, Product ile ilişkili olan Location nesnesinden geldiğini söylemiştik. Fluent ile bu ilişkilendirmeyi sağlayan ProductMap içindeki lambda ifadesi ise şu şekilde:

Component(x => x.Location);

Bu ifadeyle, Product sınıfı içindeki Location özelliğine bağlı olan özelliklerin Product nesnesine aitmiş gibi Product ile aynı tabloda saklanması sağlanmış oluyor. Ancak Product tablosundaki Aisle ve Shelf özelliklerine C# ile erişmek istendiğinde Product.Location.Aisle ve Product.Location.Shelf şeklinde erişilebiliyor (Location özelliğinin null değerine sahip olmadığını varsayıyoruz tabi).

Uygulamanın Çalıştırılması

NHibernate, veritabanı ile tüm iletişimi Session nesneleri üzerinden gerçekleştirir. Bağlantıların yönetimi, bağlantı havuzu yönetimi vs. tamamı Session tarafından bizleri rahatsız etmeyecek şekilde arka planda gerçekleştirilir. Dolayısıyla, veritabanına ulaşmak için öncelikle yapılması gereken, Session konfigürasyonunu gerçekleştirmek. NHibernate soyut fabrika desenini (abstract factory pattern) uygulamaktadır. Bu da demek oluyor ki aslında konfigürasyon yapılması gereken nokta Session örnekleri değil, Session üretiminden sorumlu olan Session fabrikasıdır.

private static ISessionFactory CreateSessionFactory()
{
  return Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2008<
    .ConnectionString("Server=.\\SQLEXPRESS;Database=FNHSample;Integrated Security=SSPI;"))
    .Mappings(m =>m.FluentMappings.AddFromAssemblyOf<Program>())
    .ExposeConfiguration(cfg => new SchemaExport(cfg).Create(false, true))
    .BuildSessionFactory();
}

Gördüğünüz üzere, tek bir ifade ile hem Nhibernate yapılandırılıyor hem de bir ISessionFactory örneği döndürülüyor. Database() metodunun parametrelerini SQL Server ile çalışacak şekilde değiştirdim. Database() metodunun hemen ardından gelen Mappings() metodu, ClassMap tanımlarının nereden alınacağını gösteriyor. Bu durumda Store sınıfının bulunduğu assembly içinden eşleme tanımlarının okunması sağlanıyor.

ExposeConfiguration ile, SchemaExport nesnesi kullanılarak veritabanı şemasının hemen üstte belirtilen eşleme kuralları dahilinde üretilmesi sağlanıyor. SchemaExport.Create metodunun ilk parametresi, arka planda çalışacak Sql ifadelerinin script olarak üretilmesini kontrol ediyor. İkinci parametre ise şema üretiminin veritabanı üzerinde gerçekleştirilmesini kontrol ediyor. Yani bir başka deyişle, yukarıdaki şekilde kullanıldığında, SQL Server üzerinde model sınıfları için tabloları oluşturmamıza gerek kalmıyor. NHibernate veritabanı içinde ihtiyaç olan tüm tabloları, tablolar içindeki ilişkisel bütünlük kurallarını kendisi tanımlıyor. Zaten farketmiş olduğunuz gibi, modellemeye veritabanından başlamadık. İşin aslı, veritabanı ile hiç ilgilenmedik bile. Modelleri tasarladıktan sonra kalan herşeyi NHibernate zaten hallediyor.

Session fabrikası yapılandırması tamamlandığına göre, fabrika Session üretmeye başlayabilir, biz de böylece nesnelerimizi veritabanında depolamaya başlayabiliriz:

var sessionFactory = CreateSessionFactory();
var session = sessionFactory.OpenSession();

Veritabanında saklanmak üzere bir kaç model örneği:

var barginBasin = new Store { Name = "Bargin Basin" };
var superMart = new Store { Name = "SuperMart" };
var potatoes = new Product { Name = "Potatoes", Price = 3.60, Status = InventoryStatus.AvailableForSale };
var fish = new Product { Name = "Fish", Price = 4.49, Status = InventoryStatus.AvailableForSale, Location = new Location { Aisle = 10, Shelf = 20 } };
var daisy = new Employee { FirstName = "Daisy", LastName = "Harrison" };
var jack = new Employee { FirstName = "Jack", LastName = "Torrance" };

Örnekler tanımlandıktan sonra Store, Product ve Employee arasında tanımlanan ilişkilerin veritabanı tarafında kalıcı olması için her sınıf içinde tanımlanmış olan IList koleksiyonları ile nesneler birbirlerine bağlanıyor:

barginBasin.AddProduct(potatoes);
barginBasin.AddProduct(fish);
superMart.AddEmployee(daisy);
superMart.AddEmployee(jack);

Burada, Store sınıfı içinde tanımlanmış olan AddProduct ve AddEmployee metodları ile ilgili söylenmesi gereken birkaç şey var. Metodların tanımlarına bir bakalım:

public virtual void AddProduct(Product product)
{
  product.StoresStockedIn.Add(this);
  Products.Add(product);
}

public virtual void AddEmployee(Employee employee)
{
  employee.Store = this;
  Staff.Add(employee);
}

AddProduct metodu bir Product örneği alıyor ve Product nesnesinin StoresStockedIn listesine Store nesnesinin kendisini ekliyor. Ardından Product nesnesini de Store'un Products listesine atıyor. Böylece çift taraflı olarak aradaki ilişki sağlanmış oluyor. Aynı şey AddEmployee metodu için de geçerli. Ancak burada ilişki bire-çok olduğundan Employee'nin Store özelliğine değer atanıyor ve Employee nesnesi Store'un Staff listesine ekleniyor.

session.SaveOrUpdate(barginBasin);
session.SaveOrUpdate(superMart);

Bu iki satır ifade ile tüm nesne hiyerarşisi bütün ilişkileri ile birlikte veritabanında depolanmış oluyor. Ayrı ayrı her ürünü (Product) ve çalışanı (Employee) veritabanında saklamadığımızı fark etmişsinizdir. Store nesnelerini depolamak, Store nesneleri ile ilişkili olan diğer tüm nesnelerin de veritabanında depolanması için yeterli.

Son olarak, bütün bu bilginin depolandığını teyid etmek için bir Session üzerinden Linq kullanarak veritabanını sorgulayalım. NHibernate üzerinden Linq ile sorgulama yapmak için NHibernate.Linq kütüphanesini indirmeniz gerekiyor. Yazının sonunda bu adresi paylaştım.

session.Linq<Store>().ToList().ForEach(store => WriteStorePretty(store));

Herhangi bir Session örneği üzerinden yukarıdaki sorguyu çalıştırabilirsiniz. NHibernate.Linq ve System.Linq isim uzaylarını tanımlamayı unutmayın. WriteStorePretty metodu örnek proje içinde tanımlanmış bir metod ve Store bilgilerini düzgün bir şekilde ekrana basıyor. Ben de tanımlanmış olan bu metodu kullandım.

Hem OR/M konusunda hem de NHibernate'in Fluent ile kullanımına dair kısa bir giriş yapmaya çalıştım. Umarım faydası olmuştur. Görüşmek üzere!

NHibernate ve NHibernate.Linq: http://sourceforge.net/projects/nhibernate/files/

Fluent NHibernate: http://fluentnhibernate.org/downloads

Fluent NHibernate kaynak kodları: http://github.com/jagregory/fluent-nhibernate

NHibernate
|

SharePoint 2010 ve Sosyal Programlama - Alfred'in Profili

Önsel Akın, 21 Ekim 2010 Perşembe

Dünyadaki her 14 insandan biri Facebook üyesi, YouTube'a her dakika toplam 24 saatlik video yükleniyor, Twitter kullanıcıları günde 65 milyon tweet postalıyor... Yıllar boyu artan bu rakamları her duyduğumda şaşkınlık yaşadım, açıkçası hala da yaşıyorum. Internet'in her döneminde yığınları kendine çeken sosyal bir servis hep oldu. Saatlerce telefon hattı meşgul olduğu için bir türlü bağlanmayı başaramadığımız BBS'ler, kendimize ait odamız olsun diye admin'leri ayartmaya çalıştığımız IRC'ler bir zamanların gözdesiydi . Günümüzün popüler servisleri ise belli. Belki ileride, sosyal platformlar, 5 duyunun kullanılabildiği paylaşım ortamlarına dönüşecek ancak zamanımızın parlayan yıldızları şu an için hepimizin bildiği, hergün kullandığı servisler. Herkese açık platformların yanında, kurumsal sosyal paylaşım uygulamaları alanında artık yeni ve güçlü bir oyuncu daha var; SharePoint 2010.

SharePoint 2010 ve Sosyal Özellikler

SharePoint 2010, kurum içi sosyal paylaşımı desteklemek için Facebook ve Twitter'dan aşina olduğumuz bazı özellikleri My Site başlığı altında sunuyor. SharePoint'te her kullanıcının kendi sayfası (aslında kendi sitesi / site koleksiyonu) anlamına gelen My Site altında, kullanıcının Facebook benzeri bir profil sayfası, formspring.me benzeri bir soru / cevap sistemi, twitter benzeri bir micro blogu, kullanıcının kurum organizasyonundaki yeri / rolü ve kullanıcının SharePoint üzerinde oluşturduğu her belge ve içerik rahat bir şekilde ulaşılabilir hale getirilmiş. Ayrıca sosyal paylaşımın vazgeçilmezlerinden olan etiketleme (Tagging) ve yorumlama (Notes) SharePoint'in her sayfasında kendisini gösteriyor.

My Site

Genelde rastladığım bir durum; Firmalar SharePoint'le ilk kez tanışırken ve çalışanlarını tanıştırmaya çalışırlarken, ilk yaptıkları şey portalin ana sayfasını hava durumu, döviz kurları, yemek listeleri, bazı gazete rss bildirimlerinden derlenen mashup'lar ile doldurmak ve çalışanların portale bu şekilde alışmasını ve düzenli olarak kullanmasını beklemek oluyor. Açıkçası bu yöntemin yüksek oranda başarılı olduğunu hiç görmedim. Bu saydıklarımın gereksiz olduğunu söylemiyorum, söylemeye çalıştığım şey bu eklenen özelliklerin portalde içi boş ve geri dönüşümü olmayan trafik oluşturmaktan başka bir işe yaramadığı. SharePoint devasa bir platform. SharePoint nedir sorusuna verilebilecek tek cümlelik bir cevap mevcut değil. Böyle bir platformu hava durumu ve döviz kurları göstermek için kullanıyorsa firma, portale yazık. Kullanıcıların işlerini kolaylaştırmak varken neden içi boş trafikle kullanıcıları portale alıştırmaya çalışalım? İş zekası raporları, en basit süreçlerin bile SharePoint akışları ile sürdürülmesi, görev yönetimleri, dosya paylaşımları ve 2010 ile daha güçlü bir altyapı sağlayan sosyal özellikleri kullanarak kullanıcıları neden portale çekmeyelim? Şu anda devam eden projelerimizden birinde, şimdiye kadar hiç uygulamadığımız bir yöntemle, portalin açılış sayfasını kullanıcıların kendi profil sayfaları olarak belirliyoruz, aynen Facebook'ta olduğu gibi. Kullanıcılar kendi işleri ile ilgili tüm süreçleri görebildikleri bir dashboard'un yanısıra, birlikte çalıştıkları insanların da neler yaptıklarını, neler paylaştıklarını ve varsa neleri talep ettiklerini kendi anasayfasından görebiliyor. Bunu sağlayan yapı SharePoint My Site.
Aşağıda hepinizin tanıdığına emin olduğum ama bir türlü tanışma fırsatı elde edememiş olduğunuz Northwind müşterilerinden Alfred Futterkiste'nin profil sayfasını görüyorsunuz (Alfred Futterkiste aslında bir firma, biz burada bir kişilik veriyoruz kendisine). Uzun çabalar sonucunda kendisinin vesikalık fotoğrafına ulaşıp sizin için profil sayfasını güncelledim. Aslında sadece Google imaj araması yaptım. Diğer Northwind çalışanlarını da aratabilirsiniz. Açıkçası verdiğim yüzlerce eğitimde hep güzel bayanlar olarak aklımda yer etmiş olan Nancy Davolio, Janet Leverling gibi çalışanların birer "teyze" olduklarını gördüğümde yaşadığım hayal kırıklığını anlatamam :)

  image
 
My Site yapılandırması düzgün bir şekilde gerçekleştirilmişse, SharePoint kullanıcılarının tamamı, standart tasarıma göre sağ üstte bulunan isimlerine tıklayarak, açılan menüden My Site seçeneği ya da My Profile seçeneği ile kendilerine ait siteye geçebilirler.
Alfred'in fotoğrafının hemen üstünde bir konuşma balonu görüyorsunuz. Bu konuşma balonu Twitter'ın 140 karakterlik mikro blog özelliği ile büyük benzerlik gösteriyor. Kullanıcı, kısa durum güncellemelerini bu balonu kullanarak gerçekleştiriyor ve tüm güncellemeler kullanıcıya ait haber bildiriminde yer alıyor (bu anlattıklarımı internet cafe'deki veletler bile rahatça anlayabilir eminim, Facebook sağolsun). Profil sayfasındaki haber bildirimleri, User Profile Service Application altında çalışan Activity Feed Job (Central Administration -> Monitoring -> User Profile Service Application - Activity Feed Job) ile güncelleniyor. Yani bir durum güncellemesi anında portale yansımayacaktır, sözünü ettiğim iş çalışıp tamamlandığında durum güncellemesi portalde yayınlanacaktır (Veletlerin anlamayacağı bir şey buldum!).
Durum güncellemesinin hemen altında Alfred'in profil bilgileri mevcut. Hemen sağda ise kullanıcının bio bölümü var.
Ask Me About ile kullanıcılar, belli ilgi alanları ya da etiketlere bağlı olarak birbirlerine soru sorabilmekteler. Ask Me about altında görünen iki bağlantı daha önceden sistemde tanımlanmış olan etiketlere karşılık geliyor. Bunlardan birine tıklandığında ise, sayfanın hemen altında yer alan Note Board üzerinden kullanıcıya soru sorulabiliyor (formspring.me).

image

My Organization Chart ile, profili izlenen kullanıcının organizasyondaki yerini görebiliyoruz. Ayrıca Organization Browser bağlantısına tıklandığında, Silverlight ile geliştirilmiş gayet başarılı bir organizasyon şeması görüntüleyici ile karşılaşıyoruz.

image

Recent Activities bölümü için açıklamaya gerek var mı?
In Common With You profili görüntüleyen kullanıcı ile profili görüntülenen kullanıcının ortak yöneticileri, ortak çalışma arkadaşları ve üyeliklerini gösteren faydalı bir araç.
Content bölümü altında, kullanıcının sahip olduğu doküman ve belgeler (yetkiler dahilinde) görüntülenmektedir.
 
Tags and Notes bölümüne geçmeden önce SharePoint'teki etiketleme ve yorumlama sisteminin nasıl çalıştığını inceleyelim.

Etiketler ve Notlar

SharePoint arayüzünde neredeyse bütün sayfalarda kullanıcıların bilgiyle etkileşimini artırmak amacıyla Etiketleme ve Not alma uygulamaları mevcut. Herhangi bir SharePoint sayfasının sağ üst kısmında aşağıdaki iki ikonu görmek mümkün.

image

Bu butonlar iki farklı şey yapıyormuş gibi dursa da aslında öyle değil. I Like It butonuna tıkladığınızda sayfaya bir "I Like It" etiketi yapıştırmış oluyorsunuz. Yani bunun sağdaki Tags butonuna tıklayıp etiket ismi olarak "I Like It" belirlemekten farkı yok. Farklı bir etiket atamak ya da sayfaya not almak istendiğinde Tags & Notes butonu kullanılabilir.

image

My Tags altında kullanıcının kendi eklediği etiketler, Suggested Tags altında da diğer kullanıcıların eklediği etiketleri görebilirsiniz. İçeriği etiketlemek istediğinizde my Tags altındaki kutucuğa doğrudan anahtar kelimeyi yazabilirsiniz. Autocomplete özelliği sayesinde daha önce kullanılan etikerler ve Metadata Service Application altında tanımlanmış olan terim kümeleri (Term Set) içindeki anahtar kelimeler açılır bir liste ile karşınıza çıkacaktır.

image

Ayrıca herhangi bir SharePoint sayfasına eklenebilecek Tag Cloud web bölümü ile sistemdeki etiketleri Web 2.0 sitelerinden aşina olduğumuz görsellikte sunma imkanı mevcut.

image

Aşağıda web bölümünü çalışırken görüyorsunuz:

image

Demo sistemimde etiketlerle yeterli sayıda etkileşim olmadığı için sıradan ve düz bir liste görüyoruz ancak etiketler kullanıldıkça, kullanım oranlarına göre farklı font boyutları ile gösteriliyor. Bu da çok kullanılan etiketlerin daha büyük fontlarla yazılması anlamına geldiği için popülariteyi görsel olarak takip edebiliyoruz.
Tag Cloud içindeki herhangi bir etikete tıkladığınızda o etiketin profil sayfasına gidersiniz. Aşağıda bu sayfanın ekran görüntüsünü görüyorsunuz.

image

Tagged Items ile bu etiketin yapıştırıldığı SharePoint nesnelerini görebiliyoruz. Get Connected altında, ilgi alanımıza giren etiketler, Ask Me About bölümünde kullandığımız etiketler ve bu etiketi takip eden kişilere göre filtrelemeler gerçekleştirebiliyoruz. Note Board ise her yerdeki ile aynı mantıkta çalışıyor, bu etiket profili ile ilgili notlar alabiliyoruz. Bu arada Note Board tamamen URL bazlı olarak çalışıyor, bunu da belirtmeliyim.
Şimdi tekrar profil sayfasındaki Tags & Notes bölümüne dönelim. Ekran görüntüsü zaten kendisini çok net anlatıyor aslında.

image

Refine By Type altında, sağ tarafta görünen etiketlenmiş SharePoint nesnelerini filtrelemek için seçenekler mevcut. Etiketler, Notlar, Özel ve Genel olmak üzere listeyi filtreleyebiliyoruz. Refine By Tag ile etiketlere göre filtreleme yapmak da mümkün.
Colleagues sekmesi altında ise Alfred'in çalışma arkadaşları görülebilir.

image

Kullanıcının çalışma arkadaşları ekleyip çıkartabildiğine dikkat edin.
Kullanıcı profilleri ile ilgili daha detaylı bilgilerle, Blog ve Wiki siteleri ile yazıya devam edeceğim.

SharePoint
|

ASP.NET MVC 2 - Model Bağlayıcılar ve Değer Sağlayıcılar

Önsel Akın, 18 Ağustos 2010 Çarşamba

Model bağlayıcı nedir?

Bİldiğiniz gibi ASP.NET MVC çatısında yönetici (controller) aksiyonlarına (action) gelen tüm parametreler aslen string veri tipindedir. Gelen değerin string veri tipinden, aksiyon parametresinin sahip olduğu veri tipine döndürülmesinden ve istemciden gelen değerin bu parametre içinde saklanmasından sorumlu olan sınıf(lar) model bağlayıcı (model binder) olarak isimlendirilmektedir. Karmaşık veri tipleri, listeler, koleksiyonlar, diziler, binary byte dizileri standart model bağlayıcılar tarafından tanınabilir.

MVC'de talep (request) önce MVCHandler nesnesine gelir. MVCHandler, bir yönetici fabrikası (ControllerFactory) aracılığıyla bir yönetici elde eder ve talebi yöneticiye yönlendirir. Gelen talebe göre yönlendirme (routing) kayıtlarına bakılarak ilgili yöneticinin ilgili aksiyonu ControllerActionInvoker nesnesi tarafından çağrılır. Bu çağrı sırasında hem aksiyon için kayıt edilmiş aksiyon filtrelerine (action filter) göre işlem gerçekleştirilir hem de aksiyon parametrelerine uygun bir şekilde gelen talep içerisinden veriler ayıklanır ve en başta da anlattığım gibi string veriler aksiyon parametrelerine uygun veri tiplerine döndürülerek veri atamaları gerçekleşir. Bu veri tipi dönüşümü ve veri atamalarından sorumlu olan en önemli bileşen ise bir DefaultModelBinder nesnesidir. DefaultModelBinder, IValueProvider nesnelerinden faydalanarak gelen değerleri okur ve hem standart .Net veri tiplerine sahip değerleri hem de karmaşık nesneleri oluşturabilir. MVC paketinde hazır gelmekte olan dört farklı IValueProvider nesnesi vardır. Bunlar talep geldiğinde işleme alınma sıralarına göre şu şekilde listelenebilir;

Değer Sağlayıcı Değer Kaynağı Üst Sınıf
FormValueProvider Request.Form NameValueCollectionValueProvider
RouteDataValueProvider RouteData.Values IDictionaryValueProvider<object>
QueryStringValueProvider Request.QueryString NameValueCollectionValueProvider
HttpFileCollectionValueProvider Request.Files DictionaryValueProvider<HttpPostedFileBase>

Şimdi farklı aksiyon parametrelerine göre model bağlama nasıl çalışıyor inceleyelim.

Primitif veri tipleri ve model bağlama

public ActionResult Login(string userName, string eMail, bool persist) {}

Yukarıdaki aksiyonda talep içinden bilgilerin alınması ve aynı isimdeki parametrelere atanması DefaultModelBinder için en kolay işlemlerden biri. Burada dikkat edilmesi gereken noktalardan biri, bool persist olarak tanımlanmış parametrenin Nullable olmamasıdır. Dolayısıyla eğer DefaultModelBinder model bağlama sırasında hiçbir değer sağlayıcı nesne ile persist parametresine değer ataması gerçekleştiremezse (ki bu sadece istemciden bu isimde bir değer gelmemesi sonucunda olur) bu durumda bir InvalidOperationException ile karşılaşırız. Nullable olmayan veri tipleri için CSharp 4.0 opsiyonel parametre özelliğinden faydalanabilirsiniz. Yani yukarıdaki aksiyon tanımını;

public ActionResult Login(string userName, string eMail, bool persist = false) { … }

şeklinde yazarak, persist değeri gelmediğinde ön ayarlı değer olarak false olmasını sağlayabilirsiniz.

Kullanıcı tanımlı tipler ve model bağlama

Tanımı aşağıdaki gibi olan bir modelimiz olsun;

public class SuperModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Height { get; set; }
    public int Bust { get; set; }
    public int Waist { get; set; }
    public int Hips { get; set; }
}

Bu modeli bir aksiyon parametresi olarak kullandığımız takdirde (tabi ki bir form postalama senaryosu dahilinde bu olmalı), DefaultModelBinder devreye girecek ve bir SuperModel nesnesi oluşturacak, istemciden postalanan verilerle SuperModel nesnesinin tüm özelliklerini dolduracaktır. Böylece okumak istediğiniz her değer için model.FirstName = Request.Form["FirstName"] benzeri gereksiz tekrarlamalara girmemiş olacağız.

Aşağıda SuperModel modeliyle çalışan bir aksiyon tanımı mevcut;

public ActionResult SaveModel(SuperModel superModel) { ... }

Model bağlama işleminde seçici geçirgenlik

Veritabanına süper model kayıt etmeyi sağlayan bir html formumuz olduğunu varsayalım. Sorumlu kişi bu form ile ad, soyad, boy ve beden ölçüleriyle birlikte süper model kayıtları oluşturuyor. Süper modelin boyunun sorumlu kişi tarafından kayıt edilmesini istemiyoruz. Aksiyonumuz hemen yukarıdaki şekilde tanımlanmış yani SuperModel parametresiyle çalışıyor. Height bilgisinin DefaultModelBinder tarafından algılanmasını engellemek için görünümdeki html formundan Height için oluşturulmuş olan input kontrolünü kaldırıyoruz. Yani form aşağıdaki şekilde tanımlanıyor;

    <h2>SaveModel</h2>

    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>

        <fieldset>
            <legend>Fields</legend>
            
            <div class="editor-label">
                <%: Html.LabelFor(model => model.FirstName) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.FirstName) %>
                <%: Html.ValidationMessageFor(model => model.FirstName) %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(model => model.LastName) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.LastName) %>
                <%: Html.ValidationMessageFor(model => model.LastName) %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Bust) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Bust) %>
                <%: Html.ValidationMessageFor(model => model.Bust) %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Waist) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Waist) %>
                <%: Html.ValidationMessageFor(model => model.Waist) %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Hips) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Hips) %>
                <%: Html.ValidationMessageFor(model => model.Hips) %>
            </div>
            
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>

    <% } %>

Form tanımında Height parametresine ait bir tanım olmadığına dikkat edin. Kullanıcı tarafından Height değerinin girilmesini engellemiş oluyoruz, ancak gerçekten Height değerinin alınmasına engel oluyor muyuz? Hayır olmuyoruz. Kullanıcı kayıt formunu postalarken, iyi niyetli bir kullanıcı(!) ise, action url parametresine ?Height=178 parametresini ekleyerek kayıt ettiği süper modelin 1.78 boyunda olmasını sağlayabilir. DefaultModelBinder, Request.Form içinde bulamadığı Height değerini, Request.QueryString içinden alacak ve dinamik olarak oluşturduğu SuperModel nesnesine bağlayacaktır. DefaultModelBinder'a Height parametresini bağlama işleminin dışında tutmasını bildirmek için Bind öz niteliğini kullanıyoruz. Güncel aksiyon tanımımız şu şekilde;

public ActionResult SaveModel([Bind(Exclude = "Height")]SuperModel superModel) { ... }

Eğer Height parametresini sadece SaveModel aksiyonu için değil, SuperModel nesnesinin kullanıldığı her aksiyonda bağlama işlemi dışında bırakmak istiyorsak, bu durumda Bind öz niteliğini sınıf üzerinde kullanabiliriz;

[Bind(Exclude = "Height")]
public class SuperModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Height { get; set; }
    public int Bust { get; set; }
    public int Waist { get; set; }
    public int Hips { get; set; }
}

Model bağlama ve koleksiyonlar

DefaultModelBinder sadece primitif ve kullanıcı tanımlı veri tipleri ile çalışmıyor. Kullanıcı tarafından post edilen verilerden diziler, koleksiyonlar ve sözlükler oluştururabilme yeteneğine de sahiptir. Birkaç örnekle bu özellikleri inceleyelim.

Aşağıda üç adet checkbox içeren bir form tanımı görüyorsunuz. Üç checkbox'ın da name özelliği, yani isimleri aynı; source.

<% using (Html.BeginForm()) { %> 

    Gazete: <input type="checkbox" name="source" value="gazete" /><br />
    Arama Motoru: <input type="checkbox" name="source" value="arama_motoru" /><br />
    Reklam: <input type="checkbox" name="source" value="reklam" /><br />

    <input type="submit" value="Gönder Bakalım" />
<% } %>

Formdan gelen bilgiyi alacak olan aksiyon tanımı da hemen aşağıda:

public ActionResult Registration(List<string> source) { ... }

Bu sayfaya gidip, karşımıza gelen formdaki üç checkbox'tan örneğin sadece Arama Motoru ve Reklam kutucuklarını işaretleyip formu postaladığımızda, aksiyondaki source parametresinde iki farklı string olduğunu ve sırasıyla değerlerinin arama_motoru ve reklam olduğunu göreceğiz. DefaultModelBinder, formdan gelen ve aynı isme sahip olan değerleri FormValueProvider kullanarak bir string listesine attı ve böylece biz de teker teker seçilmiş olan değerlere ulaşabildik.

Son bir örnek olarak, az önce yaptığımız string listesi uygulamasını süper modellerimizle gerçekleştirelim. Bir dizi süper model bilgisini form üzerinden sunucuya postalayan bir görünüm ve postalanmış bilgiler içinden süper modelleri ayıklayabilecek bir aksiyon tanımı yapalım.

    <h2>SaveModels</h2>

    <% using (Html.BeginForm()) { %> 

        <% for (int i = 0; i < 10; i++) { %>
        
        <fieldset>
            <legend><%: i %>. Süper Model</legend>
            
            <div class="editor-label">
                <%: Html.Label(string.Format("model[{0}].FirstName", i)) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBox(string.Format("model[{0}].FirstName", i)) %>
            </div>
            
            <div class="editor-label">
                <%: Html.Label(string.Format("model[{0}].LastName", i)) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBox(string.Format("model[{0}].LastName", i)) %>
            </div>
            
            <div class="editor-label">
                <%: Html.Label(string.Format("model[{0}].Bust", i)) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBox(string.Format("model[{0}].Bust", i)) %>
            </div>
            
            <div class="editor-label">
                <%: Html.Label(string.Format("model[{0}].Waist", i)) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBox(string.Format("model[{0}].Waist", i)) %>
            </div>
            
            <div class="editor-label">
                <%: Html.Label(string.Format("model[{0}].Hips", i)) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBox(string.Format("model[{0}].Hips", i)) %>
            </div>    
         
        </fieldset>                  
  
        <% } %>

        <p>
            <input type="submit" value="Create" />
        </p>  

    <% } %>

Yukarıdaki form tanımında bir döngü ile 10 adet süper model kaydı yapılabilmesi için kontrol serisi oluşturuyorum. Kontrollerin isimlendirmesine lütfen dikkat. Her bir model için model[0].FirstName, model[5].Waist şeklinde isimleri olan input kontrolleri oluşturmuş oluyorum. input kontrollerine bu şekilde isim vermek tamamen kurallar dahilinde. DefaultModelBinder, dizi şeklinde bir isimlendirmeyle karşılaştığında, aksiyon parametresi ile eşleştirmek için indeksleyicilerin hemen önündeki ismi parametre ismi olarak alıyor, yani bizim senaryomuzda aksiyon parametresinin adı model olmalı.

Aşağıda aksiyon tanımı mevcut:

[HttpPost]
public ActionResult SaveModels(List<SuperModel> model)
{
    return View();
}

Form postalandığında, DefaultModelBinder devreye giriyor, postalanan verilerden sırasıyla 10 adet SuperModel nesnesi oluşturuyor ve her bir fieldset içinden gelen veriyi bir SuperModel nesnesine bağlıyor. Gördüğünüz gibi çok dinamik ve esnek bir yapı mevcut. ASP.NET Webforms yapısından çok daha esnek.

Konumuz burada bitmez. Zira model bağlayıcı ve değer sağlayıcı konusu üzerinde daha konuşulabilecek bir konu. İlerleyen yazılarda, MVC'nin her noktasında olduğu gibi bu sistemi nasıl genişletebiliriz, IValueProvider ve IModelBinder arayüzlerini kullanarak nasıl model bağlayıcı ve değer sağlayıcı geliştirebiliriz inceleyeceğiz.

Asp.Net Mvc
|

OCS 2007 R2 ve "Limited External Call" uyarısı

Önsel Akın, 18 Ağustos 2010 Çarşamba

And the Oscar goes to...

Microsoft ürünleri arasında, Kurulum ve Uygulama dalında En Zor Kuruluma Sahip Ürün Oscar'ını Office Communications Server'a uygun görüyorum.

İki gündür kurum içi ve kurum dışı kullanıcılar arasında ses/görüntü bağlantısının sağlanamaması problemi üzerinde uğraşıyorum. Genelde bu problem harici kullanıcıların IP adreslerinin içteki kullanıcılara lokal adres olarak gönderilmesinden kaynaklanıyor(du). İlk baktığım problem bu olurdu ve hemen çözerdim ancak bu kez öyle olmadı. Hem ses/görüntü bağlantısı sağlanamıyor hem de Communicator "Limited External Call" uyarısı veriyordu.

Kurulumu hazır sanal makine imajlarından yapmıştım ve Administrator hesabının bölgesel ayarlarının Türkçe olması dikkatimi çekmemişti. Ayarların Türkçe olması, Edge Server'ın Authentication token'ı oluştururken Türkçe formatlama kullanmasına ve kullanıcı doğrulamasının doğru(!) çalışmamasına neden oluyormuş. Aşağıdaki adresteki hotfix'i indirip uyguladım ancak nafile.

http://support.microsoft.com/kb/968938/

Kullanıcı doğrulama, Edge Sunucusu üzerinde RTCProxyService hesabı ile gerçekleştiriliyor. RTCProxyService hesabının bölgesel ayarlarını English (United States) olarak değiştirdim ve mutlu son! :)

Başınıza gelirse belki bu postayı hatırlarsınız.

Office Communications Server
|

SharePoint 2010 ve Service Locator Pattern - 1

Önsel Akın, 18 Ağustos 2010 Çarşamba

Konumuz, SharePoint üzerinde geliştirme yapılırken genelde gözardı edilen, ya da birçok geliştiricinin şimdiye kadar pek farketmemiş olduğu SharePoint Service Locator arayüzü.

SharePoint üzerinde uygulama geliştirmenin aslında temel anlamda ASP.Net uygulamaları geliştirmekten pek farkı yok. SharePoint ile gelen ve birçok uygulamada hız kazandıran araçları kullanmak dışında aslında yaptığınız şey ASP.Net uygulaması geliştirmek. Bu açıdan bakıldığında, herhangi bir tasarım deseni kullanmadan ASP.Net uygulaması geliştirmek ne kadar bünyeye(!) zararlıysa, aynı şey SharePoint için de geçerli. En çok kullanılan SharePoint özelliği olan SharePoint listelerini ele alalım. Arayüzden birkaç tıklama ile veri saklama amaçlı listeler oluşturup, yine birkaç tıklama ile bu listeler arasında ilişkiler tanımlayıp ve yine birkaç tıklama ile bu listelere uygun otomatik oluşturulmuş formlar üzerinden yeni veriler saklamak mümkün. Dikkat! "Eğer birkaç tıklama ile işler bu kadar hızlı yürüyebiliyorsa, dikkat edilmediğinde yine aynı hızda işler kontrolden çıkabilir."

Her platformda olduğu gibi, SharePoint'te de yazılım geliştirmenin bir adabı olmalı. Ben de IoC konusu ile giriş yaparak, ileriki yazılarımda da SharePoint listeleri üzerinde generic repository pattern uygulamaları, Business Connectivity Services modelleme, SharePoint ve birim testleri gibi konularla bu bahsettiğim yazılım geliştirme adabının nasıl olması gerektiğinden bahsedeceğim.

Yazıyı okumaya başlamadan önce, eğer konuya hakim değilseniz öncelikle Ioc (Dependency Injection) nedir okuyup anlamanızı tavsiye ederim. Konuyla ilgili birçok Türkçe ve yabancı dilde kaynak bulabilirsiniz.

SharePoint Service Locator (SPSL)

Öncelikle, başlıkta geçen Service ifadesinin web servisi ya da WCF servisi olmadığını belirtmekte fayda var. Burada Servis ile kastedilen şey belli bir arayüzü uygulayan bir bileşen, daha C# ifadesi ile, bir sınıf. SPSL ise, belli bir yöntemle (konfigürasyon dosyası kullanarak ya da programatik olarak) kayıt edilmiş olan servislerin bulunması ve kullanımınıza sunulmasını sağlayan başka bir SharePoint servisi. Nihai amaç ise uygulama geliştirirken kullandığınız kütüphanelerin belli uygulamalarına olan bağımlılığı ortadan kaldırmak, modüler hale getirmek, modüler hale getirdiğiniz bileşenleri sadece konfigürasyon üzerinde düzenleme yaparak değiştirilebilir hale getirmek. Birazdan vereceğim örneklerle konuyu biraz daha net ifade edebileceğim.

SharePoint Service Locator nasıl çalışır?

SPSL'in çalışma prensibinin temelinde yatan şey aslında çok basit bir Dictionary'dir. Bu Dictionary, içinde bir arayüz (Interface) ve bu arayüze karşılık, bu arayüzü uygulayan bir tip adından oluşan anahtar-değer çiftlerini (ya da teknik ismiyle tip eşleşmelerini) saklar. Aşağıda bu Dictionary için birkaç kayıt örneği vererek sürecin nasıl işlediğinden bahsedelim:

Arayüz Tip
ILogger SharePointLogger*
IConfigManager ConfigManager
ICustomerRepository SPCustomerRepository

* SharePointLogger ve ConfigManager SharePoint ile hazır olarak gelen iki bileşen. ICustomerRepository ve SPCustomerRepository ise benim örnek vermek amacıyla uydurduğum isimler. Örneğin daha iyi anlaşılabilmesi için SPCustomerRepository nesnelerinin SharePoint listeleri üzerinde müşteri bilgilerini yönetmek amaçlı kullanılan bir sınıf olduğunu varsayalım.

Yukarıdaki yapılandırmaya göre, bir ICustomerRepository nesnesine ihtiyaç duyduğumda, bunu doğrudan bir SPCustomerRepository nesnesi oluşturarak yapmak yerine, SPSL'dan bana bir ICustomerRepository nesnesi vermesini isteyerek gerçekleştiriyorum. SPSL, tip eşleştirmeleri sözlüğüne bakarak, bir ICustomerRepository nesnesi istendiğinde hangi tipten bir nesne oluşturup döndüreceğini belirliyor, nesneyi oluşturuyor ve bana teslim ediyor. Bir kod örneği ile anlatacak olursak;

SPCustomerRepository repository = new SPCustomerRepository();

yerine;

IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(); ICustomerRepository repository = serviceLocator.GetInstance<ICustomerRepository>();

yazarak, SPCustomerRepository nesnesinin SPSL tarafından inşa edilmesini ve bana teslim edilmesini sağlamış oluyorum. Dikkat edilmesi gereken nokta, artık yazdığım kod içinde SPCustomerRepository tip adını hiç kullanmamış olmam, başka bir deyişle artık SPCustomerRepository tipine bağımlılığımın kalmamış olması. Ayrıca ileride ihtiyaçlar değişir de müşteri bilgilerini artık SharePoint listeleri üzerinde değil de bir SQL Server veritabanı üzerinde saklamak gerekirse, ICustomerRepository arayüzünü uygulayan bir SQLCustomerRepository sınıfı yazılıp, SPSL'e artık bir ICustomerRepository nesnesi istendiğinde SPCustomerRepository değil SQLCustomerRepository nesneleri döndürmesi söylenebilir. Böylece yazılan kodun büyük bir kısmı değişmeden kalırken, sadece konfigürasyon kullanarak SharePoint üzerinde çalışan uygulama, SQL Server üzerine minimum etki ile taşınabilir.

Service Locator'a ulaşmak

Yukarıda anlatmış olduğum tüm işlemlerin çalışmasını sağlayan Service Locator nesnesi de aslında IServiceLocator arayüzünü uygulayan bir nesne. SharePoint çalıştığı sürece, arka planda daima tek bir kopyası aktif olan bir IServiceLocator nesnesi mevcut. Tip eşleştirmelerini kayıt etmek, SPSL'dan nesne talep etmek vb. gibi işlemlerin tamamı bu tek SPSL tarafından gerçekleştirilir. Bu tekil SPSL'e erişmek için ise SharePointServiceLocator adındaki statik sınıf kullanılır. Bir SPContext altında çalışan koddan bir IServiceLocator örneği elde etmek için aşağıdaki ifade kullanılır:

IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();

Eğer bir SPContext altında değilseniz (bir feature ya da Object Model ile SPSL'e erişmeye çalışırken olabilir bu),

IServiceLocator locator = SharePointServiceLocator.GetCurrent(spSite);

ile GetCurrent metoduna parametre olarak bir SPSite nesnesi geçebilirsiniz.

Tip eşleştirmelerinin kayıt edilmesi

Uygulamalardan ulaşmak istediğiniz servis arayüzlerini ve bu arayüzlerin eşleştiği sınıfları önce SPSL'a kayıt etmelisiniz. Kayıt işlemini gerçekleştirmek için daha önce bahsettiğim tekil IServiceLocator nesnesine ulaşmanız gerekli. Aşağıda bunun yöntemini görebilirsiniz:

IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent(); IServiceLocatorConfig typeMappings = serviceLocator.GetInstance<IServiceLocatorConfig>(); typeMappings.RegisterTypeMapping<ICustomerRepository, SPCustomerRepository>();

Yukarıdaki ifadelerle, Service Locator nesnesine, bundan böyle bir ICustomerRepository nesnesi talep edildiğinde, bir SPCustomerRepository nesnesi döndürmesi gerektiğini söylemiş oluyoruz. Kayıt işleminin bir IServiceLocatorConfig nesnesi üzerinden yapıldığına dikkat edin.

Eğer servisi tekil (singleton) olarak kayıt etmek isterseniz IServiceLocator nesnesini bir ActivatingServiceLocator nesnesine döndürmelisiniz. Aşağıdaki örnekte tekil bir nesnenin nasıl kayıt edilebileceğini görebilirsiniz:

ActivatingServiceLocator serviceLocator = (ActivatingServiceLocator)SharePointServiceLocator.GetCurrent(); serviceLocator.RegisterTypeMapping<ICustomerRepository, SPCustomerRepository>(InstantiationType.AsSingleton);

Peki tekil kayıt etmek ne anlama gelmektedir? IServiceLocator.GetInstance<T>() metodu her çağırıldığında yeni bir nesne oluşturur ve döndürür. Yani her talebinizde elinizde tamamen yeni bir nesne vardır. Nesnelerinizi tekil kayıt ederseniz eğer, Service Locator kayıt ettiğiniz arayüze sahip tek bir nesne örneği oluşturur ve her GetInstance ile nesne talep ettiğinizde size o tek örneği döndürür.

Proje için ek bilgi

Yukarıda anlattığım tüm özellikleri canlı gösteren bir projeyi yazı ekinde bulabilirsiniz. Visual Studio 2010 ile çözümü açıp Deploy seçeneği ile SharePoint'e aktarmanız yeterli. Projede, bir Feature Event Receiver kullanarak Service Locator Sample Feature aktive edildiğinde Service Locator üzerinden tip kayıtlarını gerçekleştiriyorum. Müşteri listesini gösteren bir Visual Web Part ile de, önce kayıt ettiğim ICustomerRepository nesnesini talep edip, ardından repository'den gelen kayıtlarla web part üzerindeki bir SPGridView nesnesini dolduruyorum. Projedeki web part'I bir sayfaya ekleyerek sonucu görebilirsiniz. Projeyi indirmek için tıklayın.

Sonraki yazıda, SharePoint Guidance içinde gelen standart Service Locator nesnelerine alternatif, çok daha güçlü bir IoC container olan Unity ile Dependency Injection olayını inceleyeceğiz.

Kaynak: SharePoint Guidance 2010

SharePoint
|
Shift to the left, shift to the right! Pop up, push down, byte, byte, byte! [10:14 06.05.2010 Twitter]
Silverlight MVVM egitimi vermekle mesgulum. [09:34 04.05.2010 Twitter]
Business Entities & Business Entity Definition Language (pdf) http://bit.ly/9Y67Rw http://ff.im/jD3Bx [20:29 30.04.2010 Twitter]
Hayatimiz sayisiz veriden meydana geliyorken hala veriyi taniyip yonetmeyi ogrenememis olmamiz acayip. [12:37 30.04.2010 Twitter]
« Eski