65.9K
CodeProject 正在变化。 阅读更多。
Home

架构指南:ASP.NET, MVC3, Entity Framework, Code-First 配合现有数据库,以及更多..

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.72/5 (24投票s)

2012 年 2 月 10 日

CPOL

12分钟阅读

viewsIcon

157534

downloadIcon

14247

使用 Entity Framework 的 Code First 技术开发一个简单的 MVC 3 应用程序架构。

推荐框架: https://nidoframework.codeplex.com/[^]

引言

在这篇白皮书中,我将引导您使用 Entity Framework 的 Code First 技术开发一个简单的 MVC 3 应用程序架构。希望稍后您能够将其发展成为可用于中小型 Web 应用程序开发的企业级架构。

尝试从头到尾阅读本文,这样您就能逐步和我一起构建这个架构。

先决条件

  • Visual Studio 2010
  • SQL Server 2005/ 2008
  • 实体框架
  • MVC 3
  • 创建数据库

创建数据库

在实际场景中,Code-First 技术根本不鼓励您创建数据库。当您编写模型时,系统会自动为您创建相应的数据库。然而,这也会带来一些问题,其中最难忍受的是,每次数据库架构更改时,系统都会重新创建整个数据库。这会导致您丢失数据库中的现有数据。我不想处理这种麻烦,因此作为一种变通方法,我们将采取不同的方法。我们使用 Code-First 技术,但配合一个现有的数据库。作为第一步,与常规的 Code First 技术不同,您必须先创建数据库。下面是我为进行此架构演示而创建的一个简单数据库。

327945/1._Database.PNG

总的来说,在创建数据库时,遵循以下一些最佳实践是很好的:

  • 让复合表拥有自己的主键
  • 使用 IsActive 字段来避免从数据库中物理删除记录。否则,当记录被删除时,您将丢失重要信息。
  • 养成使用 Name 字段来描述表记录的习惯。在向用户显示记录时,这可以用作记录描述符。

创建 MVC3 项目

在此演示中,我将使用 ASP.NET MVC3 Web 应用程序,因此让我们创建一个名为“MVC3Demo”的项目。我们还将使其成为一个 Razor Internet 应用程序。

327945/2._New_Web_Project.PNG

我决定为业务逻辑代码创建一个单独的项目。因此,让我们创建一个名为 MVC3Demo.Bll 的类库类型项目。

检查 MVC Web 项目的 Web.Config

我们的 Web 项目的 Web.Config 已定义了连接字符串,如果您仔细查看,会注意到名为‘ApplicationServices’的连接字符串默认使用 SQL-Express 数据库,该数据库随 Visual Studio 2010 一起安装。此配置设置将为您在 App_Data 文件夹内创建一个数据库。这是系统存储应用程序服务的地方,例如成员资格服务(如果您在 MVC 示例项目中创建新用户,那么它会使用成员资格服务,确切地说,是 ‘AspNetSqlMembershipProvider’,在自动创建的 SQL Express 数据库中创建这些用户)相关的记录。然而,我们不想使用该数据库,因此让我们修改它,并为存储我们系统数据对象的数据库添加另一个连接字符串。

修改后的 Web 配置文件将如下所示..

327945/3._WebConfig.PNG

现在,有了这个更改,我们添加了另一个名为 DemoDbContext 的条目,并且您需要使用完全相同的名称来命名我们创建的用于通过 Entity-Framework 与数据库通信的数据库上下文类。此上下文类必须通过继承 .NET Framework 系统类 DbContext 来创建。这样,系统会自动查找名称与继承 DbContext 的类同名的配置标记,以查找连接字符串详细信息。此外,在我们的例子中,我们决定保持应用程序服务设置不变,以便我们可以选择使用单独的数据库进行角色和配置文件管理。

创建成员资格数据库

您需要使用名为 aspnet_regsql 的命令在我们的数据库服务器上新建成员资格数据库。要做到这一点,让我们转到 开始 >> ‘所有程序’ >> ‘Microsoft Visual Studio 2010’ >> ‘Visual Studio Tools’ >> ‘Visual Studio Command Prompt (2010)’,然后输入 'aspnet_regsql'。这将引导您通过一个向导,让您为您的数据库创建‘AccessControlDemo’数据库。

327945/4._Access_Control_DB.PNG

运行项目,并验证新用户是否可以注册到系统中,以及您是否可以使用新创建的用户登录系统。

为您的 BLL 项目安装 ‘EntityFramework’ 包

由于我们的业务逻辑层是类库类型的项目,因此它没有作为 Entity Framework 后端所需的引用。如果您有(它会自动随 MVC 3.0 安装)库包管理器,您可以使用它来为该项目安装所需的‘Entity-Framework DLL’。要做到这一点,在 Visual Studio 2010 的项目中,转到 Tools > Library Package Manager > Package Manager Console。在控制台中,“PM>”提示符之后,输入“install-package entityframework”,这将安装该包并将‘EntityFramework’引用添加到您的 WEB 项目。使用已安装的位置并将 EntityFramework.Dll 引用到您的 BLL 项目。

创建 DemoDbContext 类

如前所述,这必须通过继承 DbContext 类来创建。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;

namespace MVC3Demo.Bll
{
    public class DemoDbContext : DbContext
    {
    }
}

完成此操作后,您几乎就完成了项目的设计框架。其余的都是在此已建立的框架之上构建您的系统。

创建模型类

这些模型类与 MVC Web 项目本身的模型类几乎相同。然而,在这种情况下,我们必须创建它们以与我们的数据库表匹配。每个表都必须有单独的模型类,并且必须具有与表字段相同的类型的属性。这种分离将允许我跨多个项目共享我的模型和模型访问逻辑。

通过此技术开发时,您需要特别注意模型及其相应属性的命名。一个拼写错误会破坏整个系统,更糟糕的是,此区域周围生成的系统错误根本没有帮助。

因此,考虑到这一点,让我创建一个名为 Student 的类。尽可能多地使用‘复制’和‘粘贴’来避免拼写错误。我过去是这样复制表记录的,然后..

327945/5._Copy_Table_Records.PNG

像这样粘贴到我的代码中..

327945/5._Copy_Code_Records.PNG

然后像这样修改它…

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace MVC3Demo.Bll.Models
{
    [Table("Student", Schema = "dbo")]
    public class Student
    {
        [ScaffoldColumn(false)]
        public int StudentId { get; set; }
        [Required]
        [StringLength(50, ErrorMessage="{0} cannot exceed {1} characters")]
        public string Name { get; set; }
        [Required]
        public int Age { get; set; }
        [Required]
        [StringLength(150)]
        public string Address { get; set; }
        [Required]
        [DisplayName("Registered Date")]
        public DateTime RegisteredDate { get; set; }
        [NotMapped]
        public bool SeniorStudent
        {
            get
            {
                DateTime today = DateTime.Now;
                TimeSpan tSpan = today.Subtract(RegisteredDate);
                return (tSpan.TotalDays > 365);
            }
        }
        [Required]
        [DisplayName("Is Active")]
        public bool IsActive { get; set; }
        public string Description { get; set; }
    }
}

这里有一个要点,我使用了属性来包含验证逻辑。不仅如此,您还可以定义相应的错误消息并将它们与属性关联起来。此外,您还可以选择在新属性(数据库中不存在)中定义一个具有特殊属性(参见 NotMapped 属性)的类。名为 SeniorStudent 的属性就是这样一个记录。在这种情况下,您需要将其标记为 NotMapped 属性。要了解有关属性的更多信息,请参阅此链接:MSDN

只需遵循相同的模式,您就可以为另外两个表创建模型类,即‘Course’和‘StudentCourse’表。

教会代码关联相关类

现在,您可能会想,表之间的关系是如何在这里定义的。这很简单,您可以这样教会模型维护其关系..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MVC3Demo.Bll.Models
{
    public class StudentCourse
    {
        public int StudentCourseId { get; set; }
        public int CourseId { get; set; }
        public Course Course { get; set; }
        public int StudentId { get; set; }
        public Student Student { get; set; }
    }
}
  • 由于 StudentCourseStudentCourse 类之间存在多对一关系,因此您需要在 StudentCourse 类中定义 StudentCourse 对象。提示:查找 Id 类型属性,并作为一般实践,在其正下方添加一个同名属性。
  • 转到 StudentCourse 类,并添加一个 ICollection<entity> 类型的属性,以表示一对多关系的多方。

看看 Course 类现在看起来是怎样的..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MVC3Demo.Bll.Models
{
    public class Course
    {
        public int CourseId { get; set; }
        public string Name { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
        public int Period { get; set; }
        public bool IsWeekEnd { get; set; }
        public bool IsActive { get; set; }
        public ICollection<StudentCourse> StudentCourses { get; set; }
    }
}

注意:我们使用这种技术在我们工作过的一些地方开发了几个系统,其中一些系统现在已在生产环境中运行。在开发阶段,命名属性时,我们总是建议复制粘贴名称,因为一个小小的拼写错误通常会导致我们难以追踪的错误。然而,这种技术已被证明适合构建中小型系统。

创建控制器

只需编译 Mvc3Demo.Bll 并将其引用添加到主 Web 项目。现在编译 Web 项目,因为在未编译时,Add Controller 向导不会显示在引用的项目中进行的修改。(我相信微软正在积极处理此版本中的一些小问题)。然后,右键单击控制器并填写相应的字段,为我们的模型 StudentCourseStudentCourse 创建‘Controller’以及关联的‘Views’。

327945/6._Create_Controller.PNG

编译它,看看是否一切正常。还有最后一个调整。如果您按原样运行此项目并尝试创建新的 Course,您将收到“Invalid object name 'dbo.Courses'”错误消息。这是因为我们数据库中的表名是单数,而上下文中的列表名称是复数,与表名直接匹配。为避免此问题,您需要如下修改 DemoDbContext 类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using MVC3Demo.Bll.Models;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace MVC3Demo.Bll
{
    public class DemoDbContext : DbContext
    {
        public DbSet<Student> Students { get; set; }

        public DbSet<course /> Courses { get; set; }

        public DbSet<studentcourse /> StudentCourses { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<pluralizingtablenameconvention />();
        }
    }
}

转到 Global.asax 文件,并修改 register route 方法,将系统指向‘Student’索引页。

327945/7._Route_Register.PNG

然后运行项目,看看一切是否正常..

327945/8._Screen_Shot.PNG

现在让我们看看‘StudentCourse’的创建视图,看看必需的下拉框是如何自动创建的..

327945/8._Screen_Shot_Create.PNG

至此,我们已完成了基本系统的开发。然而,如果您查看三个控制器类,您会发现存在重复的代码。为了提高代码的整体质量,我决定添加一个抽象控制器类。这样我就可以避免重复代码,并为系统建立更好的架构。这样,我可以让新加入的开发人员轻松适应代码,并以很小的学习曲线编写高质量的代码。

创建抽象控制器类

在创建此类的过程中,我考虑进行一些额外的重构,如下所示:

  • 添加一个名为 Common 的单独文件夹,并将您可能希望在任何项目中通用的几个类添加到此文件夹。这包括异常日志记录、Cookie 处理、web.config 的应用程序设置数据处理,以及一个包含所有常用字符串的类。
  • 此外,我决定向同一个 Common 文件夹添加两个扩展。我希望它们将来能派上用场。一个扩展用于 Query 类,另一个用于 View 类。
  • 注意:随着您开发多个项目,此文件夹中找到的类可以轻松地成长为一个单独的项目。这些项目可以归入您的公司名称下。例如,如果您的公司名称是 XYZ,那么通用项目可以命名为‘Xyz.Common’,让您的公司开发的所有项目引用此项目。通过这种方式,您可以为适用的领域设定公司范围的标准。

  • 添加一个名为 Libraries 的单独文件夹。这将保留此项目引用的所有第三方 DLL。这样,您可以避免在将项目部署到生产环境后出现的一些常见的 DLL 引用相关错误。
  • 向 Web 项目的 Model 文件夹添加另一个名为 ConfirmationViewModel 的类。这只是为了演示如何为删除记录调用共享视图。您将在稍后看到它的使用。

通过这些重构,我设法从我的控制器类中移除了几乎所有重复的代码,并将它们实现到我的通用抽象类中。最后,完全翻新的 CourseController 类如下所示:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVC3Demo.Bll.Models;
using MVC3Demo.Bll;

namespace Mvc3Demo.Controllers
{
    public class CourseController : BaseController<Course>
    {
        public CourseController()
            : this(new DemoDbContext())
        { }

        public CourseController(DemoDbContext db)
            : base(db.Courses, db)
        {
        }

        protected override void LoadEditViewBags(Course entity)
        {
        }

        protected override void LoadAddViewBags()
        {
        }

        protected override Type LogPrefix
        {
            get { return this.GetType(); }
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}

重要的基类控制器如下。此代码的一部分取自我们内部公司的一个项目。因此,它包含一些额外的常用方法。即使您决定在您的项目中使用此代码,我也没有任何问题。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVC3Demo.Bll;
using System.Data.Entity;
using System.Data;
using MvcContrib.UI.Grid;
using log4net;
using System.Reflection;
using System.Data.Entity.Infrastructure;
using Mvc3Demo.Common;
using Mvc3Demo.Models;

namespace Mvc3Demo.Controllers
{
    public abstract class BaseController<E> : Controller
        where E : class
    {
        protected DbSet<e /> dbEntitySet;
        protected DemoDbContext db;
        protected readonly ILog logManager;

        public BaseController()
        { }

        public BaseController(DbSet<e /> dbSet, DemoDbContext dbContext)
        {
            dbEntitySet = dbSet;
            db = dbContext;
            logManager = LogManager.GetLogger(LogPrefix);
        }

        public BaseController(DemoDbContext dbContext)
        {
            dbEntitySet = null;
            db = dbContext;
            logManager = LogManager.GetLogger(LogPrefix);
        }

        public virtual ViewResult Index()
        {
            return View(dbEntitySet);
        }

        //
        // GET: /entity/Details/5
        /// <summary />
        /// Get record of the entity from the database 
        /// and pass it on to the Details 'PartialView'
        /// </summary />
        /// <param name="id" />Primary Key of the record to be searched</param />
        /// <returns /></returns />
        public virtual PartialViewResult Details(int id)
        {
            return PartialView(GetDetailsData(id));
        }

        //
        // GET: /entity/Create
        /// <summary />
        /// Create the empty View of the entity
        /// </summary />
        /// <returns /></returns />
        public virtual ActionResult Create()
        {
            GetCreateData();
            return View();
        }

        //
        // POST: /entity/Create
        /// <summary />
        /// newly create the entity object in the database. calls to the 'GetCreatePostBackData' 
        /// and then call to the 'LoadEditViewBags' and fianlly return a 'View'
        /// </summary />
        /// <param name="entity" /></param />
        /// <param name="DoNotRedirect" /></param />
        /// <returns /></returns />
        [HttpPost]
        public virtual ActionResult Create(E entity, bool? DoNotRedirect)
        {
            try
            {
                int i = GetCreatePostBackData(entity, DoNotRedirect);
                if (i < 0)
                {
                    LoadEditViewBags(entity);
                    return View(entity);
                }
                else
                    return Redirect(PortalCookie.Get(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO));
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        // GET: /entity/Delete/5
        /// <summary>
        /// Delete a record via Ajax delete popup
        /// </summary>
        /// <param name="id" />
        /// <returns>
        public virtual ActionResult Delete(int id)
        {
            try
            {
                PortalCookie.Set(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO, Request.UrlReferrer.ToString());
                ConfirmationViewModel confirm = new ConfirmationViewModel();
                confirm.Id = id;
                confirm.Action = "Delete";
                confirm.Controller = typeof(E).Name;
                confirm.OperationName = "Delete";
                if (Request.IsAjaxRequest())
                    return PartialView("Confirmation", confirm);
                else
                    return View("Confirmation", confirm);
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        //
        // POST: /entity/Delete/5
        /// <summary>
        /// Once a delete is confirmed via the popup it will do the DB operation 
        /// and relavent messages added to the view bag
        /// </summary>
        /// <param name="id" />
        /// <returns>
        [HttpPost, ActionName("Delete")]
        public virtual ActionResult DeleteConfirmed(int id)
        {
            try
            {
                E entity = dbEntitySet.Find(id);
                dbEntitySet.Remove(entity);
                int i = db.SaveChanges();
                this.AddToMessagePlus(i + " record(s) Deleted Successfully!"
                    , MessageTypes.Success);
                return Redirect(PortalCookie.Get(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO));
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        //
        // GET: /entity/Edit/5
        /// <summary>
        /// Update a particular entity. 
        /// </summary>
        /// <param name="id" />
        /// <returns>
        public virtual ActionResult Edit(int id)
        {
            try
            {
                E entity = GetEditData(id);
                return View(entity);
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        //
        // POST: /entity/Edit/5

        [HttpPost]
        public virtual ActionResult Edit(E entity, bool? DoNotRedirect)
        {
            try
            {
                int i = GetEditPostBackData(entity, DoNotRedirect);

                if (i < 0)
                {
                    LoadEditViewBags(entity);
                    return View(entity);
                }
                else
                    return Redirect(PortalCookie.Get(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO));
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        protected int EditEntity(E entity)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    UpdateTrackingData(entity, false);
                    db.Entry(entity).State = EntityState.Modified;
                    int i = db.SaveChanges();
                    return i;
                }
                return -2;
            }
            catch (Exception e)
            {
                logManager.Error("Error in Controller", e);
                return -1;
            }
        }

        protected int CreateEntity(E entity)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    UpdateTrackingData(entity, true);
                    dbEntitySet.Add(entity);
                    int i = db.SaveChanges();
                    return i;
                }
                return -2;
            }
            catch (Exception e)
            {
                this.logManager.Error("Db Related Error Occured", e);
                return -1;
            }
        }

        protected IEnumerable<e> GetIndexData()
        {
            try
            {
                return UpdateIncludes(dbEntitySet).AsEnumerable();
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        protected E GetDetailsData(int id)
        {
            try
            {
                return dbEntitySet.Find(id);
            }
            catch (Exception e)
            {
                this.AddToMessage(e.Message, MessageTypes.Error);
                logManager.Error("Error in Controller", e);
                throw e;
            }
        }

        protected void GetCreateData()
        {
            PortalCookie.Set(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO, (Request.UrlReferrer == null)
                ? Request.RawUrl.ToString() : Request.UrlReferrer.ToString());
            LoadAddViewBags();
        }

        protected E GetEditData(int id)
        {
            PortalCookie.Set(ConstString.URL_DATA, ConstString.URL_REDIRECT_TO, Request.UrlReferrer.ToString());
            E entity = dbEntitySet.Find(id);
            LoadEditViewBags(entity);
            return entity;
        }

        /// <summary>
        /// CreateEntity is called and 
        /// then relavent message is add to the view bag
        /// </summary>
        /// <param name="entity" />
        /// <param name="DoNotRedirect" />
        /// <returns>
        protected int GetCreatePostBackData(E entity, bool? DoNotRedirect)
        {
            int i = CreateEntity(entity);
            return AddCreateSatusMessage(DoNotRedirect, i);
        }

        protected int AddCreateSatusMessage(bool? DoNotRedirect, int i)
        {
            if (i == ConstString.ERROR_INT)
                this.AddToMessage("Error: No record(s) Created!", MessageTypes.Error);
            else if (i == ConstString.ERROR_INVALID_OBJECT_INT)
                this.AddToMessage("Warning: Some record(s) yet to be filled!", MessageTypes.Warning);
            else if ((DoNotRedirect.HasValue) && (DoNotRedirect.Value))
                this.AddToMessage(i + " record(s) Created Successfully!", MessageTypes.Success);
            else
                this.AddToMessagePlus(i + " record(s) Created Successfully!", MessageTypes.Success);
            return i;
        }

        /// <summary>
        /// EditEntity is called and 
        /// then relavent message is add to the view bag
        /// </summary>
        /// <param name="entity" />
        /// <param name="DoNotRedirect" />
        /// <returns>
        protected int GetEditPostBackData(E entity, bool? DoNotRedirect)
        {
            int i = EditEntity(entity);
            return AddEditStatusMessage(DoNotRedirect, i);
        }

        protected int AddEditStatusMessage(bool? DoNotRedirect, int i)
        {
            if (i == ConstString.ERROR_INT)
                this.AddToMessage("Error: No record(s) Updated!", MessageTypes.Error);
            else if (i == ConstString.ERROR_INVALID_OBJECT_INT)
                this.AddToMessage("Warning: Some record(s) yet to be filled!", MessageTypes.Warning);
            else if ((DoNotRedirect.HasValue) && (DoNotRedirect.Value))
                this.AddToMessage(i + " record(s) Updated Successfully!", MessageTypes.Success);
            else
                this.AddToMessagePlus(i + " record(s) Updated Successfully!", MessageTypes.Success);
            return i;
        }

        private PagedViewModel<e> createPageViewModel(GridSortOptions gridSortOptions
            , int? page, string sortColumn)
        {
            ShowCurrentMessage();
            return new PagedViewModel<e>
            {
                ViewData = ViewData,
                Query = dbEntitySet,
                GridSortOptions = gridSortOptions,
                DefaultSortColumn = sortColumn,
                Page = page,
                PageSize = Common.ConstString.PAGE_SIZE,
            };
        }

        private PagedViewModel<e> createPageViewModel(GridSortOptions gridSortOptions
            , int? page, string sortColumn, DbQuery<e> dbQuery)
        {
            ShowCurrentMessage();
            return new PagedViewModel<e>
            {
                ViewData = ViewData,
                Query = dbQuery,
                GridSortOptions = gridSortOptions,
                DefaultSortColumn = sortColumn,
                Page = page,
                PageSize = Common.ConstString.PAGE_SIZE,
            };
        }

        private void UpdateTrackingData(E entity, bool isNew)
        {
            this.UpdateTrackingData<e>(entity, isNew);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name" />Name of the view
        /// <param name="value" />Select list to be assigned to a drop down box
        /// <returns>BaseController itself</returns>
        internal BaseController<e> AddToViewBag(string name, SelectList value)
        {
            ViewData[name] = value;
            return this;
        }

        /// <summary>
        /// If you have any dropdown boxes please use this method to load their list
        /// to respective view bags.
        /// </summary>
        /// <example>
        /// this.AddToViewBag("EmployeeId", new SelectList(db.Employees, "EmployeeId", "EPFNo"))
        /// .AddToViewBag("DowntimeReasonId", new SelectList(db.DowntimeReasons, "DowntimeReasonId", "Name"))
        /// </example>
        protected abstract void LoadAddViewBags();

        protected abstract void LoadEditViewBags(E entity);

        protected virtual IQueryable<e> UpdateIncludes(DbSet<e> dbEntitySet)
        {
            return dbEntitySet.AsQueryable<e>();
        }

        protected void UpdateTrackingData<t>(T entity, bool isNew)
        {
            PropertyInfo pInfoMBy = entity.GetType().GetProperty(ConstString.PROPERTY_MODIFY_BY);
            if (pInfoMBy != null)
                pInfoMBy.SetValue(entity, Convert.ChangeType(User.Identity.Name, pInfoMBy.PropertyType), null);
            PropertyInfo pInfoMDate = entity.GetType().GetProperty(ConstString.PROPERTY_MODIFY_DATE);
            if (pInfoMDate != null)
                pInfoMDate.SetValue(entity, Convert.ChangeType(DateTime.Now, pInfoMDate.PropertyType), null);
            PropertyInfo pInfoABy = entity.GetType().GetProperty(ConstString.PROPERTY_ADDED_DATE);
            if (pInfoABy != null)
                pInfoABy.SetValue(entity, Convert.ChangeType(DateTime.Now, pInfoABy.PropertyType), null);
        }

        /// <summary>
        /// Return this.GetType(); This name is used to trace
        /// error locations of the log message write via log4net library.
        /// </summary>
        protected abstract Type LogPrefix
        {
            get;
        }

        public class CommonMessage
        {
            public CommonMessage()
            {
                this.Message = "";
                this.MessageType = MessageTypes.None;
            }

            public CommonMessage(string message, MessageTypes mType)
            {
                this.Message = message;
                this.MessageType = mType;
            }
            public string Message { get; set; }
            public MessageTypes MessageType { get; set; }
        }

        public CommonMessage GetFromMessagePlus()
        {
            string[] tempArray = TempData.GetMessageSummary();
            if (tempArray == null)
                return null;
            if (tempArray.Length > 0)
            {
                string[] mesgs = tempArray[0].Split('|');
                int t;
                if (int.TryParse(mesgs[1], out t))
                    return new CommonMessage(mesgs[0], (MessageTypes)t);
            }
            return null;
        }

        public void AddToMessagePlus(string message, MessageTypes mtype)
        {
            TempData.AddStatusMessage(message + "|" + (int)mtype);
        }

        public void ShowCurrentMessage()
        {
            CommonMessage temp = GetFromMessagePlus();
            if (temp != null)
                AddToMessage(temp.Message, temp.MessageType);
        }

        public void AddToMessage(string message, MessageTypes mtype)
        {
            switch (mtype)
            {
                case MessageTypes.Success:
                    {
                        ViewBag.successbox = message;
                    } break;
                case MessageTypes.Warning:
                    {
                        ViewBag.warningbox = message;
                    } break;
                case MessageTypes.Error:
                    {
                        ViewBag.errormsgbox = message;
                    } break;
            }
        }
    }

    public enum MessageTypes
    {
        Error,
        Warning,
        Success,
        None
    }
}

您需要研究这个类,看看如何进一步改进它。与我的一些其他文章不同,我将不解释这个类的每一行代码,但如果您有任何具体问题,请将其作为问题类型的评论发布到本文,我将尽快回复您。

使 Grid 可排序

有很多花哨的 Grid 视图可以与 MVC2 Razor 视图一起使用。是的,正如你们都知道的,这方面还需要很多改进(MSFT 请注意)。在我看来,其中最好的就是 jQuery grid,但我认为使用更基础简单的‘MvcContrib.Gird’就足够了。

您可以在这里了解更多关于如何集成‘MVCContrib’ grid 的信息:http://mvccontrib.codeplex.com/wikipage?title=Grid

在集成 grid 的过程中,我不得不从我的抽象类中删除 Index 方法(Action)的实现。这在我与 CourseController 类中想要的新方法之间造成了歧义。因此,如果我使用 MVCContrib grid,我有两种可能的设计选项:

  • 从抽象基类控制器中删除 Index 方法的实现,并在具体的控制器类中实现它。
  • 为在具体控制器中执行操作实现定义一个新的 View,同时保留抽象控制器的 Index 方法。

我决定选择第一种方案,因为它使我的设计更简单,并且提供了相对较少的选择 微笑 | :)。软件设计有一个著名的理论,‘关键不在于添加,而在于移除所有不必要的’。让我们来实践一下。

新修改的 Course 的 Index ‘View’的外观如下:

327945/8._Index_COde.PNG

最终的‘Course’ Index view 如下所示:

327945/9._Final_Screen.png

下一篇推荐阅读

© . All rights reserved.