在我的映射逻辑层(Model to ViewModel)中,我尝试在编辑视图中填充SelectListItem
以与HTML.DropDownListFor帮助器一起使用。
我尝试使用以下代码示例中的查询来检索品牌名称列表以填充SelectListItem,但触发了以下异常:
已经有一个与此命令关联的开放DataReader 必须先关闭。
public class MedicalProductMapper
{
private MvcMedicalStoreDb _db; // DataContext class
public MedicalProductMapper(MvcMedicalStoreDb db)
{
_db = db;
}
public MedicalProductViewModel GetMedicalProductViewModel(MedicalProduct source)
{
MedicalProductViewModel viewModel = new MedicalProductViewModel();
viewModel.ID = source.ID;
viewModel.Name = source.Name;
viewModel.Price = source.Price;
viewModel.BrandID = source.BrandID;
// This following line produces the exception
viewModel.BrandName = _db.Brands.Single(b => b.ID == source.BrandID).Name;
var queryBrands = from b in _db.Brands
select b;
viewModel.BrandSelectListItem = queryBrands as IEnumerable<SelectListItem>;
return viewModel;
}
}
我知道通过在连接字符串中启用多个活动结果集(MARS),可以轻松解决问题,但我想知道是否有办法在不修改连接字符串的情况下做我想做的事。
以下是一些课程,以防它们有助于解决这个问题:
@model MvcMedicalStore.Models.MedicalProductViewModel
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
@Html.HiddenFor(model => model.ID)
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price)
</div>
// BRAND NAME
<div class="editor-label">
@Html.LabelFor(model => model.BrandName)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.BrandName, Model.BrandSelectListItem)
@Html.ValidationMessageFor(model => model.BrandName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();
//
// GET: /MedicalSupply/
public ActionResult Index()
{
var viewModel = _db.Products.AsEnumerable()
.Select(product => GetMedicalProductViewModel(product));
return View(viewModel);
}
public MedicalProductViewModel GetMedicalProductViewModel(MedicalProduct product)
{
var mapper = new MedicalProductMapper(_db);
return mapper.GetMedicalProductViewModel(product);
}
public MedicalProduct GetMedicalProduct(MedicalProductViewModel viewModel)
{
var mapper = new MedicalProductMapper(_db);
return mapper.GetMedicalProduct(viewModel);
}
//
// GET: /MedicalSupply/Edit/5
public ActionResult Edit(int id = 0)
{
MedicalProduct medicalProduct = _db.Products.Find(id);
if (medicalProduct == null)
{
return HttpNotFound();
}
var viewModel = GetMedicalProductViewModel(medicalProduct);
return View(viewModel);
}
//
// POST: /MedicalSupply/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(MedicalProduct medicalProduct)
{
if (ModelState.IsValid)
{
_db.Entry(medicalProduct).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
var viewModel = GetMedicalProductViewModel(medicalProduct);
return View(viewModel);
}
}
[InvalidOperationException:已经有一个打开的DataReader 与此命令关联,必须先关闭。]
System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand的 命令)+5287423
System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(字符串 方法,SqlCommand命令)+20
System.Data.SqlClient.SqlCommand.ValidateCommand(String方法, 布尔异步)+155
System.Data.SqlClient.SqlCommand.RunExecuteReader(的CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,String 方法,TaskCompletionSource`1完成,Int32超时,任务&amp;任务, Boolean asyncWrite)+82
System.Data.SqlClient.SqlCommand.RunExecuteReader(的CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,String 方法)+53
System.Data.SqlClient.SqlCommand.ExecuteReader(的CommandBehavior 行为,字符串方法)+134
System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(的CommandBehavior 行为)+41
System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10 System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand) entityCommand,CommandBehavior行为)+437[EntityCommandExecutionException:执行时发生错误 命令定义。有关详细信息,请参阅内部异常。]
System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand,CommandBehavior行为)+507
System.Data.Objects.Internal.ObjectQueryExecutionPlan.Execute(ObjectContext的 context,ObjectParameterCollection parameterValues)+730
System.Data.Objects.ObjectQuery1.GetResults(Nullable
1 forMergeOption)+131
System.Data.Objects.ObjectQuery1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() +36 System.Linq.Enumerable.Single(IEnumerable
1 source)+179 System.Data.Objects.ELinq.ObjectQueryProvider.b_ 3(IEnumerable1 sequence) +41
1 查询,表达式queryRoot)+59
System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle(IEnumerable
System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute(表达式 表达式)+133
System.Data.Entity.Internal.Linq.DbQueryProvider.Execute(表达式 表达式)+123 System.Linq.Queryable.Single(IQueryable1 source, Expression
1谓词)+287
MvcMedicalStore.Mappers.MedicalProductMapper.GetMedicalProductViewModel(MedicalProduct 源代码)在c:\ Users \ Matt \ Documents \ Visual Studio中 2012 \项目\ MvcMedicalStore \ MvcMedicalStore \映射器\ MedicalProductMapper.cs:28 MvcMedicalStore.Controllers&LT;。&以及c _DisplayClass1.b_ 0(MedicalProduct 产品)在c:\ Users \ Matt \ Documents \ Visual Studio中 2012 \项目\ MvcMedicalStore \ MvcMedicalStore \ \控制器HomeController.cs:28 System.Linq.WhereSelectEnumerableIterator2.MoveNext() +145
1续)+388
ASP._Page_Views_Home_Index_cshtml.Execute() in c:\Users\Matt\Documents\Visual Studio 2012\Projects\MvcMedicalStore\MvcMedicalStore\Views\Home\Index.cshtml:25 System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +197
System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +119
System.Web.WebPages.StartPage.RunPage() +17
System.Web.WebPages.StartPage.ExecutePageHierarchy() +62
System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +76
System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +743
System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +382
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +431 System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +39
System.Web.Mvc.<>c__DisplayClass1a.<InvokeActionResultWithFilters>b__17() +74 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func
System.Web.Mvc&LT;。&以及c _DisplayClass1c.b_ 19() +72 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext,IList1 filters, ActionResult actionResult) +303
1.End() +136 System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult,Object tag)+56
System.Web.Mvc.Async.<>c__DisplayClass2a.<BeginInvokeAction>b__20() +155 System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult) +184 System.Web.Mvc.Async.WrappedAsyncResult
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult的 asyncResult)+40
System.Web.Mvc&LT;。&以及c _DisplayClass1d.b_ 18(IAsyncResult的 asyncResult)+40
System.Web.Mvc.Async&LT;。&以及c _DisplayClass4.b_ 3(IAsyncResult的 ar)+47 System.Web.Mvc.Async.WrappedAsyncResult1.End() +151
1.End()+151
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +44 System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +47 System.Web.Mvc.Async.WrappedAsyncResult
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, 对象标签)+59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, 对象标签)+40 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult)+39
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult的 asyncResult)+39
System.Web.Mvc&LT;。&以及c _DisplayClass8.b_ 3(IAsyncResult的 asyncResult)+45
System.Web.Mvc.Async&LT;。&以及c _DisplayClass4.b__3(IAsyncResult的 ar)+47 System.Web.Mvc.Async.WrappedAsyncResult`1.End()+151
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, 对象标签)+59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, 对象标签)+40
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +40 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult) 结果)+38
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9628700 System.Web.HttpApplication.ExecuteStep(IExecutionStep step,Boolean&amp; completedSynchronously)+155
答案 0 :(得分:5)
您在每个产品的选择中提出另一个请求。但是您的产品被枚举,因此第一个datareader没有关闭。这就是您打开多个数据加载器的原因。
public ActionResult Index()
{
var products = _db.Products.ToArray() // force loading the results from database
// and close the datareader
var viewModel = products.Select(product => GetMedicalProductViewModel(product));
return View(viewModel);
}
附加:我认为您应该优化模型创建:您为数据库中的每个产品提出相同的请求(选择品牌)。
为避免不必要的多个数据库往返,您应该:
答案 1 :(得分:2)
编辑:正如您已经知道多个结果集标志,正如评论中指出的那样,我认为我将此答案更改为更有用的答案。
可以解决您的问题的东西,也是从您不打算编辑的上下文中获取数据的一个非常好的做法,是明确告诉EntityFramework不跟踪实体,在上下文中有效地呈现它们永远不会更新回数据库的只读对象。
这很容易做到:只需使用&#39; AsNoTracking()&#39;。你基本上只需要:
var brands = _db.Brands.AsNoTracking().ToList();
现在,您可以使用此列表将其设置为产品视图模型上的查找,您也可以使用它来获取该特定产品的viewmodel的brandName。只需使用以下品牌列表扩展您的GetMedicalProductViewModel:
GetMedicalProductViewModel(MedicalProduct source, IEnumerable<Brand> brands)
然后使用品牌而不是你的_db.Brands,你会很高兴:
var brands = _db.Brands.AsNoTracking().ToList();
var viewModel = _db.Products.AsNoTracking().Select(product => GetMedicalProductViewModel(product, brands));
return View(viewModel);
此外,请注意您使用相同的viewmodel进行编辑和列表。在这种情况下,您可以看到效率非常低,因为索引页面上的每个产品都有自己的品牌列表副本,这最终会成为索引视图中的大量额外数据,而您不会实际需要。因此,我强烈建议使用没有BrandSelectListItem的MedicalProductIndexViewModel(并且它本身可能应该复制到Items)。
这真的可以产生很大的不同 - 如果有10个品牌,那么如果您的页面大小为50,那么500个键值对,这可能接近产品数据的10倍指数真的需要。如果有100个品牌......你就能看到它。
如果您没有在ProductIndex中使用BrandName,您也可以省略它,并使其更高效,因为也可以跳过该部分查询。
此外,我没有使用GetMedicalProductViewModel,而是通常只提供我的viewmodel构造函数参数并从那里填充。
最后,认为像品牌查询也可以使用Ajax调用按需填充,这通常也更有效,因为它可以加载,而页面已经存在供用户开始使用,可以使用异步在键入品牌名称等时搜索