如何将列表自定义转换为其他类型?



使用实体框架和 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();
}
无效强制转换

异常:无法强制转换类型的对象 "StudentManagerDemoCore.Models.Student"键入 'StudentManagerDemoCore.ViewModels.StudentView'.

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

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

我有一种感觉,我在这里缺少一些东西,但我不确定它是什么。该错误意味着我的铸造方法存在问题,但这并不能解释为什么它有时有效。

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

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

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

await _context.Student.Select(student => new StudentView()
{
Id = student.Id,
//....
}

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

我建议的最佳解决方案是使用自动映射器。

来自 MSDN(我的强调(:

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

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

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

我还通过在"学生视图"类中实现从"学生视图"到"学生"的转换,然后使用磁控管的解决方案来解决这个问题。

所以我在"视图"类中实现了转换。我不知道为什么需要这样做,考虑到这种转换对于将"学生"转换为"学生视图"不是必需的,这对我来说似乎很神奇。也许某处写了一些东西,由于某种原因需要双向投射。

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 的答案,因为他提供了另一种正确的解决方案方法,我只是选择了不同的方法。

最新更新