实体框架主动加载-传递数据到视图模型

本文关键字:数据 视图 模型 框架 加载 实体 | 更新日期: 2023-09-27 18:06:46

. NET MVC核心应用程序,从一个动作方法如下所示,我传递博客数据及其相关数据从Posts表到视图作为return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());因为我从两个表传递数据,我需要使用如下所示的ViewModel。问题:我如何使用ViewModel从我的控制器动作方法传递相关数据Test()查看下图?

在下面的代码中,我得到了明显的错误:

InvalidOperationException:传入ViewDataDictionary的模型项的类型是'System.Collections.Generic.List'1[asp_core_logs . models . error]。,但是这个ViewDataDictionary实例需要一个类型为' system . collections . general . 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; }
}
<<p> 视图/strong>:
@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类来投影您的查询:

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

用于将数据作为ViewModel从Action传递到view。首先创建一个视图模型的新实例,并通过调用上下文查询(无论您的Linq查询是什么)为每个属性赋值,并返回视图列表作为视图模型变量。

var blogWithRelatedPost = new BolblogWithRelatedPost();
// your logic here for assigning value to property or LINQ query
return View(blogWithRelatedPost);