什么是在C#中访问数据库的最佳方法(设计模式)?

时间:2015-03-30 15:54:37

标签: c# database visual-studio design-patterns

我是设计模式的新手。 目前我正在开发一个系统,我有一个版权DB。从我的数据库到CRUD的最佳方法是什么? 我当前的代码如下所示(C#代码):

我为所有类定义了一个带公共函数的接口。

namespace Model
{
    public interface ICommon
    {
        void insert();
        void update();
        void delete();
    }
}

Common类(抽象类)实现ICommon接口,几个命令方法和属性。

namespace Model
{
    public abstract class Common : ICommon
    {
        public Guid RecId { set; get; }

        public abstract void insert();
        public abstract void update();
        public abstract void delete();
        public abstract List<Common> find();

        /// <summary>
        /// Insert or update the record
        /// </summary>
        public void save()
        {
            if (this.RecId == Guid.Empty)
            {
                this.insert();
            }
            else
            {
                this.update();
            }
        }
    }
}

然后,正确的类(例如UserTable类)扩展了Common类并实现了摘要方法和其他细节属性。

我从StoresProcedures和SqlParameter,SqlCommand和SqlConnection进行CRUD的方式。这是一个例子:

    class CustTableModel : Common
        {
            public string SerialNumber { set; get; }
            public string ApplicationVersion { set; get; }
            public string KernelVersion { set; get; }
            public string Name { set; get; }
            public bool Active { set; get; }

            public override void insert()
            {
                List<SqlParameter> parameters = new List<SqlParameter>();
                SqlParameter parameter;

                // SerialNumber
                parameter = new SqlParameter("@serialNumber", System.Data.SqlDbType.Int);
                parameter.Value = this.SerialNumber;
                parameters.Add(parameter);

                // ApplicationVersion
                parameter = new SqlParameter("@applicationVersion", System.Data.SqlDbType.Int);
                parameter.Value = this.ApplicationVersion;
                parameters.Add(parameter);

                // KernelVersion
                parameter = new SqlParameter("@kernelVersion", System.Data.SqlDbType.Int);
                parameter.Value = this.KernelVersion;
                parameters.Add(parameter);

                // Name
                parameter = new SqlParameter("@name", System.Data.SqlDbType.Int);
                parameter.Value = this.Name;
                parameters.Add(parameter);

                // Active
                parameter = new SqlParameter("@active", System.Data.SqlDbType.Bit);
                parameter.Value = this.Active;
                parameters.Add(parameter);

                DBConn.execute("CUSTTABLE_INSERT", parameters); // The code of DBConn is below.
}
}

为了更好地理解,这里是DBConn类:

public class DBConn
    {
        protected SqlConnection sqlConnection;
        protected string command { set; get; }
        protected List<SqlParameter> parameters { set; get; }

        protected void openConnection()
        {
            this.sqlConnection = new SqlConnection();
            this.sqlConnection.ConnectionString = "Data Source=.\\SQLEXPRESS;Initial Catalog=JYL_SOAWS_DB;Integrated Security=True";
            this.sqlConnection.Open();
        }

        protected void closeConnection()
        {
            if (this.sqlConnection.State == System.Data.ConnectionState.Open)
            {
                this.sqlConnection.Close();
            }
        }

        /// <summary>
        /// Executa o processo no banco.
        /// </summary>
        /// <returns>Quantidade de registros afetados.</returns>
        protected SqlDataReader run()
        {
            SqlCommand command = new SqlCommand();
            SqlDataReader ret;

            this.openConnection();

            command.CommandType = System.Data.CommandType.StoredProcedure;
            command.Connection = this.sqlConnection;
            command.CommandText = this.command;

            if (this.parameters != null)
            {
                foreach (SqlParameter parameter in this.parameters)
                {
                    command.Parameters.Add(parameter);
                }
            }

            ret = command.ExecuteReader();

            this.closeConnection();

            return ret;
        }

        /// <summary>
        /// Interface da classe à outros objetos.
        /// </summary>
        /// <param name="commandName">Nome da store procedure a ser executada.</param>
        /// <param name="parameters">A lista com os parâmetros e valores.</param>
        /// <returns>Numero de registros afetados.</returns>
        public static SqlDataReader execute(string commandName, List<SqlParameter> parameters = null)
        {
            DBConn conn = new DBConn();

            conn.command = commandName;
            conn.parameters = parameters;

            return conn.run();
        }
    }

我很确定有更好的方法。

有人能帮帮我吗?谢谢你的到来。

3 个答案:

答案 0 :(得分:4)

你在这里遇到了两个微妙的不同模式。

第一个是repository pattern - 一种从数据访问中抽象出业务逻辑的方法

第二种是Active Record模式,即实体负责在数据库中维护自己的状态。

我建议你远离C#中的ActiveRecord(你现在可能知道或者可能不知道Inversion of Control模式,但它非常有用且与AR相当不兼容。)

如果你刚开始的话,我会建议你看看像dapper.net这样的东西(我仍然在我的小项目中使用它)。它是一个Micro-ORM,它使用大量的样板远离使用数据库,没有看法或难以学习(我使用和喜欢EntityFramework&amp; NHibernate,但它们并不容易为初学者选择)

与此同时,我将创建一个存储库(一个包含Create(Foo实体),Read(Guid entityId),Update(Foo实体)和删除(Guid entityId)方法的类)。

另外,在使用Guids作为主键时要小心,因为它们可能会导致一个有趣的情况:由于大多数Guid实现(几乎总是)具有非顺序布局,并且数据按主键进行物理排序,例如insert可能会导致大量磁盘IO,因为数据库会重新排序磁盘上的数据页,以适应插入表中某个任意位置的新数据。 Guid生成用作主键的一个好策略是使用Guid Comb生成器

祝你好运!

答案 1 :(得分:1)

这是最好的模式。我建议不要使用ORM。特别是EF

public class MyModel 
{
    public string Id {get;set;}
    //public valuetype PropertyA {get;set;}  //other properties
}

public interface IMyModelRepository
{
    List<MyModel> GetModels();

    MyModel GetModelById(string id);

    void AddMyModel(MyModel model);
    //other ways you want to get models etc
}

public class MyModelRepositorySql : IMyModelRepository
{
    public List<MyModel> GetModels()
    {
        //SqlConnection etc etc
        while (SqlDataReader.Read())
        {
           results.Add(this.populateModel(dr));
        }
        return results;
    }

    protected MyModel populateModel(SqlDataReader dr)
    {
        //map fields to datareader
    }

    public MyModel GetModelById(string id)
    {
        //sql conn etc
        this.populateModel(dr);
    }
}

这是我的理由。

使用存储库模式允许您注入持久化数据的方式,这些数据不需要数据库。这对于单元测试至关重要,但如果您可以将模拟存储库注入到项目中进行集成测试,您会发现它非常有用

ORM虽然起初看起来很容易,但从长远来看,节省很多打字会导致问题。你只需要搜索实体框架问题的堆栈溢出,就可以看到人们在遇到以次优方式运行的查询时遇到的结点。

在任何大型项目中,您将遇到数据获取要求,这需要一些优化的检索数据的方法,这将破坏您精心设计的对象图/可注入的通用存储库或聪明的前沿ORM

POCO对象很好。当您尝试将它们序列化或递归地添加到数据库等时,复杂对象(具有其他对象作为属性的对象)会很麻烦。保留基础数据模型POCO并仅使用linq将它们组合在服务或视图模型中。 / p>

使用GUID ID做得好btw!不要听那些认为永远不会用完的傻瓜! (存储为varchar(50)并让DBA对索引进行排序)任何数据库生成的id的问题是你必须能够在不连接数据库的情况下创建对象。

答案 2 :(得分:0)

对于执行CRUD操作,我建议使用Entity framework进行Repository模式。

实体框架是由Microsoft提供的ORM。它使用一组POCO类(实体)处理数据库,以执行插入/更新/删除/创建操作。

要对这些实体执行查询,将使用语言集成查询(LINQ)。 LINQ使用类似的SQL语法,它将数据库结果作为实体集合返回。

这是一个示例 Repository pattern with EF

干杯!