步步为营:三层架构+ASP.NET MVC 架构的快速搭建(入门篇)

简介:

上一篇博客中《两天完成一个小型工程报价系统》,许多朋友向我讨源码。其实我之后没发了,确实那种三层架构没什么意思,只是我个人的孤芳自赏,很多的处理都不是很成熟。所以我重新架构了一番,以一个小例子来阐述我的架构模式,当然,这和企业级的架构还是差距很大,不过,还是值得一读。不积硅步,无以至江海,只有从小细节慢慢领悟,步步为营,才能设计出优秀的企业级架构。

 

  重构后的架构图:

解决方案里项目设计:

数据库访问层接口设计:IBaseDao

 

  • Repository模式

这儿使用了Repository模式,抽象出一个公共的接口IBaseDao,IBaseDao接口中定义了一系列契约(CRUD ),不管我的我数据访问用了那种技术,只要我实现了IBaseDao接口,则必须要实现接口定义的契约,所以接口的稳定至关重要,当然这样做的好处是显而易见的,减少了冗余代码。

复制代码
  public interface IBaseDao<T> where T:class
    {
        IList<T> GetAllEntities();
        bool SaveEntity(T entity);
        bool UpdateEntity(T entity);
        bool DeleteEntity(T entity);
        bool DeleteEntityByID(object id);
        T GetEntityByID(object id);
        IList<T> GetPageEntities(int pageIndex, int PageSize);
    }
复制代码
  • 首先ICustomerDao继承了IBaseDao接口:

  public interface  ICustomerDao<Customer>:IBaseDao

  public interface  ICustomerDao:IBaseDao<Customer>

   public interface ICustomerDao:IBaseDao<Customer>
    {
    }

 

 

  • OracleDao实现ICustomerDao接口:
复制代码
 public class CustomerOracleDao:IDao.ICustomerDao
    {
        public IList<Model.Customer> GetAllEntities()
        {
            return GetAllEntitiesBySqlWhere("");

        }
        public IList<Model.Customer> GetAllEntitiesBySqlWhere(string sqlWhere)
        {

            string sql = string.Format("select * from Customer Where 1=1  {0}",sqlWhere);
            List<Customer> listCustomers = new List<Customer>();
            using (OracleDataReader odr = OracleHelper.ExecuteReader(OracleHelper.ConnectionString, System.Data.CommandType.Text, sql, null))
            {
                while (odr.Read())
                {
                    Model.Customer customer = new Customer();
                    customer.ID = odr.GetInt32(0);
                    customer.Name = odr.IsDBNull(1) ? "" : odr.GetString(1);
                    customer.Phone = odr.IsDBNull(2) ? "" : odr.GetString(2);
                    customer.Remark = odr.IsDBNull(3) ? "" : odr.GetString(3);
                    listCustomers.Add(customer);

                }
            }
            return listCustomers;
        }

        private int GetNewID()
        {
            string sql = "select s_customer.nextval from dual";
            return int.Parse(OracleHelper.ExecuteScalar(OracleHelper.ConnectionString, CommandType.Text, sql, null).ToString());
        }

        public bool SaveEntity(Model.Customer entity)
        {
            entity.ID = GetNewID();
            bool resultValue = false;
            string sql = string.Format(@"insert into Customer(ID,Name,Phone,Remark) values({0},'{1}','{2}','{3}')",entity.ID,entity.Name,entity.Phone,entity.Remark );
            if (OracleHelper.ExecuteNonQuery(OracleHelper.ConnectionString, CommandType.Text, sql, null) > 0)
            {
                resultValue = true;
            }
            return resultValue;
           
        }

        public bool UpdateEntity(Model.Customer entity)
        {
            string sql = string.Format("update Customer set Name='{0}',Phone='{1}',Remark='{2}' where ID={3}",entity.Name,entity.Phone,entity.Remark,entity.ID);
            return OracleHelper.ExecuteNonQuery(OracleHelper.ConnectionString, CommandType.Text, sql, null) > 0; 
        }

        public bool DeleteEntity(Model.Customer entity)
        {
            return DeleteEntityByID(entity.ID);
        }

        public bool DeleteEntityByID(object id)
        {
            string sql = string.Format("delete from Customer where ID={0} ",id);
            return OracleHelper.ExecuteNonQuery(OracleHelper.ConnectionString, CommandType.Text, sql, null) > 0;
        }

        public Model.Customer GetEntityByID(object id)
        {
            string sqlWhere = string.Format(" and id={0}", id);
            int CID = (int)id;
            List<Model.Customer> list = GetAllEntitiesBySqlWhere(sqlWhere) as List<Customer>;
            return list.SingleOrDefault(c => c.ID == CID);
        }

        public IList<Model.Customer> GetPageEntities(int pageIndex, int PageSize)
        {
            throw new NotImplementedException();
        }
    }
复制代码
  • EFDao实现ICustomerDao接口:
复制代码
 //用EF来实现数据访问层接口
    public class CustomerEFDao:ICustomerDao
    {
        //上下文网关
        private Hotel.Model.HotelContainer hotelDB = new Model.HotelContainer();
        /// <summary>
        /// 获取全部用户信息
        /// </summary>
        /// <returns></returns>
        public IList<Insigma.Hotel.Model.Customer> GetAllEntities()
        {
            return hotelDB.Customer.ToList<Customer>();
        }

        public bool SaveEntity(Insigma.Hotel.Model.Customer entity)
        {
            hotelDB.Customer.AddObject(entity);
            bool returnValue = false;
            //返回受影响的行数
            if (hotelDB.SaveChanges()>0)
            {
                returnValue = true;
            }
            return returnValue;
        }
        /// <summary>
        /// 更新客户信息
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public bool UpdateEntity(Insigma.Hotel.Model.Customer entity)
        {
            //新的方法来保存
            //hotelDB.Customer.Attach(entity);//附加到表对应集合缓冲中
            //hotelDB.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Modified);
            bool resultValue = false;
            //if (hotelDB.SaveChanges()>0)
            //{
            //    resultValue = true;
            //}
            //return resultValue;
            HotelContainer hoteEntities = new HotelContainer();
            var oldCustomer = (from c in hoteEntities.Customer
                               where c.ID == entity.ID
                               select c).SingleOrDefault<Customer>();
            oldCustomer.Name = entity.Name;
            oldCustomer.Phone = entity.Phone;
            oldCustomer.Remark = entity.Remark;
            if (hoteEntities.SaveChanges()>0)
            {
                resultValue = true;
            }
            return resultValue;
        }
        /// <summary>
        /// 删除客户信息(一般是软删除)
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public bool DeleteEntity(Insigma.Hotel.Model.Customer entity)
        {
            return DeleteEntityByID(entity.ID);
        }
        /// <summary>
        /// 根据ID删除数据
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool DeleteEntityByID(object id)
        {
            
            int Id = (int)id;
            bool resultValue = false;
            var deleteCustomer = hotelDB.Customer.Where<Customer>(c => c.ID == Id).SingleOrDefault<Customer>();
            if (deleteCustomer != null)
            {
                hotelDB.Customer.DeleteObject(deleteCustomer);
                if (hotelDB.SaveChanges() > 0)
                {
                    resultValue = true;
                }
            }
            return resultValue;
        }
        /// <summary>
        ////// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public Insigma.Hotel.Model.Customer GetEntityByID(object id)
        {
         
            int Id = (int)id;
            return hotelDB.Customer.Where<Customer>(c => c.ID == Id).SingleOrDefault<Customer>();
        }

        public IList<Insigma.Hotel.Model.Customer> GetPageEntities(int pageIndex, int PageSize)
        {
         
            var result = hotelDB.Customer.OrderBy(c =>c.ID).Skip<Customer>((pageIndex - 1) * PageSize).Take<Customer>(PageSize).ToList();
            return result;
        }
    }
复制代码

 

  • SQLDao实现ICustomerDao接口:
复制代码
public class CustomerSQLDao:ICustomerDao
    {

        public IList<Insigma.Hotel.Model.Customer> GetAllEntities()
        {
            using (CustomerTableAdapter adapter=new CustomerTableAdapter ())
            {
                var result = (from c in adapter.GetData()
                             select new Customer { ID=c.ID,Name=c.Name,Phone=c.Phone}).ToList<Customer>();
                return result;
            }
        }

        public bool SaveEntity(Insigma.Hotel.Model.Customer entity)
        {
            bool resultValue=false;
            using (CustomerTableAdapter adapter=new CustomerTableAdapter ())
            {
                if (adapter.Insert(entity.Name, entity.Phone, entity.Remark) > 0)
                {
                    resultValue = true;
                }
                return resultValue;
            }
        }

        public bool UpdateEntity(Insigma.Hotel.Model.Customer entity)
        {
            bool resultValue = false;
            using (CustomerTableAdapter adapter=new CustomerTableAdapter ())
            {
                if (adapter.UpdateCustomer(entity.Name,entity.Phone,entity.Remark,entity.ID)>0)
                {
                    resultValue = true;
                }
                return resultValue;
            }
        }

        public bool DeleteEntity(Insigma.Hotel.Model.Customer entity)
        {
            return DeleteEntityByID(entity.ID);
        }

        public bool DeleteEntityByID(object id)
        {
            bool resultValue = false;
            int CID=(int)id;
            using (CustomerTableAdapter adapter=new CustomerTableAdapter ())
            {
                //若取名Delete会变成Delete1
                if (adapter.DeleteCustomerByID(CID)>0)
                {
                    resultValue = true;
                }
                return resultValue;
            }
        }

        public Insigma.Hotel.Model.Customer GetEntityByID(object id)
        {
            using (CustomerTableAdapter adapter=new CustomerTableAdapter ())
            {
                var table=adapter.GetCustomerByID(Convert.ToInt32(id));
                Customer customer = new Customer();
                customer.ID = table[0].ID;
                customer.Name = table[0].Name;
                customer.Phone = table[0].Phone;
                return customer;

            }
        }

        public IList<Insigma.Hotel.Model.Customer> GetPageEntities(int pageIndex, int PageSize)
        {
            using (CustomerTableAdapter adapter=new CustomerTableAdapter ())
            {
                var result = (from c in adapter.GetData()
                              select new Customer { ID = c.ID, Name = c.Name, Phone = c.Phone }).OrderBy(c => c.ID).Skip<Customer>((pageIndex - 1) * PageSize).Take<Customer>(PageSize).ToList<Customer>();
                return result;
            }
        }
    }
复制代码

这样我们就设计好了数据访问层实现方式,一共有三层方法来实现对数据库的访问,但是不管是OracleDao,还是EFDao,SQLDao实现了IBaseDao接口。这就是Repository模式。

业务逻辑层接口设计:IBLL

 

复制代码
 public interface ICustomerService
    {
        //Controller来调用业务逻辑
        IList<Customer> GetAllCustomers();
        bool SaveCustomer(Customer customer);
        bool UpdateCustomer(Customer customer);
        bool DeleteCustomer(int id);
        IList<Customer> GetPageCustomers(int pageIndex, int pageSize);
        Customer GetCustomerByID(int id);
    }
复制代码
  • 这儿我们更希望通过配置文件的方式手动的去选择需要的数据访问层技术。所以这儿可以运用抽象工厂+反射的技术,当然还有IOC依赖注入
复制代码
 public class CustomerService:ICustomerService
    {
        public CustomerService()
        {
            //最好通过抽象工厂和反射 IOC依赖注入
            //this.customerDao = new EFDao.CustomerEFDao();
            this.customerDao = DaoFactory.GetCustomerDao();
        }
        /// <summary>
        /// 数据表数据访问层接口(好处:接口是稳定)
        /// </summary>
        private ICustomerDao customerDao;

        public ICustomerDao CustomerDao
        {
            get { return customerDao; }
            set { customerDao = value; }
        }
        public IList<Model.Customer> GetAllCustomers()
        {
            return CustomerDao.GetAllEntities();
        }

        public bool SaveCustomer(Model.Customer customer)
        {
            return CustomerDao.SaveEntity(customer);
        }

        public bool UpdateCustomer(Model.Customer customer)
        {
            return CustomerDao.UpdateEntity(customer);
        }

        public bool DeleteCustomer(int id)
        {
            return CustomerDao.DeleteEntityByID(id);
        }

        public IList<Model.Customer> GetPageCustomers(int pageIndex, int pageSize)
        {
            return CustomerDao.GetPageEntities(pageIndex, pageSize);
        }

        public Model.Customer GetCustomerByID(int id)
        {
            return CustomerDao.GetEntityByID(id);
        }
    }
复制代码

 

  • 抽象共厂的实现,因为反射出实例很耗费性能,所以运用了缓存来减轻负担:
复制代码
public class DaoCache
    {
        public DaoCache()
        {

        }
        public static object GetDao(string key)
        {
            System.Web.Caching.Cache daoCache = HttpRuntime.Cache;
            return daoCache.Get(key);
        }
        public static void InsertDaoCache(string key, object value)
        {
            if (GetDao(key) == null)
            {
                System.Web.Caching.Cache daoCache = HttpRuntime.Cache;
                daoCache.Insert(key, value);
            }
        }
    }
复制代码
  • 抽象工厂反射实例:
复制代码
 public class DaoFactory
    {
       public static readonly string DaoPath=System.Configuration.ConfigurationManager.AppSettings["DaoPath"];
        public static readonly string DaoHZ=System.Configuration.ConfigurationManager.AppSettings["HZ"];
        public static object CreateDao(string assemblyPath, string objType)
        {
            var cacheDao = DaoCache.GetDao(objType);
            if (cacheDao==null)
            {
                cacheDao = Assembly.Load(assemblyPath).CreateInstance(objType);
                DaoCache.InsertDaoCache(objType, cacheDao);
            }
            return cacheDao;
        }
        public static ICustomerDao GetCustomerDao()
        {
            return CreateDao(DaoPath, string.Format("{0}.Customer{1}", DaoPath, DaoHZ)) as ICustomerDao;
        }
    }
复制代码

这样,最简单的业务逻辑层完成了,当然这儿很复杂,还需要用到Facade模式。

表示层(MVC)

 

  • 表示层当然是指挥家Controller去访问业务逻辑层,把数据装配到Model中,交给View来显示。
复制代码
 public class CustomerController : Controller
    {
       // private HotelContainer db = new HotelContainer();
        private Insigma.Hotel.IBLL.ICustomerService customerService =new BLL.CustomerService();
        //
        // GET: /Customer/

        public ViewResult Index()
        {
            return View(customerService.GetAllCustomers());
        }

        //
        // GET: /Customer/Details/5

        public ViewResult Details(int id)
        {
            Customer customer = customerService.GetCustomerByID(id);
            return View(customer);
        }

        //
        // GET: /Customer/Create

        public ActionResult Create()
        {
            return View();
        } 

        //
        // POST: /Customer/Create

        [HttpPost]
        public ActionResult Create(Customer customer)
        {
            if (ModelState.IsValid)
            {
                customerService.SaveCustomer(customer);
                return RedirectToAction("Index");  
            }

            return View(customer);
        }
        
        //
        // GET: /Customer/Edit/5
 
        public ActionResult Edit(int id)
        {
            Customer customer = customerService.GetCustomerByID(id);
            return View(customer);
        }

        //
        // POST: /Customer/Edit/5

        [HttpPost]
        public ActionResult Edit(Customer customer)
        {
            if (ModelState.IsValid)
            {
                customerService.UpdateCustomer(customer);
                return RedirectToAction("Index");
            }
            return View(customer);
        }

        //
        // GET: /Customer/Delete/5
 
        public ActionResult Delete(int id)
        {
            Customer customer = customerService.GetCustomerByID(id);
            return View(customer);
        }

        //
        // POST: /Customer/Delete/5

        [HttpPost, ActionName("Delete")]
        public ActionResult DeleteConfirmed(int id)
        {
            customerService.DeleteCustomer(id);
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
      
        }
    }
复制代码
  • 现在只需要修改配置文件就可以灵活的选择OracleDao、EFDao、SQLDao,而我们的项目丝毫不会有任何改动:

     

        <add key="DaoPath" value="Insigma.Hotel.EFDao" />
        <add key="HZ" value="EFDao" />

     

     

     

总结:

这是对稍早那篇文章的总结,接口引入稳定了开发。反射的引用让程序员更加关注业务层,提高了开发效率。

 

牛人的架构设计图:来自刘冬.NET

 

 

 

 

 

本博客为 木宛城主原创,基于 Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名 木宛城主(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。

本文转自木宛城主博客园博客,原文链接:http://www.cnblogs.com/OceanEyes/archive/2012/07/06/aspmvcgo.html,如需转载请自行联系原作者
目录
相关文章
|
1月前
|
数据采集 机器学习/深度学习 运维
量化合约系统开发架构入门
量化合约系统核心在于数据、策略、风控与执行四大模块的协同,构建从数据到决策再到执行的闭环工作流。强调可追溯、可复现与可观测性,避免常见误区如重回测轻验证、忽视数据质量或滞后风控。初学者应以MVP为起点,结合回测框架与实时风控实践,逐步迭代。详见相关入门与实战资料。
|
5月前
|
消息中间件 存储 Kafka
一文带你从入门到实战全面掌握RocketMQ核心概念、架构部署、实践应用和高级特性
本文详细介绍了分布式消息中间件RocketMQ的核心概念、部署方式及使用方法。RocketMQ由阿里研发并开源,具有高性能、高可靠性和分布式特性,广泛应用于金融、互联网等领域。文章从环境搭建到消息类型的实战(普通消息、延迟消息、顺序消息和事务消息)进行了全面解析,并对比了三种消费者类型(PushConsumer、SimpleConsumer和PullConsumer)的特点与适用场景。最后总结了使用RocketMQ时的关键注意事项,如Topic和Tag的设计、监控告警的重要性以及性能与可靠性的平衡。通过学习本文,读者可掌握RocketMQ的使用精髓并灵活应用于实际项目中。
4219 9
 一文带你从入门到实战全面掌握RocketMQ核心概念、架构部署、实践应用和高级特性
|
8月前
|
人工智能 前端开发 Java
DDD四层架构和MVC三层架构的个人理解和学习笔记
领域驱动设计(DDD)是一种以业务为核心的设计方法,与传统MVC架构不同,DDD将业务逻辑拆分为应用层和领域层,更关注业务领域而非数据库设计。其四层架构包括:Interface(接口层)、Application(应用层)、Domain(领域层)和Infrastructure(基础层)。各层职责分明,避免跨层调用,确保业务逻辑清晰。代码实现中,通过DTO、Entity、DO等对象的转换,结合ProtoBuf协议,完成请求与响应的处理流程。为提高复用性,实际项目中可增加Common层存放公共依赖。DDD强调从业务出发设计软件,适应复杂业务场景,是微服务架构的重要设计思想。
|
11月前
|
机器学习/深度学习 资源调度 算法
图卷积网络入门:数学基础与架构设计
本文系统地阐述了图卷积网络的架构原理。通过简化数学表述并聚焦于矩阵运算的核心概念,详细解析了GCN的工作机制。
643 3
图卷积网络入门:数学基础与架构设计
|
11月前
|
监控 前端开发 API
一款基于 .NET MVC 框架开发、功能全面的MES系统
一款基于 .NET MVC 框架开发、功能全面的MES系统
331 5
|
11月前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
323 3
|
消息中间件 Java Kafka
实时数仓Kappa架构:从入门到实战
【11月更文挑战第24天】随着大数据技术的不断发展,企业对实时数据处理和分析的需求日益增长。实时数仓(Real-Time Data Warehouse, RTDW)应运而生,其中Kappa架构作为一种简化的数据处理架构,通过统一的流处理框架,解决了传统Lambda架构中批处理和实时处理的复杂性。本文将深入探讨Kappa架构的历史背景、业务场景、功能点、优缺点、解决的问题以及底层原理,并详细介绍如何使用Java语言快速搭建一套实时数仓。
1281 4
|
Kubernetes 关系型数据库 MySQL
Kubernetes入门:搭建高可用微服务架构
【10月更文挑战第25天】在快速发展的云计算时代,微服务架构因其灵活性和可扩展性备受青睐。本文通过一个案例分析,展示了如何使用Kubernetes将传统Java Web应用迁移到Kubernetes平台并改造成微服务架构。通过定义Kubernetes服务、创建MySQL的Deployment/RC、改造Web应用以及部署Web应用,最终实现了高可用的微服务架构。Kubernetes不仅提供了服务发现和负载均衡的能力,还通过各种资源管理工具,提升了系统的可扩展性和容错性。
436 3
|
存储 前端开发 测试技术
Android kotlin MVVM 架构简单示例入门
Android kotlin MVVM 架构简单示例入门
257 1
|
消息中间件 关系型数据库 Java
‘分布式事务‘ 圣经:从入门到精通,架构师尼恩最新、最全详解 (50+图文4万字全面总结 )
本文 是 基于尼恩之前写的一篇 分布式事务的文章 升级而来 , 尼恩之前写的 分布式事务的文章, 在全网阅读量 100万次以上 , 被很多培训机构 作为 顶级教程。 此文修改了 老版本的 一个大bug , 大家不要再看老版本啦。

热门文章

最新文章

下一篇
oss云网关配置