看书的时候遇到很多不知所云的错误,都是在网上找到的解决方法,没办法,从asp.net到mcv很多的新技术,没有一点思路,只能在网上搜罗了。
1.更新产品不成功
更新产品的时候一直不能更新成功,但是很奇怪的是添加是没问题的,看到网上说再更新的时候要通知EF当前执行的是更新动作,加上context.Entry(product).State = System.Data.EntityState.Modified;这句,这不扯淡么,我添加的时候也没有要通知EF要Add呀,下面是代码。最后在删除的时候也加上context.Entry(product).State = System.Data.EntityState.Deleted;。
例子是书里的一个SportsStore实例,更新的方法在AdminController里
代码:
[HttpPost]
public ActionResult Edit(Product product,HttpPostedFileBase image)
{
if (ModelState.IsValid)
{
if (image != null)
{
product.ImageMimeType = image.ContentType;
product.ImageData = new byte[image.ContentLength];
image.InputStream.Read(product.ImageData, 0, image.ContentLength);
}
try
{
repository.SaveProduct(product);
}
catch (Exception ex)
{
throw ex;
}
TempData["message"] = string.Format("{0} has been saved", product.Name);
return RedirectToAction("Index");
}
else
{
return View(product);
}
}
这段代码没有问题,有问题的是下面这段代码:
public class EFProductRepository : IProductsRespository { private EFDbContext context = new EFDbContext(); public IQueryable<Product> Products { get { return context.Products; } } public void SaveProduct(Product product) { if (product.ProductID == 0) { context.Products.Add(product);
context.Entry(product).State = System.Data.EntityState.Added; } //else // { // context.Entry(product).State = System.Data.EntityState.Modified; // } context.SaveChanges(); } #region IProductsRespository 成员 public void DeleteProduct(Product product) { context.Products.Remove(product); context.SaveChanges(); } #endregion }
书中的代码是没有注释的那几行的,因为Product没有改变,数据库因此没有更新。必须去掉注释行,告诉context,Product的内容已经发生改变,才能正常地更新到数据库里。
2.这里讲到登陆
作者为了偷懒,没有将账号密码存放到数据库中,而是在web.config里面添加了以下节点,意思是暂时用admin这个账号应付一下,结果使用bool result = FormsAuthentication.Authenticate(username, password);这句来验证的时候老是验证失败,擦!书上命名就是这么写的,后来去官网看看FormsAuthentication.Authenticate这个方法,看到人家添加这个节点的时候再credentials这里加了一个passwordFormat="SHA1",是加密方式,我这里没有加密,难道需要声明一下,于是照葫芦画瓢加了一个<credentials passwordFormat="Clear">就是不加密的意思吧,结果果然可以验证通过,各位屌丝程序员有幸看到这里不妨模仿一下,没办法,谁叫我是小白呢。
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" >
<credentials>
<user name="admin" password="secret"/>
</credentials>
</forms>
</authentication>
3.书中讲到给Product添加两个字段的时候要更新SportsStore.edmx这个文件,这不扯淡么 , 我向上全文搜索没有什么地方提到这个文件,后来在http://www.cnblogs.com/r01cn/archive/2012/06/20/2555348.html这篇博客中看到博友说道下面的话,恍然大悟,这书的作者大概糊涂了,又或者不是同一个人写的。
注:本小节是多余的,在SportsStore应用程序中不需要做这部分工作。而且,如果做了,会出现错误 — 译者注
4.上传图片
这里想上传一个图片但是作者定义数据类型public byte ImageData { get; set; },先不说在数据库里面存文件怎么样,但是这样一个byte存不下一张图片吧,应该是
public byte[] ImageData { get; set; }。到返回产品数据的时候就麻烦了因为不能直接从二进制数组中读出图片来需要专门写一个获取图片的方法,返回类型是FileContentResult,这是不是找抽呢,返回的产品过多时会很慢的呢,所以说叫教材就是教材,要照这上面的方法做项目,肯定是做不成了。
public FileContentResult GetImage(int productId)
{
Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId);
if (prod != null)
{
return File(prod.ImageData, prod.ImageMimeType);
}
else
{
return null;
}
}
5.我擦!
又遇到这个问题了The model backing the 'EFDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).这个需要删除数据库重新建。
网上说这个需要在Globa.asax.cs中的Application_Start()方法下添加Database.SetInitializer<EFDbContext>(null);这个方法,无奈加进去之后提示找不到Database这个类,还需要在SportsStore.WebUI这个项目中添加EntityFramework,但是我使用Nugget工具添加后显示版本不对,得知SportsStore.Domain这个里面已经添加了EntityFramework但是版本是4.0的,没办法还是通过Nugget将已有的都删掉,然后再下载最新的,在SportsStore.WebUI,SportsStore.Domain两个项目中都执行这个操作,最后搞定了,有点蛋疼啊。
最后来看看这个方法的原型:
//
// 摘要:
// Sets the database initializer to use for the given context type. The database
// initializer is called when a the given System.Data.Entity.DbContext type
// is used to access a database for the first time. The default strategy for
// Code First contexts is an instance of System.Data.Entity.CreateDatabaseIfNotExists<TContext>.
//
// 参数:
// strategy:
// The initializer to use, or null to disable initialization for the given context
// type.
//
// 类型参数:
// TContext:
// The type of the context.
public static void SetInitializer<TContext>(IDatabaseInitializer<TContext> strategy) where TContext : DbContext;
参数为null的时候不再实例化上下文。可以这么理解么?
6.路由
一定要在Global.asax里面定义一个路由,要不然整个MVC程序都不知道如何找到控制器,方法进而访问页面。mvc中会有多个路由模型,请求的时候用url去匹配,第一个符合的就返回。
我们可以给一个路由默认值,默认的controller和action,记住这两个单词是不能错的,如下。
routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Home", action = "Index" });
还有一个很有意思的地方
routes.MapRoute("", "X{controller}/{action}");
routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Home", action = "Index" });
routes.MapRoute("MyRoute1", "Public/{controller}/{action}");
假设有上面三个路由模式,只要有一个路由模式提供了默认值其他的模式都可以访问
http://localhost/UrlsAndRoutes可以访问,因为第二个提供了默认值,http://localhost/UrlsAndRoutes/Public/Home/Index可以,http://localhost/UrlsAndRoutes/XHome/Index这个也可以,因为第二个路由模式已经提供了默认值。
7.传参
这个是我一直比较好奇的一个东西,mvc里面没有request.querystring,request.form这些东西,怎么接受参数了,看到这里有一个使用路由模式定义参数的方法,有点不灵活。
routes.MapRoute("MyRoute", "{controller}/{action}/{name}/{id}", new { controller = "Home", action = "Index"});比如这个路由模式定义了两个参数name和id注意这里参数不再是?key1=value1&key2=value2... 这样的方式了,而是使用/来间隔,注意不能使用controller,action,area这样的关键字来定义参数名字,很显然他们已经有特别的意义了啊。
http://localhost/UrlsAndRoutes/home/index/asdfas/asdfas这里我传了两个参数,在home控制器里面可以使用RouteData.Values[""]这样的变量来接受参数,这个倒是和request.form很相像。如下:
public class HomeController : Controller { public ActionResult Index() { ViewBag.name = RouteData.Values["name"]; ViewBag.id = RouteData.Values["id"]; return View(); } }
在视图里面可以直接使用ViewBag.name和ViewBag.id这样的变量来进一步接受数据,这里还用接受数据这个称呼,在我看来最终要把这个数据展示出来,都是接受数据,最后在视图里面展示,
@{ ViewBag.Title = "Home Page"; } <h2>@ViewBag.name @ViewBag.id</h2> <p> </p>
然后我们在页面就可以看到这两个变量了。这个过程倒是可以理解。
有个疑问呢,如果第一个name不想传递该怎么办了,?? 这不是很傻比么,有人会说给自定义值,就在那个路由模式里面给,是个好方法,但是每次都去改这个路由模式么?UrlParameter.Optional这个选项给出了答案,在定义默认值的时候可以使用这个选项来告诉mvc这个参数可以不传的哦。
这里又讲到直接把参数传递给action方法,只要action方法中的参数名字和路由模式里面定义的参数名字一致就可以。例如路由里面定义第一个参数叫namea
routes.MapRoute("MyRoutea", "{controller}/{action}/{namea}/{id}");
只有参数名字不一样,为了和上面的区分所以我又定义了一个action
public ActionResult Indexa(string namea, string id) { //ViewBag.name = RouteData.Values["name"]; //ViewBag.id = RouteData.Values["id"]; ViewBag.name = namea; ViewBag.id = id; return View(); }
这回参数名字用namea,访问路径http://localhost/UrlsAndRoutes/home/Indexa/sssss/aaaa得到结果如下:
这句解释了原理:
The MVC Framework compares the list of segment variables with the list of action method parameters, and if the names match, passes the values from the URL to the method.
8.传参,碎碎念
如果传递的参数过多的话是不是会很烦人呢,路由中可以定义任意长度参数,简单点说就是把多个参数放在一个字符串里,得到之后自己再分割。如此种种我们定义了一个新的路由模式routes.MapRoute("", "x{controller}/{action}/{id}/{*catchall}", new { id = "DefaultId" });,这里使用了静态片段x,然后action方法定义了
public ActionResult Index(string name, string id, string catchall) { //ViewBag.name = RouteData.Values["name"]; //ViewBag.id = RouteData.Values["id"]; ViewBag.name = name; ViewBag.id = id; ViewBag.cactchall = catchall; return View(); }
作者:Tyler Ning
出处:http://www.cnblogs.com/tylerdonet/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过以下邮箱地址williamningdong@gmail.com 联系我,非常感谢。