三层应用架构设计 [英] Three layer application architecture design

查看:60
本文介绍了三层应用架构设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有三层应用程序如下:

I have three layer application as follows:

Presentation > Entities (VO) > Business Layer (BAL) > Data Layer (DAL)



我不喜欢这个解决方案中的内容:



  • 在BAL类中仍有System.Data参考
  • 每个BAL类需要DAL类


    • 每个VO实体都有自己的BAL类,每个BAL类都包含它自己的DAL类。是否可以为所有BAL类创建一个通用DAL类,而不是每次为BAL创建DAL?
    • 在BAL中使用System.Data是否可以?
    public partial class FrmLogin : Form
    {
            private readonly UserBal _userBal;
    
            public FrmLogin()
            {
                InitializeComponent();
                _userBal = new UserBal();
            }
    
            private void btnSearch_Click(object sender, EventArgs e)
            {
                var userVo = _userBal.SearchByName(txtUsername.Text);
            }
    }





    VO示例类:





    VO example class:

    public class UserVo
    {
        public int IdUser { get; set; }
    
        public string Firstname { get; set; }
    
        public string Lastname { get; set; }
    
        public string Email { get; set; }
    }





    BAL示例类:





    BAL example class:

    public class UserBal
    {
        private readonly UserDal _userDal;
    
        public UserBal()
        {
            _userDal  = new UserDal();
        }
    
        public UserVo SearchByName(string name)
        {
            var userVo = new UserVo();
            var dataTable = _userDal.SearchByName(name);
    
            foreach (DataRow dr in dataTable.Rows)
            {
                userVo.IdUser = int.Parse(dr["t01_id"].ToString());
                userVo.Firstname = dr["t01_firstname"].ToString();
                userVo.Lastname = dr["t01_lastname"].ToString();
                userVo.Email = dr["t01_email"].ToString();
            }
            return userVo;
        }
    
        public UserVo SearchById(string _id)
        {
            var userVo = new UserVo();
            var dataTable = _userDal.SearchById(_id);
    
            foreach (DataRow dr in dataTable.Rows)
            {
                userVo.IdUser = int.Parse(dr["t01_id"].ToString());
                userVo.Firstname = dr["t01_firstname"].ToString();
                userVo.Lastname = dr["t01_lastname"].ToString();
                userVo.Email = dr["t01_email"].ToString();
            }
            return userVo;
        }
    }





    DAL示例类:





    DAL example class:

    public class UserDal
            {
                private readonly DbManager _dbManager;
    
                public UserDal()
                {
                    _dbManager = new DbManager("DBConnection");
                }
                public DataTable SearchByName(string username)
                {              
                        var parameters = new List<IDbDataParameter>
                        {
                            _dbManager.CreateParameter("@FirstName", 50, username, DbType.String),
                        };
    
    //_dbManager.SearchByName returns DataTable object
                       return _dbManager.SearchByName("SELECT * FROM tbUsers WHERE FirstName=@FirstName", CommandType.Text, parameters.ToArray());
                }
                public DataTable SearchById(string id)
                {
                    var parameters = new List<IDbDataParameter>
                    {
                        _dbManager.CreateParameter("@Id", 50, id, DbType.Int32),
                    };
    
    //_dbManager.SearchById returns DataTable object
                    return _dbManager.SearchById("SELECT * FROM tbUsers WHERE Id=@Id", CommandType.Text, parameters.ToArray());
                }
            }





    我的尝试:



    如上所示代码的当前设计



    What I have tried:

    as above shown current design of code

    推荐答案

    我个人在数据访问层中没有searchbyname。我通常将存储过程或sql语句留在业务逻辑层中,因为这是一个移动目标,并且可以随业务需求而变化而不是访问数据可能是一个很好的可重用dll。下面是一个示例SQL Server类,oracle非常相似。它所做的只是以您告诉它的方式访问数据库,以及您告诉它的数据库以及返回或执行的东西。我在许多项目中使用它而不必改变任何东西,因为对于任何新项目,通常唯一改变的是业务需求和使用数据的前端。在我开始任何新项目之前,我的编码的1/3完成了:)通常,业务逻辑和节省用户的时间最长。

    在BAL中使用System.data很好。 DataTables是一种非常通用的返回数据的方法,无需将自己锁定到某种数据库类型。

    这就是我的2美分。



    I personally would not have searchbyname in the data access layer. I generally leave the stored procedure or sql statements in the business logic layer because thats a moving target and can change with business needs vs accessing data can be a nice reusable dll. Below is a sample SQL Server class, oracle is very similar. All it does is access the database in the way you tell it, and what database you tell it to and return or execute stuff. I use this across many projects without having to change a thing because with any new project the only thing that usually changes are the business requirements and the front end to work with the data. 1/3rd of my coding is done before I start any new project :)..sort of usually the business logic and saving users from themselves takes the longest.
    Having System.data in the BAL is fine. DataTables are a fairly generic way of returning data without locking yourself into a certain database type.
    Thats just my 2 cents.

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.Configuration;
    
    namespace DataAccess
    {
        public class SqlServer : IDisposable
        {
            // Flag: Has Dispose already been called? 
            bool disposed = false;
    
            protected Dictionary<int, SqlParameter> _parameters { get; set; }
            protected int _commandTimeout { get; set; }
            /// <summary>
            /// Name of connection in config file
            /// </summary>
            public string ConnectionName { get; set; }
            /// <summary>
            /// Full connectionstring
            /// </summary>
            public string ConnectionString { get; set; }
    
            public enum ReturnType
            {
                DataTable,
                DataReader,
                Scalar,
                DataSet,
                NoResult
            }
    
            /// <summary>
            /// Instantiate object
            /// </summary>
            public SqlServer()
                :this("DefaultConnection")
            {
    
            }
    
            /// <summary>
            /// Instantiate object
            /// </summary>
            /// <param name="connectionName">Connection Name attribute in app or web config file</param>
            public SqlServer(string connectionName)
            {
                ConnectionName = connectionName;
            }
    
            /// <summary>
            /// Get SQL Connection
            /// </summary>
            /// <param name="connectionName">Connection Name attribute in app or web config file</param>
            /// <returns>SQL Connection</returns>
            private SqlConnection getConnection(string connectionName)
            {
                SqlConnection sqlConn;
                string connString;
    
                try
                {
                    connString = (ConfigurationManager.ConnectionStrings[ConnectionName].ConnectionString != null) ? ConfigurationManager.ConnectionStrings[ConnectionName].ConnectionString : ConnectionString;
                    sqlConn = new SqlConnection(connString);
                    sqlConn.Open();
                }
                catch (SqlException ex)
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                return sqlConn;
            } // private SqlConnection getConnection()
    
    
            /// <summary>
            /// Adds the parameters to a SQL command
            /// </summary>
            /// <param name="commandText">The SQL query to execute</param>
            /// <param name="parameters">Parameters to pass to the SQL query</param>
            private static void AddParameters(SqlCommand command, Dictionary<string, object> parameters)
            {
                if (parameters == null)
                {
                    return;
                }
    
                foreach (var param in parameters)
                {
                    var parameter = command.CreateParameter();
                    parameter.ParameterName = param.Key;
                    parameter.Value = param.Value ?? DBNull.Value;
                    command.Parameters.Add(parameter);
                }
            }
    
            /// <summary>
            /// Executes SQL Statement
            /// </summary>
            /// <param name="SQL">SQL String</param>
            /// <param name="parms">Dictionary of sql parameters, collection expects the dictionary key to start with 1 and go in sequential order.</param>
            /// <param name="returnDataType">Enum of datatype you want returned.</param>
            /// <returns>Specified returnDataType Object</returns>
            public object executeSQL(string SQL, ReturnType returnDataType)
            {
                SqlCommand sqlComm = new SqlCommand();
                SqlDataAdapter sqlDA = null;
    
                try
                {
                    sqlComm = new SqlCommand(SQL, getConnection(ConnectionName));
                    sqlComm.CommandType = CommandType.Text;
    
                    switch (returnDataType)
                    {
                        case ReturnType.DataReader:
                            return sqlComm.ExecuteReader();
                        case ReturnType.DataTable:
                            DataTable dtResult = new DataTable();
                            sqlDA = new SqlDataAdapter(sqlComm);
                            sqlDA.Fill(dtResult);
                            return dtResult;
                        case ReturnType.DataSet:
                            sqlDA = new SqlDataAdapter(sqlComm);
                            DataSet dsResult = new DataSet();
                            sqlDA.Fill(dsResult);
                            return dsResult;
                        case ReturnType.Scalar:
                            return sqlComm.ExecuteScalar();
                        case ReturnType.NoResult:
                            return sqlComm.ExecuteNonQuery();
                        default:
                            return sqlComm.ExecuteReader();
                    }//end switch
    
                } //end try
                catch (SqlException ex)
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    if (sqlComm != null)
                    {
                        //can't close connection on a datareader
                        if (sqlComm.Connection.State == ConnectionState.Open & returnDataType != ReturnType.DataReader) { sqlComm.Connection.Close(); }
                        sqlComm.Dispose();
                    }
                    if (sqlDA != null) { sqlDA.Dispose(); }
                }
    
            } //public object executeSQL
    
            /// <summary>
            /// Executes SQL Statement
            /// </summary>
            /// <param name="SQL">SQL String</param>
            /// <param name="parms">Dictionary of sql parameters, collection expects the dictionary key to start with 1 and go in sequential order.</param>
            /// <param name="returnDataType">Enum of datatype you want returned.</param>
            /// <returns>Specified returnDataType Object</returns>
            public object executeSQL(string SQL, Dictionary<string,object> parameters, ReturnType returnDataType)
            {
                SqlCommand sqlComm = new SqlCommand();
                SqlDataAdapter sqlDA = null;
    
                try
                {
                    sqlComm = new SqlCommand(SQL, getConnection(ConnectionName));
                    sqlComm.CommandType = CommandType.Text;
                    AddParameters(sqlComm, parameters);
    
                    switch (returnDataType)
                    {
                        case ReturnType.DataReader:
                            return sqlComm.ExecuteReader();
                        case ReturnType.DataTable:
                            DataTable dtResult = new DataTable();
                            sqlDA = new SqlDataAdapter(sqlComm);
                            sqlDA.Fill(dtResult);
                            return dtResult;
                        case ReturnType.DataSet:
                            sqlDA = new SqlDataAdapter(sqlComm);
                            DataSet dsResult = new DataSet();
                            sqlDA.Fill(dsResult);
                            return dsResult;
                        case ReturnType.Scalar:
                            return sqlComm.ExecuteScalar();
                        case ReturnType.NoResult:
                            return sqlComm.ExecuteNonQuery();
                        default:
                            return sqlComm.ExecuteReader();
                    }//end switch
    
                } //end try
                catch (SqlException ex)
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    if (sqlComm != null)
                    {
                        //can't close connection on a datareader
                        if (sqlComm.Connection.State == ConnectionState.Open & returnDataType != ReturnType.DataReader) { sqlComm.Connection.Close(); }
                        sqlComm.Dispose();
                    }
                    if (sqlDA != null) { sqlDA.Dispose(); }
                }
    
            } //public object executeSQL
    
            /// <summary>
            /// Executes stored procedure 
            /// </summary>
            /// <param name="storedProcName">Name of stored procedure</param>
            /// <param name="parms">Dictionary of sql parameters, collection expects the dictionary key to start with 1 and go in sequential order.</param>
            /// <param name="returnDataType">Enum of datatype you want returned.</param>
            /// <returns>Specified returnDataType Object</returns>
            public object executeProcedure(string storedProcName, Dictionary<int, SqlParameter> parms, ReturnType returnDataType)
            {
                SqlCommand sqlComm = new SqlCommand();
                SqlDataAdapter sqlDA = null;
    
                try
                {
                    sqlComm = new SqlCommand(storedProcName, getConnection(ConnectionName));
                    sqlComm.CommandType = CommandType.StoredProcedure;
    
                    for (int i = 1; i <= parms.Count; i++)
                    {
                        sqlComm.Parameters.Add(parms[i]);
                    }
    
                    switch (returnDataType)
                    {
                        case ReturnType.DataReader:
                            return sqlComm.ExecuteReader();
                        case ReturnType.DataTable:
                            DataTable dtResult = new DataTable();
                            sqlDA = new SqlDataAdapter(sqlComm);
                            sqlDA.Fill(dtResult);
                            return dtResult;
                        case ReturnType.DataSet:
                            sqlDA = new SqlDataAdapter(sqlComm);
                            DataSet dsResult = new DataSet();
                            sqlDA.Fill(dsResult);
                            return dsResult;
                        case ReturnType.Scalar:
                            return sqlComm.ExecuteScalar();
                        case ReturnType.NoResult:
                            return sqlComm.ExecuteNonQuery();
                        default:
                            return sqlComm.ExecuteReader();
                    }//end switch
    
                } //end try
                catch (SqlException ex)
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    if (sqlComm != null)
                    {
                        //can't close connection on a datareader
                        if (sqlComm.Connection.State == ConnectionState.Open & returnDataType != ReturnType.DataReader) { sqlComm.Connection.Close(); }
                        sqlComm.Dispose();
                    }
                    if (sqlDA != null) { sqlDA.Dispose(); }
                }
    
            } //public object executeProcedure
    
            // Dispose() calls Dispose(true)
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            // Protected implementation of Dispose pattern. 
            protected virtual void Dispose(bool disposing)
            {
                if (disposed)
                    return;
    
                if (disposing)
                {
                    // Free any other managed objects here. 
                }
                disposed = true;
            }
        }
    }


    过度设计,IMO。



    您的BAL(如图所示)可以很容易地与您的DAL组合以创建一个访问层。



    您现有的BAL将是上市; DAL方法是私有的。



    (BAL中的数据行应该是赠品;它们只是从物理数据库中删除一步;我希望仅查看实体/ DTO的集合。



    直接从数据访问层加载可观察集合(对于视图)没有错通过查询;而不是诉诸人工业务层只是为了满足模型。
    It's over-engineered, IMO.

    Your "BAL" (as shown) could easily combined with your DAL to create a single "access layer".

    Your existing BAL would be "public"; the DAL methods private.

    ("Data rows" in the BAL should be a give-away; they are only 1 step removed from the physical database; I would expect to see only "collections" of entities / DTOs).

    Nothing wrong with loading an "observable collection" (for a view) directly from the "data access layer" via a query; instead of resorting to an artificial "business layer" just to satisfy the model.


    这篇关于三层应用架构设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆