Created
January 1, 2013 12:44
-
-
Save iannono/4427155 to your computer and use it in GitHub Desktop.
MVC3: Concise Guide
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## 准备工作 | |
[从全新数据库开始](http://msdn.microsoft.com/en-us/data/jj193542) | |
[从已有的数据库开始](http://msdn.microsoft.com/en-us/data/jj200620) | |
*值得注意的是需要安装Nuget和Power Tools两个插件,并且保证你的EF是5.0* | |
## 模型 | |
对于需要返回多个模型到同一个视图的情况,可以采用如下的方式: | |
//建立新的视图模型 | |
using System; | |
using System.Collections.Generic; | |
using GGZBTQPT_PRO.Models; | |
namespace GGZBTQPT_PRO.ViewModels | |
{ | |
public class T_ZC_SelectUser | |
{ | |
public ICollection<T_ZC_User> Users { get; set; } | |
public ICollection<T_ZC_Department> Departments { get; set; } | |
public ICollection<T_ZC_Role> Roles { get; set; } | |
public ICollection<T_ZC_System> Systems { get; set; } | |
} | |
} | |
//视图中的使用 | |
@model GGZBTQPT_PRO.ViewModels.T_ZC_SelectUser | |
@foreach (var user in Model.Users) | |
{ | |
<span>@user.Title</span> | |
} | |
@foreach (var role in Model.Roles) | |
{ | |
<span>@role.Title</span> | |
} | |
//控制器中的使用 | |
/// <summary> | |
/// 返回当前角色下的所有用户 | |
/// </summary> | |
/// <param name="id"></param> | |
/// <returns></returns> | |
public PartialViewResult SelectUser(int id) | |
{ | |
Array selected_users = db.T_ZC_Role.Where( r => r.ID == id).First().Users.ToArray(); | |
ViewBag.selected_users = selected_users; | |
var select_user = new T_ZC_SelectUser(); | |
select_user.Users = db.T_ZC_User.ToList(); | |
select_user.Departments = db.T_ZC_Department.ToList(); | |
return PartialView(select_user); | |
} | |
模型中多对多的查询,可以采用如下的方式: | |
db.Role.Where( r => r.ID == id).First().Users.ToList(); | |
其中Role和User是多对多的关系 | |
写在map中的映射关系对应的是对数据库的操作,在多对多的应用中,只需要编写一边的映射关系即可 | |
## 区域 | |
添加区域后,如何保证路由的正确,需要添加对应的命名空间 | |
//修改Global.asax.cs | |
public static void RegisterRoutes(RouteCollection routes) | |
{ | |
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); | |
routes.MapRoute( | |
"Default", // Route name | |
"{controller}/{action}/{id}", // URL with parameters | |
new { controller = "Home", action = "Index", id = UrlParameter.Optional },// Parameter defaults | |
new[] { "Web.Controllers" }// 引入默认的命名空间,Web代表项目的名称,请按需修改 | |
); | |
} | |
从外部路由到area的写法: | |
@Html.ActionLink("xxx", "action", "controller", new { area = "area_name"}, null) | |
## 视图 | |
当控制器范围list的时候,需要在视图中申明集合类型,eg: | |
`@model Icollection<GGZBTQPT.Models.T_ZC_User` | |
关于ICollection<>, IEnumerator<>, IList<>,参考[IEnumerator.IEnumerable.ICollection.IList](http://www.cnblogs.com/lenxu/archive/2012/01/09/2316935.html) | |
当需要在视图中自定义一些展示逻辑的时候,可以通过`@helper`进行封装,eg: | |
@helper GenerateUser(string selected_users, GGZBTQPT_PRO.Models.T_ZC_User user) | |
{ | |
if(selected_users.IndexOf(user.ID.ToString()) > 0) | |
{ | |
<li><a tname="@user.Name" tvalue="@user.ID" checked=true >@user.Name</a></li> | |
} | |
else | |
{ | |
<li><a tname="@user.Name" tvalue="@user.ID" >@user.Name</a></li> | |
} | |
} | |
当需要每个页面实现单独的逻辑的时候,可以通过section进行定义 | |
@RenderSection("name",false) | |
@section name { // 各自的独立逻辑 } | |
在超链接中使用action,可以采用@Url.Action | |
<a class="add" href="@Url.Action("create","ZC_Menu", new { system_id = ViewBag.SystemID})" rel="create" target="dialog" title="添加新的功能菜单" mask="true" width="420" height="340"><span>添加菜单</span></a> | |
关于dropdownlist | |
@Html.DropDownList("ParentID", "请选择上级菜单") | |
new SelectList(db.T_ZC_Menu.Where(p => p.IsValid == true && p.SystemID == system_id), "ID", "Name", t_zc_menu.ParentID); | |
## 控制器 | |
使用DWZ,控制器的返回值为PartialView,否则,需要在视图页面中添加`layout = null`,用于避免mvc自动添加布局模板 | |
## 常用的对象关系 | |
*一对多* | |
public class T_ZC_Menu | |
{ | |
public int ID { get; set; } | |
public int SystemID { get; set; } | |
public T_ZC_System System { get; set; } | |
} | |
public class T_ZC_System | |
{ | |
public int ID { get; set; } | |
public virtual ICollection<T_ZC_Menu> Menus { get; set; } | |
} | |
//(Menus)一个系统对应多个功能菜单 | |
this.HasRequired(t => t.System) | |
.WithMany(s => s.Menus) | |
.HasForeignKey(t => t.SystemID) | |
.WillCascadeOnDelete(false); | |
*多对多* | |
public class T_ZC_Role | |
{ | |
public int ID { get; set; } | |
public virtual ICollection<T_ZC_User> Users { get; set; } | |
} | |
public class T_ZC_User | |
{ | |
public int ID { get; set; } | |
public ICollection<T_ZC_Role> Roles { get; set; } | |
} | |
// (Roles) 每个角色对应多个用户,每个用户对应多个角色 | |
//~~如果需要双向的多对多更新,需要在两边的model映射中都编写相应的映射关系~~ | |
//多对多只需要在其中一边的模型上增加映射关系即可 | |
//Role | |
this.HasMany(r => r.Users) | |
.WithMany(u => u.Roles) | |
.Map(m => | |
{ | |
m.ToTable("T_ZC_RoleT_ZC_User"); | |
m.MapLeftKey("T_Role_ID"); | |
m.MapRightKey("T_User_ID"); | |
}); | |
[其他实体关系参考](http://www.cnblogs.com/dudu/tag/Entity%20Framework%20%E5%AE%9E%E8%B7%B5%E7%B3%BB%E5%88%97/) | |
补充一点:建立双向外键联系(即双向一对一)的时候,需要使用 .WillCascadeOnDelete(false),否则无法操作数据库,参考如下: | |
public T_HY_MemberMap() | |
{ | |
// Primary Key | |
this.HasKey(t => t.ID); | |
// Properties | |
this.Property(t => t.ID) | |
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); | |
this.HasRequired(t => t.Person) | |
.WithMany() | |
.HasForeignKey(a => a.PersonID) | |
.WillCascadeOnDelete(true); | |
} | |
public T_QY_CorpMap() | |
{ | |
// Primary Key | |
this.HasKey(t => t.ID); | |
// Foreign Key | |
this.HasRequired(t => t.Member) | |
.WithMany() | |
.HasForeignKey(a => a.MemberID) | |
.WillCascadeOnDelete(false); | |
} | |
2012-10-16更新 | |
多对多的这种实体关系只在已经生成的实体上才能应用,也就是说必须是数据库中取出的已有数据所生成的实体,如下的情况是会报错的 | |
var member = new Member(); | |
var favored_item = new Favorite(); | |
favored_item.Members.Add(member); 此时的favored_item.Members为null | |
//正确的方式应该是: | |
member.Favorites.Add(favored_item); //此时member.Favorites为null | |
//多对多删除的时候,对应的级联删除需要手动处理,这是为了避免产生循环删除的问题 | |
## DWZ相关 | |
因为DWZ是一个独立的富客户端框架,基本可以用来替换mvc的布局架构,它通过配置文件自动载入所需的框架信息,包括弹出窗口、tab页等都通过js生成。因此,使用的时候只需在模板页中载入所需的js文件及初始化的js代码,首页中写入整个应用的布局信息。其他的相关功能页面可以采用分部视图的方式进行嵌入,而无需用到其他的模板页。 | |
由于界面部件都是通过js生成的,因此在使用的时候需要注意_通过ajax读取的内容可能需要重复执行一些初始化的js代码,用来为新生成的内容增加js处理事件_;比较典型的就是通过弹出窗口读取模型新增视图的时候,无法执行客户端校验的问题,可以通过如下的方式解决: | |
<script type="text/javascript"> | |
$.validator.unobtrusive.parse(document); | |
</script> | |
在指定rel,使用ajax进行更新的时候,不同的视图,相关的rel名称需要不一样,例如: | |
<li> @Html.ActionLink(system.Name,"MenuInfo","ZC_Menu",new { system_id = system.ID},new { target = "ajax", rel = "menuInfoBox" })</li> | |
<li> @Html.ActionLink(department.Name,"UserInfo","ZC_User",new { department_id = department.ID},new { target = "ajax", rel = "userInfoBox" })</li> | |
在使用ActionLink等help方法的时候,对于传递的routeValues,需要保证key的名称和action中的方法参数名称一致,eg: | |
<li> @Html.ActionLink(department.Name,"UserInfo","ZC_User",new { department_id = department.ID},new { target = "ajax", rel = "userInfoBox" })</li> | |
//对应的action方法,也需要使用department_id | |
public PartialViewResult UserInfo(int department_id) | |
{ | |
var users = db.T_ZC_User.Include("Department").Where(m => m.DepartmentID == department_id); | |
return PartialView(users); | |
} | |
设置ajaxToDo参数的超链接,和ajax表单一样,通过返回的json串进行回调。如果需要刷新当前navTab,navTabId参数需要返回false,同时默认的navTabAjaxDone方法是没有重载当前tab页的,需要修改dwz.ajax.js | |
function navTabAjaxDone(json) { | |
DWZ.ajaxDone(json); | |
if (json.statusCode == DWZ.statusCode.ok) { | |
if (json.navTabId) { //把指定navTab页面标记为需要“重新载入”。注意navTabId不能是当前navTab页面的 | |
navTab.reloadFlag(json.navTabId); | |
} else { //重新载入当前navTab页面 | |
navTab.reload(); #=>加上这句就好了 | |
navTabPageBreak({}, json.rel); | |
} | |
if ("closeCurrent" == json.callbackType) { | |
setTimeout(function () { navTab.closeCurrentTab(); }, 100); | |
} else if ("forward" == json.callbackType) { | |
navTab.reload(json.forwardUrl); | |
} | |
} | |
## Migration常用命令 | |
`Enable-Migrations` 用于启用Migration | |
`Add-Migration migration-name` 用于添加新的migration,该命令会自动比较当前模型中所做的修改,生成对应的数据库操作语句 | |
`Update-Database` 用于将修改更新到数据库 | |
[详细参考](http://msdn.microsoft.com/en-us/data/jj591621) | |
## Enumberable的一些常用方法 | |
## 资源文件的操作 | |
修改webconfig文件 | |
<system.web> | |
<globalization uiCulture="auto" culture="auto" enableClientBaseCulture="true"/> | |
</system.web> | |
增加App_GlobalResources文件夹,并在该文件夹下增加对应的资源文件;cs文件中的命名空间改为相对应的文件路径,eg:`GGZBTQPT_PRO.App_GlobalResource` | |
## 关于枚举类型 | |
如果想要使用枚举类型,必须首先保证使用的是vs2012;某些文章里面说到的通过Nuget安装EF5,其实只是安装了其中的一部分特性。而EF5所支持的enum特性,需要.net4.5的支持,而.net4.5不支持vs2010,悲剧啊[参考:](http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/55dc58a4-6190-4bad-b317-1787cf86bf3d) | |
在下拉菜单中使用枚举类型进行绑定,[参考](http://stackoverflow.com/questions/388483/how-do-you-create-a-dropdownlist-from-an-enum-in-asp-net-mvc) | |
@Html.DropDownListFor(model => model.Type, new SelectList(Enum.GetNames(typeof(MemberTypes)))) | |
//or in controller, 这里的(int)type实在是让我很困惑的一件事 | |
var types = from MemberTypes type in Enum.GetValues(typeof(MemberTypes)) | |
select new { ID = (int)type, Name = type.ToString() }; | |
ViewData["MemberTypes"] = new SelectList(types, "ID", "Name"); | |
//in view | |
@Html.DropDownList("MemberTypes") | |
## 关于测试 | |
入门链接 | |
http://dotnetslackers.com/articles/aspnet/Built-in-Unit-Test-for-ASP-NET-MVC-3-in-Visual-Studio-2010-Part-1.aspx | |
http://dotnetslackers.com/articles/aspnet/Built-in-Unit-Test-for-ASP-NET-MVC-3-in-Visual-Studio-2010-Part-2.aspx | |
## 关于Excel生成和读取 | |
生成参考:http://code.google.com/p/excel-generator/wiki/MVC_ExcelResult | |
读取参考:https://github.com/paulyoder/LinqToExcel#welcome-to-the-linqtoexcel-project | |
## 一些小技巧 | |
* 通过为字段设置`[Display(Name = "登录名")]`,可以避免在视图中编写额外的labelText | |
* action中传递的参数名是不分大小写的 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment