如何自定义将列表转换为不同的类型?

Posted

技术标签:

【中文标题】如何自定义将列表转换为不同的类型?【英文标题】:How to custom cast a list to a different type? 【发布时间】:2020-01-17 12:20:41 【问题描述】:

使用实体框架和 Razor 页面,我目前有一个系统可以将我的模型包装在视图模型中并将它们显示到屏幕上,我认为这是我应该在 MVVM 中执行的操作架构。

但是,我在将“Foo”类型的列表转换为“FooView”时遇到了一些问题,但是将“Foo”类型的变量转换为“FooView”是可以的。代码如下:

学生班

public class Student

    public Student()
    
        Id = null;
        FirstName = "";
        Surname = "";
        Age = 0;
        DateOfBirth = new DateTime();
        Results = new HashSet<Result>();
    

    public Student(string firstName, string surname, DateTime dateOfBirth)
    
        Id = null;
        FirstName = firstName;
        Surname = surname;
        DateOfBirth = dateOfBirth;
        Results = new HashSet<Result>();
    

    public int? Id  get; set; 

    public string FirstName  get; set; 

    public string Surname  get; set; 

    public int Age  get; private set; 

    // Implement backing field
    private DateTime _dateOfBirth;
    public DateTime DateOfBirth
    
        get => _dateOfBirth;

        set
        
            // Set Age when date of birth is provided
            _dateOfBirth = value;
            Age = (DateTime.Today.Year - this._dateOfBirth.Year);
            if (_dateOfBirth.Date > DateTime.Today.AddYears(-this.Age)) this.Age--;
        
    

    public HashSet<Result> Results  get; set; 

    public static implicit operator StudentView(Student student)
    
        return new StudentView(student);
    


成功将学生项目投射到剃刀页面上的学生查看项目 WORKS

@foreach (var item in Model.Result)

    <tr>
        <td>
            @html.DisplayFor(modelItem => ((StudentView)item.Student).FullName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Score)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Grade)
        </td>
        <td>
            <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
            <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
            <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
        </td>
    </tr>

尝试投射列表以键入“学生视图”失败

public async Task OnGetAsync()

    Student = await _context.Student.Cast<StudentView>().ToListAsync();

InvalidCastException:无法转换类型的对象 'StudentManagerDemoCore.Models.Student' 输入 'StudentManagerDemoCore.ViewModels.StudentView'。

StudentManagerDemoCore.Pages.Teacher.ManageStudent.IndexModel.OnGetAsync() 在Index.cshtml.cs

Student = await _context.Student.Cast<StudentView>().ToListAsync();

我有一种感觉,我在这里缺少一些东西,但我不确定它是什么。该错误表明我的转换方法存在问题,但这并不能解释为什么它有时会起作用。

有人知道我哪里出错了吗?

【问题讨论】:

看看这个项目:automapper.org 请不要将异常发布为截图。将它们作为文本发布。 @dymanoid 嗨,我很乐意,但不确定发布它们的格式。否则我认为它会占用很大一部分问题。 您可以剪掉不相关的堆栈帧以缩短堆栈跟踪。但一般情况下,发布大的异常文本是没有问题的。 发现了以下密切相关的内容:codeproject.com/Articles/1043977/… 【参考方案1】:

您有多种选择来解决您的问题。

第一个解决方案是手动执行以下操作:

await _context.Student.Select(student => new StudentView()

    Id = student.Id,
    //....

另一种解决方案是使用手动映射器来执行映射。一个静态函数,它接受 Student 并将其转换为 StudentView 并返回它。您可以在其他地方重复使用它。

我建议的最佳解决方案是使用AutoMapper。

【讨论】:

嗨,第一个解决方案确实有效,但我有一个构造函数,它接受一个“学生”类型的参数,所以它也可以工作。虽然它确实有效,但我尝试使用“Student”类中的转换方法来实现它,因此我在整个项目中拥有一致的 Student 和 StudentView 转换方式,而不是有时使用构造函数。 另外,AutoMapper 项目看起来很有趣,谢谢你让我知道。【参考方案2】:

来自MSDN(我的重点):

此方法的源序列是 IEnumerable,这意味着元素具有编译时静态类型的对象。 此方法执行的唯一类型转换是引用转换和拆箱转换。集合中元素的运行时类型必须与目标类型匹配,或者在值类型的情况下,元素的运行时类型必须是目标类型的装箱转换的结果。不允许其他类型的转换,例如不同数值类型之间的转换。

因此,对于您的具体情况,当您定义 StudentStudentView 之间的隐式转换时,请改为执行以下操作:

public async Task OnGetAsync()

    Student = await _context.Student.Select(x=>(StudentView)x).ToListAsync();

【讨论】:

并非如此。模型类和视图模型类通常是两个完全不相关的类,它们之间没有简单的转换。用Cast(T) 投射都没有关系,如果一个不起作用,另一个也不起作用。 @WiktorZychla OP Student 类定义了StudentStudentView 之间的隐式转换,因此(T) 有效。但由于Cast只做拆箱和引用转换,它不调用隐式转换。 我的错,接线员确实在sn-p下面。 嗨,我已经尝试过这个解决方案,因为如果我选择你描述的方式(应该提到我在 OP 中尝试过)但遇到另一个错误:(在“StudentView”和“Student”类型之间没有定义强制运算符。)这很奇怪。下面的方法确实有效 (Select(x=> new StudentView(x))) 这与转换方法完全相同。 我更好奇为什么演员阵容会出现摇摆不定,因为我认为我已经正确定义了所有内容,并且将学生列表投射到学生视图列表似乎更直观,而不是从学生列表中创建学生视图列表,但我想就是这样。目前我只有一种方式,Student > StudentView,我可能需要另一种方式吗?【参考方案3】:

我还通过在“StudentView”类中实现从“StudentView”到“Student”的转换,然后使用磁控管的解决方案来解决了这个问题。

所以我在“视图”类中实现了转换。为什么需要这样做我不知道,考虑到将“Student”转换为“StudentView”不需要这种转换,这对我来说似乎很神奇。也许某处写了一些东西,出于某种原因需要双向转换。

public class StudentView : ViewBase<Student>

    // Removed for brevity

    public static implicit operator Student(StudentView student)
    
        return new Student(student);
    


然后更新要执行的铸造,如磁控管突出显示的那样:

    public async Task OnGetAsync()
    
        Student = await _context.Student.Select(x => (StudentView)x).ToListAsync();
    

我还想强调一下billybob 的回答,因为他提供了另一种解决解决方案的正确方法,我只是选择了另一种方法。

【讨论】:

以上是关于如何自定义将列表转换为不同的类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何将自定义类型转换为原始类型?

如何防止 gorm 将自定义整数类型转换为字符串?

将表转换为自定义类型数组

添加到自定义类中的列表,然后将其转换为 json String

自定义类型转换器converter

如何将自定义 json 转换为自适应卡片 json 格式