nighttrain_ Ответов: 2

Проектирование архитектуры трехслойного приложения


У меня есть трехслойное приложение следующим образом:
Presentation > Entities (VO) > Business Layer (BAL) > Data Layer (DAL)

Мне не нравится следить за вещами в этом решении:

  • в бал-классах еще есть система.Ссылка на данные
  • для каждого класса BAL требуется класс DAL

Мой вопрос:

  • Каждая сущность во своя и каждый бал класс бал класс содержит свой класс даль. Можно ли иметь один общий класс DAL для всех классов BALs вместо того, чтобы каждый раз создавать DAL для BAL?
  • Хорошо ли иметь систему?Данные в бал?


Мой код:

уровень представления:

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);
        }
}


Пример класса во :

public class UserVo
{
    public int IdUser { get; set; }

    public string Firstname { get; set; }

    public string Lastname { get; set; }

    public string Email { get; set; }
}


Пример класса бал :

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:

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());
            }
        }


Что я уже пробовал:

как показано выше текущий дизайн кода

2 Ответов

Рейтинг:
2

j snooze

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.
Наличие System.data в BAL-это нормально. Таблицы данных-это довольно общий способ возврата данных без блокировки себя в определенном типе базы данных.
Это всего лишь мои 2 цента.

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;

            tr


Рейтинг:
0

Gerry Schmitz

Это слишком продумано, ИМО.

Ваш "BAL" (как показано) может легко объединиться с вашим DAL, чтобы создать единый "уровень доступа".

Ваш существующий BAL будет "публичным"; методы DAL-частными.

("Строки данных" в BAL должны быть отданы; они всего на 1 шаг удалены из физической базы данных; я ожидал бы увидеть только "коллекции" сущностей / DTOs).

Нет ничего плохого в том, чтобы загрузить "наблюдаемую коллекцию" (для представления) непосредственно из "уровня доступа к данным" с помощью запроса; вместо того чтобы прибегать к искусственному "бизнес-слою" только для удовлетворения модели.