gpt4 book ai didi

c# - 我如何在 MVC 中跨我的 View 模型和服务使用基类?

转载 作者:太空宇宙 更新时间:2023-11-03 12:06:02 26 4
gpt4 key购买 nike

背景

在 Asp.Net Core 中使用 MVC,我正在使用 Controller -> 服务 -> View 模型 -> View 方法。

我想要 2 个服务共享一些基础数据和功能。

我的要求是一个实体由 2 种不同的用户类型共享。 IE。管理员用户和标准用户。

管理员用户将有权访问其他属性(在 View 模型中定义)和功能(在服务中定义),而标准用户则不能。

在下面的示例代码中,Foo1 相当于管理员,Foo2 是标准用户。

Controller 操作

private readonly IFoo1Service _foo1Service;
private readonly IFoo2Service _foo2Service;

public HomeController
(IFoo1Service foo1Service,
IFoo2Service foo2Service)
{
_foo1Service = foo1Service;
_foo2Service = foo2Service;
}

public IActionResult Foo1()
{
Foo1ViewModel vm = _foo1Service.NewViewModel();
return View(vm);
}

public IActionResult Foo2()
{
Foo2ViewModel vm = _foo2Service.NewViewModel();
return View(vm);
}

服务

public class Foo1Service : BaseFooService, IFoo1Service
{
public Foo1ViewModel NewViewModel()
{
//*** LINE CAUSING THE ERROR ***
//NewBaseFooViewModel returns a BaseFooViewModel
//AS Foo1ViewModel derives from the BaseFooViewModel
//I thought I could cast it
Foo1ViewModel vm = (Foo1ViewModel) NewBaseFooViewModel();
//set some defaults
vm.Foo1Field1 = "Foo1Field1";
vm.Foo1Field2 = "Foo1Field2";
return vm;
}

public Foo1ViewModel GetViewModelFromEntity(Entity entity)
{
Foo1ViewModel vm = (Foo1ViewModel) GetBaseFooViewModelFromEntity(entity);
vm.Foo1Field1 = entity.Foo1Field1;
vm.Foo1Field2 = entity.Foo1Field2;
return vm;
}
}

public class Foo2Service : BaseFooService, IFoo2Service
{
public Foo2ViewModel NewViewModel()
{
Foo2ViewModel vm = (Foo2ViewModel) NewBaseFooViewModel();
return vm;
}

public Foo2ViewModel GetViewModelFromEntity(Entity entity)
{
Foo2ViewModel vm = (Foo2ViewModel) GetBaseFooViewModelFromEntity(entity);
return vm;
}
}

public class BaseFooService : IBaseFooService
{
public BaseFooViewModel NewBaseFooViewModel()
{
return new BaseFooViewModel()
{
BaseFooField1 = "BaseFooField1",
BaseFooField2 = "BaseFooField2",
BaseFooField3 = "BaseFooField3"
};
}

public BaseFooViewModel GetBaseFooViewModelFromEntity(Entity entity)
{
return new BaseFooViewModel()
{
BaseFooField1 = entity.BaseFooField1,
BaseFooField2 = entity.BaseFooField2,
BaseFooField3 = entity.BaseFooField3
};
}
}

接口(interface)

public interface IFoo1Service : IBaseFooService
{
Foo1ViewModel NewViewModel();
}

public interface IFoo2Service : IBaseFooService
{
Foo2ViewModel NewViewModel();
}

public interface IBaseFooService
{
BaseFooViewModel NewBaseFooViewModel();
}

查看模型

public class Foo1ViewModel : BaseFooViewModel
{
public string Foo1Field1 { get; set; }
public string Foo1Field2 { get; set; }
}

public class Foo2ViewModel : BaseFooViewModel
{

}

public class BaseFooViewModel
{
public string BaseFooField1 { get; set; }
public string BaseFooField2 { get; set; }
public string BaseFooField3 { get; set; }
}

观看次数

Foo1

@model  BaseServiceSample.ViewModels.Foo.Foo1ViewModel

<h1>Base foo fields</h1>
<p>@Model.BaseFooField1</p>
<p>@Model.BaseFooField2</p>
<p>@Model.BaseFooField3</p>

<h2>Foo1 fields</h2>
<p>@Model.Foo1Field1</p>
<p>@Model.Foo1Field2</p>

Foo2

@model BaseServiceSample.ViewModels.Foo.Foo2ViewModel

<h1>Base foo fields</h1>
<p>@Model.BaseFooField1</p>
<p>@Model.BaseFooField2</p>
<p>@Model.BaseFooField3</p>

启动时的依赖注入(inject)

services.AddScoped<IFoo1Service, Foo1Service>();
services.AddScoped<IFoo2Service, Foo2Service>();

问题

应用程序编译正常,但在运行时出现错误:

InvalidCastException: Unable to cast object of type 'BaseServiceSample.ViewModels.Foo.BaseFooViewModel' to type 'BaseServiceSample.ViewModels.Foo.Foo1ViewModel'

请参阅我在 Foo1Service 中的评论,这些评论位于导致运行时错误的代码行上方。

我认为,如果一个类派生自基类,则它可以转换为派生类,但我可能将其与 MVC 中模型绑定(bind)的工作方式混淆了。

问题

我如何更改我的代码,使其支持管理两个不同用户组的共享属性和功能的基本 View 模型和基本服务的基本要求,但允许用户组扩展这些属性/功能?

根据我的研究,我可能需要使用抽象类或类型参数,但我无法让它工作。

我在没有尝试提供类型参数的情况下包含了代码示例,以保持代码更简单,并希望有人更容易指导我在哪里需要使用它。

对于类型参数,我的意思是:

BaseFooService<T> : IBaseFooService<T> where T : class

最佳答案

问题是:

public Foo2ViewModel GetViewModelFromEntity(Entity entity)
{
Foo2ViewModel vm = (Foo2ViewModel) GetBaseFooViewModelFromEntity(entity);
return vm;
}

public BaseFooViewModel GetBaseFooViewModelFromEntity(Entity entity)
{
return new BaseFooViewModel()
{
BaseFooField1 = entity.BaseFooField1,
BaseFooField2 = entity.BaseFooField2,
BaseFooField3 = entity.BaseFooField3
};
}

GetBaseFooViewModelFromEntity 返回一个新的 BaseFooViewModel。它不返回 Foo2ViewModel。您可以将 Foo2ViewModel 转换为其基类,但反之则不行。

I thought that if a class derived from a base class that it could be casted as the derived class

您不能将基类的任何对象强制转换为任何派生类型。仅当对象实际上是该派生类型(或从派生类型派生的东西)时,转换才有效。

换句话说,您不能这样做,因为 BaseFooViewModel 不是 Foo2ViewModel:

var model = new BaseFooViewModel();
var fooModel = (Foo2ViewModel)model;

但您可以这样做,因为 Foo2ViewModel 始终是 BaseFooViewModel:

var fooModel = new Foo2ViewModel();
var model = (BaseFooViewModel)fooModel;

你可以这样做(但你为什么要这么做?)

Foo2ViewModel fooModel = new Foo2ViewModel();
BaseFooViewModel model = (BaseFooViewModel)fooModel;
Foo2ViewModel thisWorks = (Foo2ViewModel)model;

之所以有效,是因为该对象始终是一个 Foo2ViewModel,因此它可以转换为该对象或其基类型。

这里有一个让编译器引导你的建议,这样你就会得到编译器错误而不是运行时错误。这将以更多编译器错误的形式打开一堆蠕虫,但好消息是您可以在不运行代码的情况下修复它们。编译器错误比运行时错误好。

第一步是将 BaseFooModel 抽象化,这样您就无法创建它的实例。大概您只想处理像 Foo1ViewModelFoo2ViewModel 这样的类。使基类抽象将确保您只能创建派生类。

public abstract class BaseFooViewModel // Just added "abstract"
{
public string BaseFooField1 { get; set; }
public string BaseFooField2 { get; set; }
public string BaseFooField3 { get; set; }
}

当你使 BaseFooModel 抽象时,这将无法编译:

new BaseFooViewModel()

...因为您无法创建抽象类的实例。您只能创建非抽象派生类的实例。

假设您的派生类没有带参数的构造函数,您可以这样做:

public TFooModel GetBaseFooViewModelFromEntity<TFooModel>(Entity entity)
where TFooModel : BaseFooViewModel, new()
{
return new TFooModel()
{
BaseFooField1 = entity.BaseFooField1,
BaseFooField2 = entity.BaseFooField2,
BaseFooField3 = entity.BaseFooField3
};
}

现在您要告诉方法要创建哪个特定的继承类。通用约束 - BaseFooViewModel, new() - 指定它必须继承自 BaseFooViewModel,并且它必须具有默认构造函数,以便该类可以创建它的新实例.

它将创建您指定的类的一个实例,但因为它"is"一个 BaseFooViewModel,所以它可以设置属于该基类的属性。

然后你可以这样调用方法:

Foo2ViewModel vm = GetBaseFooViewModelFromEntity<Foo2ViewModel>(entity);

另一种看待它的方式。像这样的运行时转换:

Foo2ViewModel vm = (Foo2ViewModel) GetBaseFooViewModelFromEntity(entity);

通常要避免。我说一般是因为有很多异常(exception)。但是在编写我们自己的泛型类时,我什至会说我们应该始终避免它。相反,最好编写代码,以便我们拥有的声明类型是我们唯一关心的类型。

换句话说,如果方法返回 Foo 或参数是 Foo 类型,那么我们唯一关心的类型是 Foo。我们不关心它的基类,也不关心我们得到的对象是否真的是继承自Foo的东西。

这里有一篇单独的文章,稍微描述一下我的个人经历。我称之为 Generic Rabbit Hole of Madness .

关于c# - 我如何在 MVC 中跨我的 View 模型和服务使用基类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55021535/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com