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);