Entity Framework 预先加载 - 将数据传递给 ViewModel
Entity Framework Eager Loading - pass data to ViewModel
在我的 ASP.NET MVC Core 应用程序中,通过如下所示的操作方法,我将博客数据及其相关数据从帖子 table 传递到视图 return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
因为我从两个 table 传递数据,我需要使用如下所示的 ViewModel。 问题:如何使用 ViewModel 从我的 Controller Action 方法传递相关数据
Test()
要查看下图?
在下面的代码中,我得到了明显的 error:
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List'1[ASP_Core_Blogs.Models.Blog]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IList'1[ASP_Core_Blogs.Models.BlogPostViewModels.BlogsWithRelatedPostsViewModel]'.
型号:
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{ }
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int PostYear { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
控制器:
[HttpGet]
public async Task<IActionResult> Test(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
}
ViewModel:
public class BlogsWithRelatedPostsViewModel
{
public int BlogID { get; set; }
public int PostID { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int PostYear { get; set; }
}
查看:
@model IList<ASP_Core_Blogs.Models.BlogPostViewModels.BlogsWithRelatedPostsViewModel>
<div class="row">
<div class="col-md-12">
<form asp-controller="DbRelated" asp-action="EnterGrantNumbers" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
<table class="table">
<thead>
<tr>
<th></th>
<th></th>
<th>Url</th>
<th>Title</th>
<th>Content</th>
</tr>
</thead>
<tbody>
@for (int t = 0; t < Model.Count; t++)
{
<tr>
<td><input type="hidden" asp-for="@Model[t].BlogID" /></td>
<td><input type="hidden" asp-for="@Model[t].PostID" /></td>
<td>
<input type="text" asp-for="@Model[t].Url" style="border:0;" readonly /> <!--Not using /*Html.DisplayFor(modelItem => Model[t].Url)*/ since it does not submit stateName on Post. Not using <label asp-for=.....> since Bootstrap bold the text of <label> tag-->
</td>
<td>
<input asp-for="@Model[t].Title" />
</td>
<td>
<input asp-for="@Model[t].Content" />
</td>
</tr>
}
</tbody>
</table>
<button type="submit" class="btn btn-default">Save</button>
</form>
</div>
</div>
您需要使用 BlogsWithRelatedPostsViewModel
class:
来预测您的查询
return View( _context.Blogs
.Include(p => p.Posts)
.SelectMany(e=> e.Posts.Select(p=> new BlogsWithRelatedPostsViewModel
{
BlogId= e.BlogId,
PostId=p.PostId,
Url=e.Url,
...
})
.ToList());
SelectMany
扩展方法允许您将 e.Posts
中的每个投影展平为一个序列,因此最后您将得到一个 List<BlogsWithRelatedPostsViewModel>
除了 Octavioccl 的答案之外,还有一个我一直在使用的不错的小扩展方法(我不知道作者是谁,但如果其他人知道,我会很乐意更新我的答案以表彰)。这样,您就不必写出每个 属性.
public static T Cast<T>(this object myobj)
{
var target = typeof(T);
var x = Activator.CreateInstance(target, false);
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var memberInfos = d as MemberInfo[] ?? d.ToArray();
var members = memberInfos.Where(memberInfo => memberInfos.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
foreach (var memberInfo in members)
{
var propertyInfo = typeof(T).GetProperty(memberInfo.Name);
if (myobj.GetType().GetProperty(memberInfo.Name) == null) continue;
var value = myobj.GetType().GetProperty(memberInfo.Name).GetValue(myobj, null);
propertyInfo.SetValue(x, value, null);
}
return (T)x;
}
用法:
var ViewModelList = ModelList.Select(model => model.Cast<ViewModel>()).ToList();
还有一个针对此特定问题构建的支持良好的框架。已调用 AutoMapper (http://automapper.org/)。
用于将数据从 Action 传递到作为 ViewModel 的视图。首先创建视图模型的新实例,然后通过调用上下文查询(无论您的 Linq 查询是什么)和 return 视图列表作为视图模型变量为每个 属性 分配值。
var blogWithRelatedPost = new BolblogWithRelatedPost();
// your logic here for assigning value to property or LINQ query
return View(blogWithRelatedPost);
在我的 ASP.NET MVC Core 应用程序中,通过如下所示的操作方法,我将博客数据及其相关数据从帖子 table 传递到视图 return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
因为我从两个 table 传递数据,我需要使用如下所示的 ViewModel。 问题:如何使用 ViewModel 从我的 Controller Action 方法传递相关数据
Test()
要查看下图?
在下面的代码中,我得到了明显的 error:
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List'1[ASP_Core_Blogs.Models.Blog]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IList'1[ASP_Core_Blogs.Models.BlogPostViewModels.BlogsWithRelatedPostsViewModel]'.
型号:
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{ }
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int PostYear { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
控制器:
[HttpGet]
public async Task<IActionResult> Test(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
}
ViewModel:
public class BlogsWithRelatedPostsViewModel
{
public int BlogID { get; set; }
public int PostID { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int PostYear { get; set; }
}
查看:
@model IList<ASP_Core_Blogs.Models.BlogPostViewModels.BlogsWithRelatedPostsViewModel>
<div class="row">
<div class="col-md-12">
<form asp-controller="DbRelated" asp-action="EnterGrantNumbers" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
<table class="table">
<thead>
<tr>
<th></th>
<th></th>
<th>Url</th>
<th>Title</th>
<th>Content</th>
</tr>
</thead>
<tbody>
@for (int t = 0; t < Model.Count; t++)
{
<tr>
<td><input type="hidden" asp-for="@Model[t].BlogID" /></td>
<td><input type="hidden" asp-for="@Model[t].PostID" /></td>
<td>
<input type="text" asp-for="@Model[t].Url" style="border:0;" readonly /> <!--Not using /*Html.DisplayFor(modelItem => Model[t].Url)*/ since it does not submit stateName on Post. Not using <label asp-for=.....> since Bootstrap bold the text of <label> tag-->
</td>
<td>
<input asp-for="@Model[t].Title" />
</td>
<td>
<input asp-for="@Model[t].Content" />
</td>
</tr>
}
</tbody>
</table>
<button type="submit" class="btn btn-default">Save</button>
</form>
</div>
</div>
您需要使用 BlogsWithRelatedPostsViewModel
class:
return View( _context.Blogs
.Include(p => p.Posts)
.SelectMany(e=> e.Posts.Select(p=> new BlogsWithRelatedPostsViewModel
{
BlogId= e.BlogId,
PostId=p.PostId,
Url=e.Url,
...
})
.ToList());
SelectMany
扩展方法允许您将 e.Posts
中的每个投影展平为一个序列,因此最后您将得到一个 List<BlogsWithRelatedPostsViewModel>
除了 Octavioccl 的答案之外,还有一个我一直在使用的不错的小扩展方法(我不知道作者是谁,但如果其他人知道,我会很乐意更新我的答案以表彰)。这样,您就不必写出每个 属性.
public static T Cast<T>(this object myobj)
{
var target = typeof(T);
var x = Activator.CreateInstance(target, false);
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var memberInfos = d as MemberInfo[] ?? d.ToArray();
var members = memberInfos.Where(memberInfo => memberInfos.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
foreach (var memberInfo in members)
{
var propertyInfo = typeof(T).GetProperty(memberInfo.Name);
if (myobj.GetType().GetProperty(memberInfo.Name) == null) continue;
var value = myobj.GetType().GetProperty(memberInfo.Name).GetValue(myobj, null);
propertyInfo.SetValue(x, value, null);
}
return (T)x;
}
用法:
var ViewModelList = ModelList.Select(model => model.Cast<ViewModel>()).ToList();
还有一个针对此特定问题构建的支持良好的框架。已调用 AutoMapper (http://automapper.org/)。
用于将数据从 Action 传递到作为 ViewModel 的视图。首先创建视图模型的新实例,然后通过调用上下文查询(无论您的 Linq 查询是什么)和 return 视图列表作为视图模型变量为每个 属性 分配值。
var blogWithRelatedPost = new BolblogWithRelatedPost();
// your logic here for assigning value to property or LINQ query
return View(blogWithRelatedPost);