C# da runtimeda tip bilgilerine erişebilmemize yarar.Kütüphane olarak System.Reflection namespace altındadır
Reflection ile runtimeda classın fieldların,propertilerin özelliklerine vs erişebilir ve ona göre işlemler yapabiliriz.ORM araçlarının temel mantığıda bunun üstüne kuruludur.
Örnek olarak veritabanımızdaki bir tablonun nesne olarak modellemesini yapalım
Kod:
public class CustomerEntity :
{
private int _CustomerID;
private string _Name;
private string _Surname;
private DateTime? _BirthDate;
[EntityPrimaryKey]
public int CustomerID
{
get
{
return _CustomerID;
}
}
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
public string Surname
{
get
{
return _Surname;
}
set
{
_Surname = value;
}
}
public DateTime? BirthDate
{
get
{
return _BirthDate;
}
set
{
_BirthDate = value;
}
}
}
Görüldüğü gibi Veritabanımda CustomerID,Name,Surname,BirthDate olarak var olan 4 alanın kod tarafında nesne olarak modellemesini yaptım.Bunlardan CustomerID nin bir CustomAttribute ile primary Key olduğunu belirttim.Tablomun adıda Customer olsun.
Not:C# da Custom Attribute Tanımlama,yapmanız gereken sadece AttributeUsage Attribute kısmını sınıfın üstüne yazmak ve Sınıfınızın Attributedan türemesini sağlamaktır.Bu sayede artık biz de kendi kullanımımıza göre bir Attribute yaratmış olduk
Kod:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class EntityPrimaryKey:Attribute
{
}
Benim senaryoma göre CustomerID de otomatik artan ve integer olsun
Formda butonun arkasına
Kod:
CustomerEntity b = new CustomerEntity();
b.Name = "Bilgehan";
b.Surname = "Yıldız";
int a= EntityProcess<CustomerEntity>.Save(b);
yukarıdaki gibi kodumuzu yazalım.Ve EntityProcess sınıfımıza gelerek sınıfımızı oluşturalım.Ben yine ORM araçlarındaki gibi dinamik bir Save metodu yapmak istiyorum bunun için sınıfımı aşağıdaki gibi tanımlıyorum.Bu şekilde artık sınıfım generic olarak benim nesnelerimi alabilecek hale geliyor
Kod:
public class EntityProcess<TEntity> where TEntity : class, new()
public static int Save(TEntity entity)
{
PropertyInfo[] properties = typeof(TEntity).GetProperties();
PropertyInfo ile Oluşturduğum Sınıfımın typeof(TEntity).GetProperties(); ile tüm propertylerini bir diziye alıyorum.Burada Tentity yerine üst katmandan çağırdığımızda CustomerEntity dolduruyor ve böylece dinamik olarak CustomerEntity classın içerisindeki propertylere ulaşabiliyoruz
Kod:
foreach (PropertyInfo p in properties)
{
//Aşağıdaki komut ilede eğer gelen propertimin PrimaryKey Attribute varsa insert queryimize eklemiyoruz
if (p.GetCustomAttributes(typeof(EntityPrimaryKey), false).Count() <= 0)
{
//Get Value ilede metoda gönderdiğimiz nesnedeki propertylerin değerini okuyabiliyoruz.örnek CustomerEntity de Name propertsini Bilgehan olarak setlemiştik.Aşapıdaki kodla Bilgehan değerini okuruz
object columnValue = p.GetValue(entity, null);
if (columnValue != null)
{
Gene Reflection yardımı ile bulabileceğimiz şeylerden birede propertyimizin Int mi,String mi olduğu,nullable olup olmadığı gibi kavramlardır
Mesela BirthDate i nullable olarak tanımlamıştık.Bir kolon nullable ise hangi tipte olduğunu bulmak için önce birinci ifteki gibi GetGenericTypeDefinition() ile nullable olup olmadığını kontrol etmeliyiz.
Ondan sonra ise ikinci ifteki gibi p.PropertyType.GetGenericArguments()[0].Name ilede tipini kontrol edebiliyoruz.
Kod:
if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
if (p.PropertyType.GetGenericArguments()[0].Name == "Int32")
{
return SqlDbType.Int;
}
Eğer propertyimiz nullable değilse aşağıdaki gibi kontroller yapabiliriz
PropertyType.Name demek yeterli oluyor
Kod:
if (p.PropertyType.Name == "Int32")
{
return SqlDbType.Int;
}
Son olarak genel olarak kodlarımıza göz atarsak TypeFinder benim yukarıda yazdığım şekilde propertnin Dbtype bulan bir sınıf.
Kod:
public class EntityProcess<TEntity> where TEntity : class, new()
{
public static int Save(TEntity entity)
{
EMDbManager db = new EMDbManager();
PropertyInfo[] properties = typeof(TEntity).GetProperties();
string insertSql = "insert into {0} ({1}) values({2});select scope_identity()";
StringBuilder columnNames = new StringBuilder();
StringBuilder parameterNames = new StringBuilder();
SqlCommand cmd = new SqlCommand();
foreach (PropertyInfo p in properties)
{
TypeFinder tp = new TypeFinder();
//Eger gelen entity bizim custom attribute ID içermiyorsa
//Otomatik Artan olmayan PrimaryKeyli yerlerde Custom PrimaryKey Attribute Olmayacak
if (p.GetCustomAttributes(typeof(EntityPrimaryKey), false).Count() <= 0)
{
object columnValue = p.GetValue(entity, null);
if (columnValue != null)
{
columnNames.Append(p.Name);
columnNames.Append(",");
parameterNames.Append("@");
parameterNames.Append(p.Name);
parameterNames.Append(",");
cmd.Parameters.Add(new SqlParameter(p.Name, columnValue, tp.FindType(p)));
}
}
}
insertSql = string.Format(insertSql, entity.GetType().Name.Replace("Entity", ""), columnNames.ToString().TrimEnd(','), parameterNames.ToString().TrimEnd(','));
En son string Format ile benim sınıfımın adı CustomerEntity entity kısmını replace yok ediyorum ve generic olarak bir insert cümlesi yukarıdaki gibi oluşturmuş oluyorum.