昨天刚刚完成了学校六十校庆上用的文字直播系统,当然不是使用MVC做的。今天我再使用ASP.NET MVC3+EF+JQuery完善一下这个系统,也作为Entity Framework 4 in Action读书笔记系列前期的一个例子吧。
创建解决方案和项目
1. 首先,新建一个空的解决方案
解决方案的名称为:LiveText,如下图:
2. 创建完解决方案,还需要创建三个项目,具体如下面的表格:
| 项目名称 | Visual Studio项目模板 | 用途 |
LiveText.Domain
|
C#类库
|
保存域的实体和逻辑
|
LiveText.WebUI
|
ASP.NET MVC 3 Web Application
|
存储控制器和视图
|
LiveText.UnitTests
|
Test Project
|
单元测试
|
3. 添加引用
我们的项目中使用到了Ninject,Moq工具类库,首先需要添加对它们的引用,简便的方法是使用VS的Package Manager Console(View ➤ Other Windows ➤Package Manager Console),输入下面的命令:
Install-Package Ninject -Project LiveText.WebUI
Install-Package Ninject -Project LiveText.UnitTests
Install-Package Moq -Project LiveText.UnitTests
具体如下图:
具体项目之间的依赖关系如下表:
| 项目名称 | 工具依赖 | 项目依赖 |
LiveText.Domain
|
没有
|
没有
|
LiveText.WebUI
|
Ninject
|
LiveText.Domain
|
LiveText.UnitTests
|
Ninject,Moq
|
LiveText.Domain,LiveText.WebUI
|
4. 设置依赖注入容器
项目中,我们使用Ninject创建控制器和处理依赖注入(DI)。在LiveText.WebUI项目中新建一个Infrastructure的文件夹,在该文件夹中新建一个NinjectControllerFactory类,代码如下:
public class NinjectControllerFactory : DefaultControllerFactory { private IKernel ninjectKernel; public NinjectControllerFactory() { ninjectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType); } private void AddBindings() { } }
然后修改Global.asax如下
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); //修改的这个地方 ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); }
效果如下图:
至此,项目的基本框架就做完了,下面设计数据库。
设计数据库
这里使用EF Code-First。
1. 编写实体类
人民网的文字直播系统分为“国新办发布会直播”、“国台办发布会直播”等类别,每个类别下面又有很多直播的内容。文字直播系统大体需要这几个实体类:
Category —— 类别类 Title —— 标题类
Text —— 文字类 User —— 用户类
在LiveText.Domain项目中新建一个文件夹Entities,在该文件夹中新建上面四个类:
public class Category { /// <summary> /// 类别编号 /// </summary> public int CategoryID { get; set; } /// <summary> /// 类别名称 /// </summary> public string Name { get; set; } /// <summary> /// 标题集合 /// </summary> public ICollection<Title> Titles { get; set; } }
public class Title { /// <summary> /// 标题编号 /// </summary> public int TitleID { get; set; } /// <summary> /// 标题名称 /// </summary> public string Name { get; set; } /// <summary> /// 所属类别 /// </summary> public Category Category { get; set; } /// <summary> /// 文字集合 /// </summary> public ICollection<Text> Texts { get; set; } }
public class Text { /// <summary> /// 文字编号 /// </summary> public int TextID { get; set; } /// <summary> /// 发言人 /// </summary> public string Prolocutor { get; set; } /// <summary> /// 发言内容 /// </summary> public string ProContent { get; set; } /// <summary> /// 日期 /// </summary> public DateTime ProDate { get; set; } /// <summary> /// 所属标题 /// </summary> public Title Title { get; set; } }
public class User { /// <summary> /// 用户编号 /// </summary> public int UserID { get; set; } /// <summary> /// 用户名 /// </summary> public string UserName { get; set; } /// <summary> /// 用户密码 /// </summary> public string Password { get; set; } }
2. 添加EFCodeFirst
在Package Manager Console中输入命令:
Install-Package EFCodeFirst -Project LiveText.Domain
3. 创建上下文类
在LiveText.Domain项目中,新建名为Concrete的文件夹,在该文件夹中新建一个LiveTextDbContext的类,它继承自System.Data.Entity.DbContext,具体代码如下:
public class LiveTextDbContext : DbContext { public DbSet<Category> Categories { get; set; } public DbSet<Title> Titles { get; set; } public DbSet<Text> Texts { get; set; } public DbSet<User> Users { get; set; } }
4. 修改Web.config
打开LiveText.WebUI项目的Web.config,添加一个数据库连接字符串,name的值要和上下文类的名称一样。
<connectionStrings> <add name="LiveTextDbContext" connectionString="Data Source=.;Initial Catalog=LiveText;Integrated Security=True;Pooling=False" providerName="System.Data.SqlClient"/> </connectionStrings>
新建一个HomeController,添加如下代码:
public class HomeController : Controller { LiveTextDbContext context = new LiveTextDbContext(); // // GET: /Home/ public ActionResult Index() { var categories = context.Categories; return View(categories); } }
给Index添加一个View,如下图:
现在就可以运行了,运行结果如下:
再看看数据库里,EF已经为我们自动生成了数据库,数据库的结构如下图:
至此,我们数据库的设计就完成了。
实现登录
这一篇,简简单单的把后台的登录功能实现,没有什么技术含量 
首先在LiveText.WebUI项目中的Model文件夹中添加一个LogOnViewModel类,代码如下:
public class LogOnViewModel { [Required(ErrorMessage = "不能为空")] public string UserName { get; set; } [Required(ErrorMessage = "不能为空")] [DataType(DataType.Password)] public string Password { get; set; } }
第二,添加一个AccountController,在Scaffolding选项里面Template选择Empty controller,如下图所示:
AccountController中的代码也很简单,主要是一个登录的action和退出的action,下面是具体代码:
public class AccountController : Controller { private LiveTextDbContext context = new LiveTextDbContext(); // // GET: /Account/LogOn public ActionResult LogOn() { return View(); } // // POST: /Account/LogOn [HttpPost] public ActionResult LogOn(LogOnViewModel model) { if (ModelState.IsValid) { if (context.Users.Any(u => u.UserName == model.UserName && u.Password == model.Password)) { FormsAuthentication.SetAuthCookie(model.UserName, false); return View("~/Views/Admin/Index.cshtml"); } else { ModelState.AddModelError("", "用户名或密码不正确"); } } return View(model); } public ActionResult LogOff() { FormsAuthentication.SignOut(); return View("LogOn"); }
在LogOn action中,如果用户提供的用户名和密码正确,就跳转到Index.cshtml。Index.cshtml在View文件夹里的Admin文件夹中,Admin是我新建的文件夹。
第三,在LogOn上右击,选择AddView,默认选项即可,添加之后,在View文件夹里自动生成Account文件夹,以及Account文件夹里LogOn.cshtml。
第四,在LogOn.cshtml,我在网上随便找了个模板加上了,具体代码可以下载源代码看一下。下面我只贴出关键性的代码:
@Html.EditorFor(u => u.UserName) @Html.ValidationMessageFor(u => u.UserName)
@Html.EditorFor(u => u.Password) @Html.ValidationMessageFor(u => u.Password)
下面让我们看一下运行的效果吧:
登录页面
输入用户名:admin 密码:admin登录成功
上一篇简单的实现了一下登录的功能,这一篇首先把后台的框架撘一撘。
搭框架
还是使用上文提到的模板,该模板使用frameset框架,关于在ASP.NET MVC下如何使用frameset框架,我推荐大家看一篇文章在ASP.NET MVC下使用frameset框架!。
上一篇中,我们在View文件夹下新建了一个Admin文件夹,所以,我们先新建一个AdminController,添加如下代码:
public class AdminController : Controller { // // GET: /Admin/ [Authorize] public ActionResult Index() { return View("Index"); } [Authorize] public ActionResult Top() { return View("Top"); } [Authorize] public ActionResult Left() { return View("Left"); } }
相对应新建如下视图:
Index.cshtml里面使用frameset引用Top.cshtml和Left.cshtml,方式如下,具体可以看上面那篇文章。
<frame src="@Url.Action("Top")" noresize="noresize" frameborder="NO" name="topFrame" scrolling="no" marginwidth="0" marginheight="0" target="main" />
下面还需要修改一下AccountController里面的LogOn action,具体如下:
// // POST: /Account/LogOn [HttpPost] public ActionResult LogOn(LogOnViewModel model) { if (ModelState.IsValid) { if (context.Users.Any(u => u.UserName == model.UserName && u.Password == model.Password)) { FormsAuthentication.SetAuthCookie(model.UserName, false); //下面是修改的地方 return RedirectToAction("Index", "Admin"); } else { ModelState.AddModelError("", "用户名或密码不正确"); } } return View(model); }
这样框架大体就搭好了,效果如下:
实现用户管理
要实现用户管理,我们先修改LiveText.Domain项目中User实体,因为我们是用Code-First生成的数据库,如果修改了实体,再一次运行就会出错,所以第一件要做的事就是在LiveText.WebUI项目中的Model文件夹中新建一个LiveTextDbInitializer类,用于当实体发生变化时重新生成数据库。
public class LiveTextDbInitializer : DropCreateDatabaseIfModelChanges<LiveTextDbContext> { protected override void Seed(LiveTextDbContext context) { context.Users.Add(new Domain.Entities.User { UserName = "admin", Password = "admin" }); base.Seed(context); } }
然后在Global.asax中的 Application_Start方法中注册一下:
Database.SetInitializer(new LiveTextDbInitializer());
下面我们就可以修改User实体了。如果一开始都写好的话,现在就不用修改了,麻烦,
。修改后的代码如下:
public class User { /// <summary> /// 用户编号 /// </summary> public virtual int UserID { get; set; } /// <summary> /// 用户名 /// </summary> [Required(ErrorMessage = "不能为空")] [StringLength(30)] [Display(Name = "用户名")] public virtual string UserName { get; set; } /// <summary> /// 用户密码 /// </summary> [Required(ErrorMessage = "不能为空")] [DataType(DataType.Password)] [StringLength(30, MinimumLength = 5, ErrorMessage = "密码最短为5个字符")] [Display(Name = "密码")] public virtual string Password { get; set; } }
接下来,我们新建一个UserController,按照下图所示选择。
最后,将Left.cshtml里面的超链接改一下:
<li><a href="@Url.Action("Create", "User")" target="main">添加用户</a></li> <li><a href="@Url.Action("Index", "User")" target="main">用户列表</a></li>
OK,再运行一下程序:
没有加样式,所以很难看
实现文字直播管理
首先,实现类别管理。
修改Category实体。
public class Category { /// <summary> /// 类别编号 /// </summary> public virtual int CategoryID { get; set; } /// <summary> /// 类别名称 /// </summary> [Required(ErrorMessage = "不能为空")] [Display(Name = "类别名称")] public virtual string Name { get; set; } /// <summary> /// 标题集合 /// </summary> public virtual List<Title> Titles { get; set; } }
新建CategoryController。
Now,类别管理完成了,看一下运行效果:
第二,实现标题管理
首先修改Title实体。
public class Title { /// <summary> /// 标题编号 /// </summary> public virtual int TitleID { get; set; } /// <summary> /// 类别编号 /// </summary> [Required(ErrorMessage = "类别不能为空")] public virtual int CategoryID { get; set; } /// <summary> /// 标题名称 /// </summary> [Required(ErrorMessage = "标题不能为空")] [Display(Name = "标题名称")] public virtual string Name { get; set; } /// <summary> /// 所属类别 /// </summary> public virtual Category Category { get; set; } /// <summary> /// 文字集合 /// </summary> public virtual List<Text> Texts { get; set; } }
新建TitleController。
这样就完成了,看一下运行效果:
实现文字管理
首先修改Text实体。
public class Text { /// <summary> /// 文字编号 /// </summary> public virtual int TextID { get; set; } /// <summary> /// 标题编号 /// </summary> [Required(ErrorMessage = "标题不能为空")] [Display(Name = "所属标题")] public virtual int TitleID { get; set; } /// <summary> /// 发表人 /// </summary> [Required(ErrorMessage = "发表人不能为空")] [Display(Name = "发表人")] public virtual string Prolocutor { get; set; } /// <summary> /// 发表内容 /// </summary> [Required(ErrorMessage = "发表内容不能为空")] [Display(Name = "发表内容")] public virtual string ProContent { get; set; } /// <summary> /// 标题 /// </summary> [Display(Name = "发表时间")] public virtual DateTime ProDate { get; set; } /// <summary> /// 所属标题 /// </summary> [Display(Name = "所属标题")] public virtual Title Title { get; set; } }
新建TextController。
OK,看一下运行结果:
今天到此为止,下一篇用JQuery在前台显示。
在上一篇中本来打算结束的,最后遇到点小问题,不得不分开,废话少说,开始吧。
这一篇中,我们完成最后的工作,在页面中显示数据。我返回的是JSON数据,所以首先写一个JsonHelper类。
在LiveText.WebUI项目里新建一个Tool文件夹,在这个文件夹里新建一个JsonHelper类。代码如下:
public static class JsonHelper { /// <summary> /// Json序列化 /// </summary> /// <param name="value"></param> /// <returns></returns> public static string JsonSerializer(this object value) { JavaScriptSerializer s = new JavaScriptSerializer(); return s.Serialize(value); } }
然后我们在新建一个一般处理程序,就命名为GetInfoList.ashx吧。在前台页面,我们对它发起AJAX请求,返回JSON数据。
public class GetInfoList : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; LiveTextDbContext dbContext = new LiveTextDbContext(); var list = from t in dbContext.Texts.Where(t => t.Title.Name == "校庆文字直播") select new { t.Prolocutor, t.ProContent, t.ProDate }; string data = list.JsonSerializer(); context.Response.ContentType = "application/json"; context.Response.Write(data); context.Response.Flush(); context.Response.End(); } public bool IsReusable { get { return false; } } }
我在上面的代码中为了方便将程序写死了,只取得标题为“校庆文字直播”的文字。当然,你得在后台新建一个“校庆文字直播”的标题,将它放在一个类别里面。我已经将数据都放在数据库里面了,一会就可以看到程序运行的效果了。
下面我们就完成前台的东西吧。我们需要修改的就是Views➤Home下面的Index.cshtml。
在Body里面加上下面代码即可:
<div id="container"> <div id="live"> <ul> </ul> </div> </div>
然后加点CSS
<style type="text/css"> div#container { width: 770px; margin-left: auto; margin-right: auto; } div#live { width: 100%; } div#live ul { list-style: none; } div#live ul li { padding-bottom: 19px; } div#live ul li p { margin-top: 0; margin-bottom: 0; } div#live .evenlibackcolor { background-color: #F5F5F5; } div#live .oddlibackcolor { background-color: #FFF; } div#live .namespan { color: #E211A5; } div#live .timespan { font-size: small; color: #AAA; margin-left: 10px; } </style>
在引入两个JS文件,DateFormate.js的下载地址:http://files.cnblogs.com/nianming/DateFormat.js
<script src="@Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/DateFormat.js")" type="text/javascript"></script>
在写一下JS代码就搞定了:
<script type="text/javascript"> $(function () { init(); setInterval("dyoper()", 10000); }); //初始 function init() { var noCache = new Date(); $.ajax({ type: 'post', url: '/Models/GetInfoList.ashx?m=' + Date(), data: {}, success: function (data) { if (data != null) { jQuery.each(data, function (entryIndex, entry) { var mydate = ConvertJSONDateToJSDateObject(entry['ProDate']); var html = ''; if (entryIndex % 2 == 0) { html = '<li class="evenlibackcolor"><p><span class="namespan">['; } else { html = '<li class="oddlibackcolor"><p><span class="namespan">['; } html += entry['Prolocutor'] + ']:</span><span>'; html += entry['ProContent'] + '</span><span class="timespan">['; html += mydate.pattern("HH:mm:ss") + ']</span></p></li>'; $("#live ul").append(html); }); } } }); } function dyoper() { var noCache = new Date(); $.ajax({ type: 'post', url: '/Models/GetInfoList.ashx?m=' + Date(), data: {}, success: function (data) { if (data != null) { $("#live ul>li").remove(); jQuery.each(data, function (entryIndex, entry) { var mydate = ConvertJSONDateToJSDateObject(entry['ProDate']); var html = ''; if (entryIndex % 2 == 0) { html = '<li class="evenlibackcolor"><p><span class="namespan">['; } else { html = '<li class="oddlibackcolor"><p><span class="namespan">['; } html += entry['Prolocutor'] + ']:</span><span>'; html += entry['ProContent'] + '</span><span class="timespan">['; html += mydate.pattern("HH:mm:ss") + ']</span></p></li>'; $("#live ul").append(html); }); } } }); } function ConvertJSONDateToJSDateObject(JSONDateString) { var date = new Date(parseInt(JSONDateString.replace("/Date(", "").replace(")/", ""), 10)); return date; } </script>
下面看一下运行效果吧:
至此,就完成了。程序源代码下载地址:http://files.cnblogs.com/nianming/LiveText20101025.rar