Search
Write a publication
Pull to refresh

CRUD с помощью Dapper.NET своими руками. Часть 1

Я не разу не писал статьи, к сожалению исходя из этого кода будет больше а текста меньше.

Есть очень много ORM решений, NHibernate, Entity Framwork, Linq2Sql, Gentle.Net., Dapper.NET,….
Я не выбирал долго, мне нужно было от ORM не только объектное отображения данных, но и отображения результата SQL запроса
К тому же, на этапе runtime. Мне показался Dapper очень гибким и как то ближе к теме.

Начнем
Для начало создадим приложение на котором будем тестировать.
Неважно какое приложения, Desktop, Console, Web а можно вообще ограничиться UnitTest. Но в своем случае я выбрал Windows Form Application.
Самое главное нам нужно создать почву, а именно классы от которого будут наследоваться модели бизнес логики. Итак поехали:


class SqlColumnAttribute : Attribute
{
        public SqlColumnAttribute(string columnName, string dbType)
        {
            ColumnName = columnName;
            ColumnDbType = dbType;
            IsPrimaryKey = false;
        }

        public string ColumnName { get; set; }
        public string ColumnDbType { get; set; }
        public bool IsPrimaryKey { get; set; }
        public bool IsAutoIncrement { get; set; }
}


В данном случае я применил Атрибуты для того что бы описать поля для таблицы базы данных.
От данного атрибута можно наследоваться сколько угодно, и создавать для разных таблиц БД (Oracle, Sqlite,...).

Вторым этапом, необходимо создать класс который будет формировать SQL запрос для четырех операций CRUD(Create, Read, Update, Delete).
Так скажем это основная логика. Я не стал оптимизировать код (написан был давно на скорую руку), но в целом все идеально работает.

Наименования для данного класса я применил: OrmBaseModel. Конечно, не самое удачно…

public class OrmBaseModel
    {
        public string SqlInsert()
        {
            try
            {
                var stringBuilder = new StringBuilder();
                var fields = new StringBuilder("(");
                var values = new StringBuilder(" values (");

                Type type = this.GetType();
                object[] tableAttributes = type.GetCustomAttributes(typeof(TableAttribute), true);
                
                if (tableAttributes.Length == 1)
                {
                    stringBuilder.Append(String.Format("INSERT INTO {0} ", ((TableAttribute)tableAttributes[0]).Name));

                    int fieldCount = 0;
                    foreach (var propertyInfo in type.GetProperties())
                    {
                        object[] columnAttributes = propertyInfo.GetCustomAttributes(typeof(SqlColumnAttribute),true);
                        if (columnAttributes.Length == 1)
                        {
                            var columnAttribute = columnAttributes[0] as SqlColumnAttribute;
                            if (columnAttribute != null && columnAttribute.IsPrimaryKey && columnAttribute.IsAutoIncrement)
                                continue;
                            if (fieldCount == 0)
                            {
                                if (columnAttribute != null)
                                {
                                    fields.Append(columnAttribute.ColumnName);
                                    values.Append("@" + columnAttribute.ColumnName);
                                }

                            }
                            else
                            {
                                if (columnAttribute != null)
                                {
                                    fields.Append("," + columnAttribute.ColumnName);
                                    values.Append(",@" + columnAttribute.ColumnName);
                                }

                            }
                            fieldCount++;
                        }
                    }
                    fields.Append(")");
                    values.Append(")");
                    stringBuilder.Append(fields);
                    stringBuilder.Append(values);
                }
                return stringBuilder.ToString();
            }
            catch (Exception ex)
            {
                
            }

            return null;

        }
        public string SqlSelect()
        {
            try
            {
                var stringBuilder = new StringBuilder();
                var fieldsSql = new StringBuilder("");
                var fromSql = new StringBuilder();
                Type type = this.GetType();
                object[] tableAttributes = type.GetCustomAttributes(typeof(TableAttribute), true);
                if (tableAttributes.Length == 1)
                {
                    stringBuilder.Append(String.Format("SELECT "));
                    fromSql.Append(String.Format(" from {0}", ((TableAttribute)tableAttributes[0]).Name));
                    int fieldCount = 0;
                    foreach (var propertyInfo in type.GetProperties())
                    {
                        object[] columnAttributes = propertyInfo.GetCustomAttributes(typeof(SqlColumnAttribute), true);
                        if (columnAttributes.Length == 1)
                        {
                            var columnAttribute = columnAttributes[0] as SqlColumnAttribute;
                            if (fieldCount == 0)
                            {
                                if (columnAttribute != null)
                                {
                                    fieldsSql.Append(columnAttribute.ColumnName);
                                }

                            }
                            else
                            {
                                if (columnAttribute != null)
                                {
                                    fieldsSql.Append("," + columnAttribute.ColumnName);
                                }

                            }
                            fieldCount++;
                        }
                    }
                    stringBuilder.Append(fieldsSql);
                    stringBuilder.Append(fromSql);
                }
                return stringBuilder.ToString();
            }
            catch (Exception ex)
            {
                //PMLogger.Logger.DebugWriteException("BasicModelTemplate.GetSqliteSelect", ex);
            }
            return null;
        }
        public string SqlUpdate()
        {
            try
            {
                var stringBuilder = new StringBuilder();
                var fieldsSql = new StringBuilder("");
                var whereSql = new StringBuilder(" WHERE ");
                Type type = this.GetType();
                object[] tableAttributes = type.GetCustomAttributes(typeof(TableAttribute), true);
                if (tableAttributes.Length == 1)
                {
                    stringBuilder.Append(String.Format("UPDATE {0} SET ", ((TableAttribute)tableAttributes[0]).Name));
                    int fieldCount = 0;
                    foreach (var propertyInfo in type.GetProperties())
                    {
                        object[] columnAttributes = propertyInfo.GetCustomAttributes(typeof(SqlColumnAttribute),
                                                                                     true);

                        if (columnAttributes.Length == 1)
                        {
                            var columnAttribute = columnAttributes[0] as SqlColumnAttribute;
                            if (columnAttribute != null && columnAttribute.IsPrimaryKey)
                            {
                                whereSql.Append(String.Format("{0}=@{0}", columnAttribute.ColumnName));
                            }
                            if (fieldCount == 0)
                            {
                                if (columnAttribute != null && !columnAttribute.IsAutoIncrement)
                                {
                                    fieldsSql.Append(String.Format("{0}=@{0}", columnAttribute.ColumnName));
                                    fieldCount++;
                                }

                            }
                            else
                            {
                                if (columnAttribute != null && !columnAttribute.IsAutoIncrement)
                                {
                                    fieldsSql.Append(String.Format(" ,{0}=@{0}", columnAttribute.ColumnName));
                                    fieldCount++;
                                }
                            }

                        }
                    }
                    stringBuilder.Append(fieldsSql);
                    stringBuilder.Append(whereSql);
                }
                return stringBuilder.ToString();
            }
            catch (Exception ex)
            {
                
            }
            return null;

        }
        public string SqlDelete()
        {
            try
            {
                var stringBuilder = new StringBuilder();
                var fieldsSql = new StringBuilder("");
                var whereSql = new StringBuilder(" WHERE ");
                Type type = this.GetType();
                object[] tableAttributes = type.GetCustomAttributes(typeof(TableAttribute), true);
                if (tableAttributes.Length == 1)
                {
                    stringBuilder.Append(String.Format("DELETE FROM {0} ", ((TableAttribute)tableAttributes[0]).Name));
                    foreach (var propertyInfo in type.GetProperties())
                    {
                        object[] columnAttributes = propertyInfo.GetCustomAttributes(typeof(SqlColumnAttribute), true);

                        if (columnAttributes.Length == 1)
                        {
                            var columnAttribute = columnAttributes[0] as SqlColumnAttribute;
                            if (columnAttribute != null && columnAttribute.IsPrimaryKey)
                            {
                                whereSql.Append(String.Format("{0}=@{0}", columnAttribute.ColumnName));
                                break;
                            }
                        }
                    }
                    stringBuilder.Append(fieldsSql);
                    stringBuilder.Append(whereSql);
                }
                return stringBuilder.ToString();
            }
            catch (Exception ex)
            {

            }

            return null;
        }

        public List<T> Select<T>(IDbConnection dbconnection)
        {
            return dbconnection.Query<T>(SqlSelect()).ToList();
        }
        public int Insert(IDbConnection dbconnection)
        {
            try
            {
                var rowsAffected = dbconnection.Execute(SqlInsert(), this);

                if (PrimaryKeyPropertyInfo != null)
                {
                    var pinfo = PrimaryKeyPropertyInfo;
                    object[] columnAttributes = pinfo.GetCustomAttributes(typeof(SqlColumnAttribute), true);
                    if (columnAttributes.Length == 1)
                    {
                        var columnAttribute = columnAttributes[0] as SqlColumnAttribute;
                        if (columnAttribute != null && columnAttribute.IsPrimaryKey && columnAttribute.IsAutoIncrement)
                        {
                            dynamic identity = dbconnection.Query("SELECT @@IDENTITY AS Id").Single();
                            pinfo.SetValue(this, Convert.ChangeType(identity.Id, TypeCode.Int32), null);
                            var i = identity.Id;
                        }
                    }
                }
                return rowsAffected;
            }
            catch (Exception)
            {
            }
            return -1;
        }
        public int Update(IDbConnection dbconnection)
        {
            var rowsAffected = dbconnection.Execute(SqlUpdate(), this);
            return rowsAffected;
        }
        public int Delete(IDbConnection dbconnection)
        {
            var rowsAffected = dbconnection.Execute(SqlDelete(), this);
            return rowsAffected;
        }


        protected PropertyInfo PrimaryKeyPropertyInfo
        {
            get
            {
                Type type = this.GetType();
                object[] tableAttributes = type.GetCustomAttributes(typeof(TableAttribute), true);
                if (tableAttributes.Length == 1)
                {
                    foreach (var propertyInfo in type.GetProperties())
                    {
                        object[] columnAttributes = propertyInfo.GetCustomAttributes(typeof(SqlColumnAttribute), true);
                        if (columnAttributes.Length == 1)
                        {
                            var columnAttribute = columnAttributes[0] as SqlColumnAttribute;
                            if (columnAttribute != null && columnAttribute.IsPrimaryKey)
                                return propertyInfo;
                        }
                    }
                }
                return null;
            }
        }

    }


В принципе основные классы реализованы. Теперь нужно создать модель и таблицу в БД. В моем случае в роли СУБД выступает MSSQL.

Я ограничился для примера одной таблицей: User, и простыми двумя полями: Id, Name.

SQL таблица:
CREATE TABLE [dbo].[Users](
	[Id] [int],
	[Name] [varchar](50) NULL
) ON [PRIMARY]


Модель:
[Serializable]
[Table(Name = "Users")]
public class User : OrmBaseModel
{
        [SqlColumn("Id", "integer", IsPrimaryKey = true)]
        public int Id { get; set; }
        
        [SqlColumn("Name", "varchar(100) NULL")]
        public string Name { get; set; }
}


Как видно по коду, я применил атрибуты для полей модели. Ранее предполагалась что класс OrmBaseModel будет в том числе создавать таблицу по модели и удалять. Но потом я отказался от данной идей.

Допустим у нас не одна таблица а скажем три, и их нужно где то содержать вместе, для этого я создал ещё один класс: Repository

    internal class Repository
    {
        private readonly SqlConnection _dbconnection;
        private List<User> _users;
        private readonly OrmAdapterModel _ormAdapterModel;
        public Repository(SqlConnection dbconnection)
        {
            _dbconnection = dbconnection;
            _ormAdapterModel = new OrmAdapterModel(_dbconnection);
        }

        public void Update(OrmBaseModel ormBaseModel)
        {
            ormBaseModel.Update(_dbconnection);
        }

        public void Delete(OrmBaseModel ormBaseModel)
        {
            ormBaseModel.Delete(_dbconnection);
        }

        public void Insert(OrmBaseModel ormBaseModel)
        {
            ormBaseModel.Insert(_dbconnection);
        }


        public List<User> Users
        {
            get
            {
                return ((new User()).Select<User>(_dbconnection));
            }
        }


    }


Данный класс имеет три метода для трех операций Create Update Delete, они применяются для всех модели(таблиц) и поля которые выступают как
SELECT, но отталкиваются от определенной модели.

Вот и все, CRUD создан!

Вот кратки пример как этим пользоваться:
        private void button1_Click(object sender, EventArgs e)
        {
            SqlConnection s = new SqlConnection(@"Data Source=localhost\xxx;Initial Catalog=Clients;User ID=sa;Password=xxx");
            s.Open();
            
            
            Repository rep = new Repository(s);
            rep.Insert(new User { Id = 1, Name = "Habrahabr" });
            rep.Insert(new User { Id = 2, Name = "Admin" });
            rep.Insert(new User { Id = 3, Name = "User" });
            rep.Insert(new User { Id = 4, Name = "Ridik" });
            rep.Insert(new User { Id = 5, Name = "Alen" });
            
            rep.Update(new User { Id = 3, Name = "Admin" });
            rep.Delete(new User { Id = 3});
            rep.Insert(new User { Id = 3, Name = "Hello" });            

            bindingSource1.DataSource = rep.Users;

        }



Вторая часть, будет лучше…
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.