Makale Özeti

Geliştirdiğimiz çoğu uygulamada kullanıcılara veya yöneticilere rapor veya bilgilendirme mail'i yollmaktayız. İşte bu makalede bu mailleri bir template'den okuyarak yollayan bir class yazacağız. Yazacağımız class gönderilecek mail içerisindeki değişkenleri parametre olarak alacak ve template'de bulunan yazım kuralına göre uygun yerlere koyacaktır. Makalemde değinmeyeceğim ancak bu template dosyalarının veritabanında tutulması sonucu maillerde değişikliği müşteri kendi istediği şekilde yapabilir olacaktır. Ayrıca mail'de yeni bir alanında gözükmesi isteniyorsa ve bu alanın bulunduğu nesne zaten parametre olarak mailde varsa kod tarafında bir değişiklik yapmanıza gerek kalmayacaktır. Ayrıca bu yöntemi sadece mail oluşturmakta değil herhangi bir metni parametrik oluşturmakta da kullanabilirsiniz.

Makale

         Merhabalar,

         Geliştirdiğimiz çoğu uygulamada kullanıcılara veya yöneticilere rapor veya bilgilendirme mail'i yollmaktayız. İşte bu makalede bu mailleri bir template'den okuyarak yollayan bir class yazacağız. Yazacağımız class gönderilecek mail içerisindeki değişkenleri parametre olarak alacak ve template'de bulunan yazım kuralına göre uygun yerlere koyacaktır. Makalemde değinmeyeceğim ancak bu template dosyalarının veritabanında tutulması sonucu maillerde değişikliği müşteri kendi istediği şekilde yapabilir olacaktır. Ayrıca mail'de yeni bir alanında gözükmesi isteniyorsa ve bu alanın bulunduğu nesne zaten parametre olarak mailde varsa kod tarafında bir değişiklik yapmanıza gerek kalmayacaktır. Ayrıca bu yöntemi sadece mail oluşturmakta değil herhangi bir metni parametrik oluşturmakta da kullanabilirsiniz.

         Şimdi isterseniz mail şablonumuzu oluştururken uymamız gereken kuralları inceleyelim.
         1)Şablon içerisinde bulunacak tüm parametreler <%%> tag'ları arasında olmalıdır.
         2)Bir class(entity) içerindeki değeri template'imize koymak için ParametreAdı.Propertyİsmi şeklinde tanımlama yapmalısınız. Örneğin Kullanıcının adının template içinde parametrik olmasını istiyorsanız <%Employee.EmployeeName%> şeklinde bir tanımlama yapmanız gerekmektedir.
         3)Yazacağımız class DataTable tipindeki parametreleri direkt html koduna çevirebilmekte olacaktır. Dolayısıyla bir datatable'ı parametrik mailinizin içine koymak istiyorsanız bunu sadece parametre ismini tanımlayarak yapabilirsiniz. Örneğin.<%DataTableOzetRapor%> bu kod sonucunda bir html çıktısı üzetilir.
         4) Eğer template içine bir string'i paramtere olarak koymak istiyosanız bunun için de DataTable da olduğu gibi sadece parametre adını geçirmelisiniz. Örneğin <%BasariliDurum%>.

         Örnek bir template dosyasını inceleyelim.
         Sayın <%Employee.EmployeeFirstName%> <%Employee.EmployeeLastName%><br><br> <%ExamRequests.Exam%> sınav talebiniz <%OnayDurumu%>. Daha önceki sınav talepleriniz aşağıda listelenmiştir<br><br> <%PreviousRequests%>
        
Bu template de Employee, ExamRequests nesneleri, OnayDurumu string değeri ve PreviousRequests datatable'ı parametre olarak geçmiştir. Ve işlem sonucunda gönderdiğimiz bilgilere göre mail mesajı oluşturulacaktır. Bu Mesajın Çıktısı ise,
  


Sayın Tamer Öz

70-536 sınav talebiniz onaylanmıştır. Daha önceki sınav talepleriniz aşağıda listelenmiştir

SinavAdiDurum
70-229Onaylandı

         Şeklinde olacaktır. Başka bir kullanıcı için mailin gönderilmesini istediğinizde bu parametreleri değiştirmek yeterli olacaktır.

Bu anlattıklarımı daha iyi aktarabilmek için isterseniz class'ı incelemeye başlayalım.

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Net.Mail;
using System.Data;
 
namespace MailGeneratorExample
{
    public class MailGenerator
    {
        private List<string> _To = new List<string>();
        private List<string> _Cc = new List<string>();
        private List<string> _Bcc = new List<string>();
        private string _From = System.Configuration.ConfigurationManager.AppSettings["MailFrom"];
        private string _Body;
        private string _Subject;
        private Hashtable _Parameters = new Hashtable();


         Bu kısımda öncelikle kullanacağımız property'lere ait member değişkenlerimizi yazıyoruz. Bu değişkenler mailde bulunacak Tc,Cc,Bcc,From(Varsayılan Config dosyasından okunuyor) Body ve subject tir. En önemli değişken Hashtable tipinde olan ve template'den metni oluştururken parametre değerlerini okuma aşamasında kullanacağımız parameters değişkenidir. Kullanımına daha sonra değineceğiz ancak Parametre ile bu parametrede bulunan nesneyi key value pair tipinde tutacağımızdan şimdilik bahsetmek yeterli olacaktır.
 

        public List<string> To { get { return _To; } set { _To = value; } }
        public List<string> Cc { get { return _Cc; } set { _Cc = value; } }
        public List<string> Bcc { get { return _Bcc; } set { _Bcc = value; } }
        public string From { get { return _From; } set { _From = value; } }
        public string Body { get { return _Body; } set { _Body = value; } }
        public string Subject { get { return _Subject; } set { _Subject = value; } }
        public Hashtable Parameters { get { return _Parameters; } set { _Parameters = value; } }


         Bu kısımda ise biraz önce tanımladığımız değişkenleri encapsule eden property'leri yazıyoruz.
 

        private List<string> GetParametersFromBody(string str, string startTag, string endTag)
        {
            List<string> l = new List<string>();
            while (str.IndexOf(startTag) > -1)
            {
                try
                {
                    int startIndex = str.IndexOf(startTag) + startTag.Length;
                    int resultLength = str.IndexOf(endTag, startIndex) - startIndex;
                    l.Add(str.Substring(startIndex, resultLength));
                }
                catch (Exception)
                {
 
                }
                str = str.Remove(0, str.IndexOf(endTag) + endTag.Length);
            }
            return l;
        }

        
         GetParametersFromBody metodu adından da anlaşılacağı gibi bir başlangıç ve bitiş kuralına göre bu kurala uyan metinler arasındaki değerleri bir liste halinde bize geri döndüren metoddur. Bu metod substring ve indexof metodları üzerine kurulmuş bir metoddur.
 

        private string FindParameterValue(string parameterName, string PropertyName)
        {
            if (Parameters.ContainsKey(parameterName))
            {
                if (Parameters[parameterName] is DataTable)
                {
                    return GenerateHtml((DataTable)Parameters[parameterName]);
                }
                else if (Parameters[parameterName] is string)
                {
                    return Parameters[parameterName].ToString();
                }
                else
                {
                    return Parameters[parameterName].GetType().GetProperty(PropertyName).GetValue(Parameters[parameterName], null).ToString();
                }
            }
            return "";
        }


         FindParameterValue metodunun görevi template de belirtilmiş bir parametre ve bu parametreye ait property'de bulunan değeri Parameters Hashtable'ından okuyup getirmektir. Bunun için öncelikle metoda parametre olarak gelen ParameterName propertysşnşn Hashtable da bulunup bulunamadığı kontrol ediliyor. Sonrasında bu isimde bulunan parametrenin DataTable tipinden olması durumunda Bu DataTable GenerateHtml metoduna gönderilerek metoddan geri döndürülüyor. Eğer parametre değeri string ise olduğu gibi yazılıyor iken parametre değeri bu tiplerin dışında bir nesne ise bu nesnenin bu metoda PropertyName parametresinde gelen değeri okunuyor ve metoddan geri döndürülüyor.
 

        private string GenerateHtml(DataTable parDataTable)
        {
            int columnCount = 0;
            StringBuilder sb = new StringBuilder();
            sb.Append(@"<table border=1 border-color=Black>");
            sb.Append(@"<tr style=background-color:Aqua;Color:White>");
            foreach (DataColumn dc in parDataTable.Columns)
            {
                sb.Append("<td align=center>");
                sb.Append(dc.ColumnName.ToString());
                sb.Append("</td>");
                columnCount++;
            }
            sb.Append("</tr>");
            if (parDataTable.Rows.Count > 0)
            {
                for (int i = 0; i < parDataTable.Rows.Count; i++)
                {
                    if (i % 2 == 0)
                    {
                        sb.Append(@"<tr style=background-color:Gray;color:Black>");
                    }
                    else
                    {
                        sb.Append(@"<tr style=background-color:White;color:Black>");
                    }
                    foreach (DataColumn dc in parDataTable.Columns)
                    {
                        sb.Append("<td>");
                        sb.Append(parDataTable.Rows[i][dc].ToString());
                        sb.Append("</td>");
                    }
                    sb.Append("</tr>");
                }
            }
            else
            {
                sb.Append("<tr><td colspan=" + columnCount.ToString() + ">Kayıt Bulunamadı</td></tr>");
            }
 
            sb.Append("</table>");
            sb.Append("<script language='javascript'>window.print()</script>");
            return sb.ToString();
        }


         Bu kısım ise elimizde bulunan DataTable'ın şemasını ve verilerini okuyarak bir HTML oluşturmaktayıs. Bu metodun çalışma prensibi olarak DataTable üzerinde bulunan her veriyi aralarına <tr> veya <td> tagları ekleyerek bir html tabloya dönüştürmesidir.Bu metod hakkında daha fazla bilgiyi ExtendedDataTable ismindeki makalemde bulabilirsiniz.
 

        public void SendMail()
        {
            MailMessage insMailMessage = new MailMessage();
            insMailMessage.IsBodyHtml = true;
            foreach (string str in this.To){insMailMessage.To.Add(str);}
            foreach (string str in this.Cc){insMailMessage.CC.Add(str);}
            foreach (string str in this.Bcc){insMailMessage.Bcc.Add(str);}
            insMailMessage.From = new MailAddress(this.From);
            insMailMessage.Body = this.Body;
 
            List<string> s = GetParametersFromBody(insMailMessage.Body, "<%", "%>");
            foreach (string str in s)
            {
                string parameterName = "";
                string propertyName = "";
                if (str.IndexOf(".") > -1)
                {
                    parameterName = str.Substring(0, str.IndexOf("."));
                    propertyName = str.Substring(str.IndexOf(".") + 1);
                }
                else
                {
                    parameterName = str;
                }
 
                string val = FindParameterValue(parameterName, propertyName);
                insMailMessage.Body = insMailMessage.Body.Replace("<%" + str + "%>", val);
            }
            insMailMessage.Subject = this.Subject;
            SmtpClient insSmtpClient = new SmtpClient(System.Configuration.ConfigurationManager.AppSettings["SMTPClient"]);
            insSmtpClient.Send(insMailMessage);
        }


         Şimdi ise tüm işlerimizi yapacağımız SendMail metodunu yazalım.Bu metodda öncelikle bir MailMessage nesnesi oluşturuyoruz ve sonrasında property'lerimizdeki değerleri bu nesnenin property'lerine atıyoruz. Daha önceden classımızın Body property'sine yazdığımız template metnini GetParametersFromBody metoduna yolluyor ve buradan gelen değerleri bir döngü içerisinde FindParameterValue metoduna yollayarak bu parametrenin değerini alıyoruz. Yazım kuralımız gereği nesneler ParametreAdı.Property adı şeklinde geçirildiği için bu metodu çağırmadan önce . karakterine göre parametre adını ve property adını ayrıştırıyoruz. Sonrasında ise gelen değeri body değeri içinde replace ederek nihai metni elde ediyoruz ve oluşturduğumuz SMTPClient nesnesi üzerinden mailimizi gönderiyoruz.

         Örnek bir uygulama ile bu classın nasıl kullanıldığını inceleyelim.
 

    public class Employee
    {
        private int _EmployeeId;
        private string _EmployeeFirstName;
        private string _EmployeeLastName;
        public int EmployeeId { get { return _EmployeeId; } set { _EmployeeId = value; } }
        public string EmployeeFirstName { get { return _EmployeeFirstName; } set { _EmployeeFirstName = value; } }
        public string EmployeeLastName { get { return _EmployeeLastName; } set { _EmployeeLastName = value; } }
    }
    public class ExamRequest
    {
        private int _ExamRequestId;
        private string _Exam;
        private Employee _Employee;
        public int ExamRequestId { get { return _ExamRequestId; } set { _ExamRequestId = value; } }
        public string Exam { get { return _Exam; } set { _Exam = value; } }
        public Employee Employee { get { return _Employee; } set { _Employee = value; } }
    }
        private void Form1_Load(object sender, EventArgs e)
        {

            Employee insEmployee = new Employee();
            insEmployee.EmployeeId = 1;
            insEmployee.EmployeeFirstName = "Tamer";
            insEmployee.EmployeeLastName = "Öz";
 
            ExamRequest insExamRequest = new ExamRequest();
            insExamRequest.ExamRequestId = 1;
            insExamRequest.Employee = insEmployee;
            insExamRequest.Exam = "70-536";
 
            string OnayDurumu = "onaylanmıştır";
 
            DataTable insDataTable = new DataTable();
            insDataTable.Columns.Add("SinavAdi", typeof(string));
            insDataTable.Columns.Add("Durum", typeof(string));
            insDataTable.Rows.Add(new object[] { "70-229", "Onaylandı" });
            string template = "Sayın <%Employee.EmployeeFirstName%> <%Employee.EmployeeLastName%><br><br> <%ExamRequest.Exam%> sınav talebiniz <%OnayDurumu%>. Daha önceki sınav talepleriniz aşağıda listelenmiştir<br><br> <%PreviousRequests%>";
 
            MailGenerator insMailGenerator = new MailGenerator();
            insMailGenerator.From = "oztamer@hotmail.com";
            insMailGenerator.To.Add("oztamer@hotmail.com");
            insMailGenerator.Subject = "Sınav Talebiniz";
            insMailGenerator.Parameters.Add("Employee", insEmployee);
            insMailGenerator.Parameters.Add("ExamRequest", insExamRequest);
            insMailGenerator.Parameters.Add("OnayDurumu", OnayDurumu);
            insMailGenerator.Parameters.Add("PreviousRequests", insDataTable);
            insMailGenerator.Body = template;
            insMailGenerator.SendMail();
 
        }


         Örnekte gördüğünüz gibi öncelikle nesnelerimizi dolduruyoruz ve doldurduğumuz nesneye bir parametre adı vererek yeni yazdığımız class'a geçiriyoruz ve işlem gerçekleşiyor. yukarıda yazmış olduğum sonucu bu şekilde elde edebiliyoruz.

         Dediğim gibi sadece mail oluşturmada değil herhangi bir şekilde parametrik text oluşturma kısımlarında kullanabilirsiniz.

         oztamer@hotmail.com
         tamer.oz@yazgelistir.com
         oztamer@hotmail.com