DAL(Data Access Layer) реализациясы

Бағдарламалау
Программа жасау кезінде код архитектурасының құрылымы өте маңызды. Солардың бірі — Үш деңгейлі архитектура жайында қысқаша айтатын болсақ, төмендегідей деңгейлерден тұрады:


  1. DAL (Data Access Layer) – мәліметтер қорынан мәліметтерді шығару, енгізу, өңдеумен айналысады
  2. BLL (Business Logic Layer) – қосымшаның қажеттіліктеріне сәйкес мәліметтерді өңдеумен айналысады. Мысалы, есептеулер жүргізу, т.б. DAL-айырмашылығы, ол тікелей DB-бен қатыспайды, өз ішінде мәліметтермен жұмыс істеу үшін DAL-ды қолданады.
  3. UI(User Interface) – ол клиентке(қолданушыға) мәліметтерді көрсетумен ғана айналысады.(Web-парақша, desctop, т.б.)
Әр деңгей өз ісіне ғана жауап беріп, тек қана көршілерімен қатысқаны дұрыс. Яғни, UI<->DAL немесе, DB<->BLL т.с.с байланыстыру жақсы емес.

Бұл жазбада DAL-класының кодын көрсетемін. Код толық зерттеліп, аяқталмаған. DAL реализациясының бір мысалы ретінде ұсынамын. Жалпы, идеяның кемшіліктерін жайында ой қосам деушілерге мархабат.
Сонымен, Project(Class Library) DAL проектысын қосамыз. Ішінде BaseObject.cs, DAL.cs кластарын құрамыз.

Мұндағы BaseObject.cs — барлық модель класстардың атасы.
namespace DAL
{
    public class BaseObject
    {
        public int Id { get; set; }

    }
}


DAL класына толығырақ тоқталсақ:
Мұнда екі field бар.
private static string connString = @"Data Source=localhost;Initial Catalog=MyDB.MDF;Integrated Security=True";
private static SqlConnection conn = null;

connString-ті кейін web.config-файлдан оқығандай етіп баптап қоюға болады.
Мәліметтер қорымен байланыс орнатып, команда жасау үшін арналған методтар:

private static void CreateConnection()
        {
            conn = new SqlConnection(connString);
        }

        private static void CloseConnection()
        {
            if (conn.State != ConnectionState.Closed) conn.Close();
        }

        private static SqlCommand CreateCommand()
        {
            CreateConnection();
            return conn.CreateCommand();
        }

        private static SqlCommand CreateCommand(string query)
        {
            CreateConnection();
            var cmd = conn.CreateCommand();
            cmd.CommandText = query;
            return cmd;
        }

Келесі метод мәліметтер қорынан жазбаларды шығарады (Select * from SomeTable):
public static T[] GetObjects<T>(string query) where T : BaseObject
        {
            var cmd = CreateCommand(query);
            if (conn.State != ConnectionState.Open) conn.Open();
            var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);

            List<T> lst = new List<T>();
            while (reader.Read())
            {

                T obj = (T)Activator.CreateInstance(typeof(T));
                var properties = typeof(T).GetProperties();
                foreach (var pi in properties)
                {
                    var val = Convert.ChangeType(reader[pi.Name], pi.PropertyType);
                    pi.SetValue(obj, val, null);
                }
                lst.Add(obj);
            }
            if (conn.State != ConnectionState.Closed) conn.Close();
            return lst.ToArray();
        }

ID бойынша бір объектіні шығару:
public static T GetObject<T>(int id) where T : BaseObject
        {
            string query = string.Format("select * from {0} where Id={1}", typeof(T).Name, id);
            var res = GetObjects<T>(query);
            return res.Count() > 0 ? res[0] : null;
        }

Мәліметтер қорына жазба енгізу(Insert into SomeTable Values(...)):
public static bool CreateObject(BaseObject obj)
        {
            var cmd = CreateCommand();
            var properties = obj.GetType().GetProperties();
            string query = string.Empty;
            var typeMap = GetTypeMap();
            foreach (var pi in properties)
            {
                if (pi.Name != "Id")
                {
                    object value = pi.GetValue(obj, null);
                    var dbtype = typeMap[pi.PropertyType];
                    var name = string.Format("@{0}", pi.Name.ToLower());
                    SqlParameter par = new SqlParameter() { ParameterName = name, DbType = dbtype, Value = value };
                    cmd.Parameters.Add(par);
                    query += query == string.Empty ? name : "," + name;
                }
            }
            cmd.CommandText = string.Format("insert into {0} values({1})", obj.GetType().Name, query);
            bool res = false;
            try
            {
                conn.Open();
                cmd.ExecuteNonQuery();
                res = true;
            }
            catch (Exception ex)
            {
                //TODO обработать исключение
            }
            finally
            {
                CloseConnection();
            }
            return res;
        }

Мәліметтерді жаңарту (Update SomeTable set ...):
public static bool UpdateObject(BaseObject obj)
        {
            var cmd = CreateCommand();
            var properties = obj.GetType().GetProperties();
            string query = string.Empty;
            var typeMap = GetTypeMap();
            foreach (var pi in properties)
            {
                if (pi.Name != "Id")
                {
                    object value = pi.GetValue(obj, null);
                    var dbtype = typeMap[pi.PropertyType];
                    var name = string.Format("@{0}", pi.Name.ToLower());
                    SqlParameter par = new SqlParameter() { ParameterName = name, DbType = dbtype, Value = value };
                    cmd.Parameters.Add(par);
                    query += query == string.Empty ? pi.Name + "=" + name : "," + pi.Name + "=" + name;
                }
            }
            cmd.CommandText = string.Format("update {0} set {1} where Id={2} ", obj.GetType().Name, query, obj.Id);

            bool res = false;
            try
            {
                conn.Open();
                cmd.ExecuteNonQuery();
                res = true;
            }
            catch (Exception ex)
            {
                //TODO обработать исключение
            }
            finally
            {
                CloseConnection();
            }
            return res;
        }

Мәліметтерді өшіру (Delete SomeTable Where Id=..):
public static bool DeleteObject(BaseObject obj)
        {
            if (obj.Id > 0)
            {
                var cmd = CreateCommand();
                cmd.CommandText = string.Format("delete from {0} where Id={1}", obj.GetType().Name, obj.Id);
                try
                {
                    conn.Open();
                    cmd.ExecuteNonQuery();
                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                    //TODO обработать исключение
                }
                finally
                {
                    CloseConnection();
                }
            }
            return false;
        }

Sql-типтері мен .Net типтерін сәйкестендіруге қажетті сөздік:
public static Dictionary<Type, DbType> GetTypeMap()
        {
            var typeMap = new Dictionary<Type, DbType>();
            typeMap[typeof(byte)] = DbType.Byte;
            typeMap[typeof(sbyte)] = DbType.SByte;
            typeMap[typeof(short)] = DbType.Int16;
            typeMap[typeof(ushort)] = DbType.UInt16;
            typeMap[typeof(int)] = DbType.Int32;
            typeMap[typeof(uint)] = DbType.UInt32;
            typeMap[typeof(long)] = DbType.Int64;
            typeMap[typeof(ulong)] = DbType.UInt64;
            typeMap[typeof(float)] = DbType.Single;
            typeMap[typeof(double)] = DbType.Double;
            typeMap[typeof(decimal)] = DbType.Decimal;
            typeMap[typeof(bool)] = DbType.Boolean;
            typeMap[typeof(string)] = DbType.String;
            typeMap[typeof(char)] = DbType.StringFixedLength;
            typeMap[typeof(Guid)] = DbType.Guid;
            typeMap[typeof(DateTime)] = DbType.DateTime;
            typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
            typeMap[typeof(byte[])] = DbType.Binary;
            typeMap[typeof(byte?)] = DbType.Byte;
            typeMap[typeof(sbyte?)] = DbType.SByte;
            typeMap[typeof(short?)] = DbType.Int16;
            typeMap[typeof(ushort?)] = DbType.UInt16;
            typeMap[typeof(int?)] = DbType.Int32;
            typeMap[typeof(uint?)] = DbType.UInt32;
            typeMap[typeof(long?)] = DbType.Int64;
            typeMap[typeof(ulong?)] = DbType.UInt64;
            typeMap[typeof(float?)] = DbType.Single;
            typeMap[typeof(double?)] = DbType.Double;
            typeMap[typeof(decimal?)] = DbType.Decimal;
            typeMap[typeof(bool?)] = DbType.Boolean;
            typeMap[typeof(char?)] = DbType.StringFixedLength;
            typeMap[typeof(Guid?)] = DbType.Guid;
            typeMap[typeof(DateTime?)] = DbType.DateTime;
            typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
            return typeMap;
        }

BaseObject-ке екі метод қосамыз, бірі — объектіні сақтауға(жаңартуға), екіншісі — өшіруге мүмкіндік береді.
public bool Save()
        {
            if (this.Id > 0)
                return DAL.UpdateObject(this);
            return DAL.CreateObject(this);
        }

        public bool Delete()
        {
            return DAL.DeleteObject(this);
        }

Person.cs — қолданушылар жайлы мәліменттер сақтайтын таблицаның моделі:
public class Person:BaseObject
    {
        public string Password { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public DateTime CreateDate { get; set; }
        public int RoleId { get; set; }
        public string Login { get; set; }
    }

Енді методтарымыздың жұмысын тамашалаймыз:
Person person = new Person();
            person.Login = "yerlan";
            person.Password = "password";
            person.Name = "Yerlan Karakulov";
            person.RoleId = 1;
            person.Email = "ereke_enu@mail.ru";
            person.CreateDate = DateTime.Now;
            person.Save();//сақтау

            Person oldPerson = BLL.GetPerson(1);
            oldPerson.Name = "Nurlan";
            oldPerson.Save();//жаңарту

            oldPerson.Delete();//өшіру

Файлдарды жүктеп алу: BLL, DAL

Кодқа қатысты идеяларыңыз болса, пікірлерде талқылайық.

2 пікір

avatar
… where Id={1} мынау деген sql injection гой. Былай жазу не рекомендуется. Отйкени база данных каждый запросты уникальный деп тусинидеи. Егер оны параметризированный етсениз производительсность оседи.
avatar
Қалай sql injection болатынын түсінбедім, егер мен Id-тің типі int, саннан басқа типте мән беретін болса, қате шығармайды ма? (url?id=555;drop database..)
Тек қана тіркелген және авторизациядан өткен қолданушылар пікір қалдыра алады.