#asp.net #asp.net-mvc-3 #entity-framework-4 #ef-code-first #sql-server-express
#asp.net #asp.net-mvc-3 #entity-framework-4 #ef-code-first #sql-server-express
Вопрос:
Итак, я перешел на MVC3, и в целом я считаю, что это здорово; Мне тоже очень нравится подход codefirst.
Сегодня я столкнулся с некоторыми проблемами, связанными с этим, и, хотя в MVC2 я смог добавлять записи и редактировать их, теперь я не могу этого сделать.
Я знаю, что это неопределенное начало, поэтому позвольте мне изменить.
Вот пример одной из моих моделей
namespace ESF_ResourceManager.Models
{
public class FileList
{
[Key]
public int FileListID { get; set; }
[DisplayName("File title: ")]
[Required(ErrorMessage = "Please set a unique title for the file")]
// TODO: Need to add remote validation - must be unique
public string FileTitle { get; set; }
[DisplayName("Choose a File")]
[FileExtensions(Extensions = "txt, zip, pdf, ppt, xls, doc, docx, xlsx, pptx", ErrorMessage = "Please choose a valid file of type txt, zip, pdf, ppt, xls, doc, docx, xlsx or pptx")]
public HttpPostedFileBase FileUrl { get; set; }
}
}
Затем соответствующий контроллер:
namespace ESF_ResourceManager.Controllers
{
public class FileListController : ResourceManagerController
{
//
// GET: /FileList/
public ActionResult Index()
{
var fileList = from fl in DBContext.FileLists
where fl.FileListID > 0
select fl;
return View(fileList.ToList());
}
//
// GET: /FileList/Create
[HttpGet]
public ActionResult Create()
{
return View();
}
//
// POST: /FileList/Create
[HttpPost]
public ActionResult Create(FileList fileDetail)
{
if (ModelState.IsValid)
{
// test the file - size only - the file type should have been checked via Extensions as par tof the model definition
if (fileDetail.FileUrl.ContentLength > 0 amp;amp; fileDetail.FileUrl.ContentLength < 1048576)
{
string fileName = Path.GetFileName(fileDetail.FileUrl.FileName);
string path = Path.Combine(Server.MapPath("~/App_Data/uploads/documents"), fileName);
fileDetail.FileUrl.SaveAs(path);
DBContext.FileLists.Add(fileDetail);
DBContext.SaveChanges();
return RedirectToAction("Index");
}
}
return View(fileDetail);
}
//
// GET: /FileList/Edit/5
[HttpGet]
public ActionResult Edit(int id)
{
var fileDetail = from fl in DBContext.FileLists
where fl.FileListID == id
select fl;
return View(fileDetail.Single());
}
//
// POST: /FileList/Edit/5
[HttpPost]
public ActionResult Edit(int id, FileList fileDetail)
{
if (ModelState.IsValid)
{
var fileEdited = DBContext.FileLists.Find(id);
UpdateModel(fileEdited);
DBContext.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
//
// GET: /FileList/Delete/5
public ActionResult Delete(int id)
{
var fileDetail = from fl in DBContext.FileLists
where fl.FileListID == id
select fl;
return View(fileDetail.Single());
}
//
// POST: /FileList/Delete/5
[HttpPost]
public ActionResult Delete(int id, FileList fileDetail)
{
try
{
DBContext.FileLists.Remove(DBContext.FileLists.Find(id));
DBContext.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
}
Вид, который является Razor (для создания):
@model ESF_ResourceManager.Models.FileList
@{
ViewBag.Title = "File List";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm("Create", "FileList", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.ValidationSummary(true)
<fieldset>
<legend>File List</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FileTitle)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FileTitle)
@Html.ValidationMessageFor(model => model.FileTitle)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.FileUrl)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.FileUrl, new { type = "file" })
@Html.ValidationMessageFor(model => model.FileUrl)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
So, I can load the view and see the form to enter the data but when I click the Create button I get the following message:
Value cannot be null. Parameter name: key
I have looked at the object in the debugger and nothing on the object coming into the create post function is null. the key is 0 and I would have expected (possibly wrongly) for this to be 1.
So my first question is what have I missed here? What do I need to do to get this to work properly for me?
The second question is more general — where is my data? The tutorials I have read on this suggest that ther eis either a SQLExpress database created some place or a SqlCE in the App_Data. Neither of which I can find so I’m confused as to where this is.
Any help on this would be greatly appreciated.
Many thanks
nathj07
EDIT
Thanks for stopping by, I’m still learning what would be helpful to include when asking questions. So, here are the requested items:
Data Context
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace ESF_ResourceManager.Models
{
public class ResourceManagerContext : DbContext
{
public DbSet<Resource> Resources { get; set; }
public DbSet<ResourceType> ResourceTypes { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<FileList> FileLists { get; set; }
}
}
Web.Config
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=152368
-->
<configuration>
<appSettings>
<add key="webpages:Version" value="1.0.0.0"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages"/>
</namespaces>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
И, наконец, —
Views/Web.config
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.web>
<httpHandlers>
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
<!--
Enabling request validation in view pages would cause validation to occur
after the input has already been processed by the controller. By default
MVC performs request validation before a controller processes the input.
To change this behavior apply the ValidateInputAttribute to a
controller or action.
-->
<pages
validateRequest="false"
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<controls>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
</controls>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
</configuration>
Если есть что-то еще, что вам нужно увидеть, я с радостью опубликую это.
Спасибо nathj07
Редактировать 2 Теперь я попытался вручную добавить базу данных SQLExpress и обновить строку подключения в файле web.config.
<connectionStrings>
<remove name="LocalSqlServer"/>
<add name="LocalSqlServer" connectionString="Data Source=.SQLExpress;Integrated Security=True;AttachDBFilename=|DataDirectory|DB_ESF_ResourceManager.mdf;User Instance=true" />
</connectionStrings>
Это оставляет меня с той же ошибкой — никакой разницы вообще. Я в полной растерянности и понятия не имею, что с этим делать дальше. Есть ли еще какие-либо предложения по этому поводу?
Спасибо nathj07
Комментарии:
1. нам могут понадобиться еще две вещи: 1) ваш класс databasecontext и ваш web.config, если вы явно указали строку подключения
2. В чем проблема сейчас? Исключение, о котором вы упомянули выше (я только что выделил сообщение)? Или вы не находите или не можете создать базу данных?
3. Хорошо, проблема не немного отличается. В принципе, я попытался воссоздать эту работу. В проекте было 4 модели — типы ресурсов, ресурс, пользователи и список файлов. Воссоздав все панели в списке файлов, я заметил, что все работает просто отлично. Я добавляю модель списка файлов, как показано, и получаю сообщение об ошибке. На этот раз у меня нет строк подключения, и я предполагаю (плохо, что я знаю), что это создало БД в каком-то месте либо в SQLServerExpress, либо в CE. Теперь у меня осталось несколько других вопросов, но я опубликую их в новых темах. Спасибо за всю помощь. Я буду голосовать соответственно nathj07
4. Как вы можете «предположить», что база данных была создана? Разве вы не можете просто проверить, есть ли БД или нет? И если вы вообще не укажете какую-либо строку подключения, это будет SQL Server Express DB (см. Мой ответ ниже). База данных — это действительно первое, что вы должны найти, чтобы определить причину вашего исключения.
5. Привет, Слаума, я полностью согласен. Дело в том, что я не знаю, как найти базу данных. Sql management express не находит ничего, к чему можно подключиться. Есть идеи о том, как я могу это найти?
Ответ №1:
Похоже, что EF не помечает ваш ключ как столбец идентификатора. Entity Framework помечает свойство как первичный ключ, если тип равен int
short
или long
, а имя равно <classname>Id
. В вашей модели часть «ID» указана в верхнем регистре. У вас есть два возможных решения:
- Разместив указанное
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
выше ваше свойство, чтобы указать, что оно фактически является столбцом идентификатора - Переименование вашего свойства с
FileListID
наFileListId
. Вероятно, вам больше не понадобится[Key]
атрибут, если вы выберете это решение.
Если вы выберете первое решение, ваше свойство будет выглядеть следующим образом:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FileListID { get; set; }
Комментарии:
1. Первичное или внешнее обнаружение не чувствительно к регистру, поэтому оба
ID
иId
в порядке.2. Слаума, спасибо за разъяснение. Я думал, что это не чувствительно к регистру, поскольку у меня есть демонстрационное решение, работающее с идентификатором верхнего регистра.
3. Джесси, спасибо за совет по настройке аннотации [DatabaseGenerated], которая научила меня чему-то новому, и это здорово.
Ответ №2:
вам также нужно добавить FileListID для привязки mvc make
@Html.HiddenFor(model => model.FileListID)
Комментарии:
1. Привет, Фернандо, я пытался добавить видимое поле ранее, и это ничего не изменило. Я попробую скрытое поле, когда вернусь к работе. Спасибо nathj07
2. Еще раз привет, Фернандо — код был в dropbox, поэтому я попробовал его из дома, и это не сработало. У вас есть еще ответы? Я добавил еще немного кода к исходному вопросу. Еще раз спасибо за помощь.
Ответ №3:
Ваш второй вопрос:
Поскольку в вашем файле web.config нет строки подключения, Entity Framework 4.1 попытается создать новую базу данных в вашем экземпляре SQL Server Express, если база данных не существует. Имя этой базы данных — Namespace .DerivedContextName, поэтому в вашем случае это должно быть: ESF_ResourceManager.Модели.ResourceManagerContext.
Вы можете просмотреть базу данных с помощью SQL Server Management Studio или в Visual Studio через обозреватель сервера.
Ваш первый вопрос:
Возможная причина, по которой вы получаете ошибку, заключается в том, что столбец ключа в базе данных, соответствующий свойству ключа FileListID
в вашей модели, не помечен как столбец идентификатора, то есть как столбец, который автоматически генерирует свое собственное значение при вставке новой строки. Ваша конфигурация модели предполагает, что столбец является идентификатором (по умолчанию вы это не отключали). Как следствие, EF не будет отправлять значение for FileListID
в базу данных при вставке нового объекта, поскольку предполагается, что база данных создаст значение. Если столбец в базе данных не является идентификатором, он не создает значения, и вы получаете свое исключение.
Итак, вы должны проверить в БД, является ли FileListID
столбец идентификатором, и если нет, включите его.