获取多个选择列表并在MVC中显示它们的推荐方法是什么?

时间:2022-09-06 20:16:26

I've searched both SO and Google on this but have not found a relevant / acceptable answer.
Background:
* Using MVC 4.5
* I have some Generic Repo<T> that I am using over the top of EF5, which in turn are accessed by generic Service<T>
* I have domain models and view models and I am using Automapper to map between them, this mapping happens in the Service layer
* On top of that, I have my Controllers which again are generic as much as possible.

So, to the question; I have a couple of scenarios where I need to present a list of options to the user and they have to select one or more. The options are user specific, so my domain User has a List<Location> which is their saved locations, and when adding / modifying, an Item, they are required to select at least one Location.

I am resisting the temptation to fetch that List<Location> in my controllers because I want to keep them generic and slim, but at the same time, I'd rather not have two properties in my ItemView model, one for AvailableLocations and one for SelectedLocations, because this model is used not just for adding / modifying but for search results etc.

Options:
* Should I introduce a different model for adding / modifying an Item, e.g. ItemInput?
* Should I use some custom mapping and get Automapper to get the list of available locations?
* In which layer should I fetch these available locations?

What are people's suggestions on a neat and generic approach to this please?
Many thanks!

我已经搜索了SO和谷歌,但没有找到相关/可接受的答案。背景:*使用MVC 4.5 *我有一些Generic Repo ,我在EF5的顶部使用,而后者又通过通用服务 访问*我有域模型和视图模型,我使用Automapper在它们之间映射,这种映射发生在服务层*最重要的是,我的控制器尽可能地是通用的。那么,对于这个问题;我有几个场景,我需要向用户提供一个选项列表,他们必须选择一个或多个。这些选项是特定于用户的,因此我的域用户具有List 这是他们保存的位置,并且在添加/修改Item时,他们需要选择至少一个Location。我正在抵制在我的控制器中获取List 的诱惑,因为我想让它们保持通用和纤薄,但与此同时,我宁愿在我的ItemView模型中没有两个属性,一个用于AvailableLocations,一个用于SelectedLocations,因为此模型不仅用于添加/修改,还用于搜索结果等。选项:*我是否应该引入不同的模型来添加/修改项目,例如ItemInput? *我应该使用一些自定义映射并获取Automapper来获取可用位置列表吗? *我应该在哪一层获取这些可用位置?人们对这个简洁而通用的方法提出了什么建议?非常感谢!

2 个解决方案

#1


2  

I would do something like this:

我会做这样的事情:

    public IEnumerable<Location> GetLocations() {
        return db.GetAll();
    }

Then inside of your controller (I followed this from MVC scaffolding):

然后在你的控制器里面(我从MVC脚手架跟着这个):

 ViewBag.Locations = new SelectList(service.GetLocations, "name", "id");

(or your own checkbox list) and put a listing control on the HTML/View page.

(或您自己的复选框列表)并在HTML / View页面上放置一个列表控件。

The reason I believe this is the best method is because the logic all resides inside the service. If you put it in your DTO/data model you may come against this problem:

我认为这是最好的方法的原因是因为逻辑都驻留在服务中。如果你把它放在你的DTO /数据模型中,你可能会遇到这个问题:

What happens if you require additional logic to pull back locations? i.e. sub locations of locations.

如果您需要额外的逻辑来撤回位置会发生什么?即位置的子位置。

You change your service (or override) to reflect the new changes, and this logic would go inside of the service:

您更改服务(或覆盖)以反映新的更改,此逻辑将进入服务内部:

    public IEnumerable<Location> GetLocations(string parent) {
        return db.GetAll().Where(loc => loc.parentname = parent);
    }

p.s. I never use generic services, the reason I have a service is because some of the data access it provides contains logic that is not meant to sit with the generic DAL.

附:我从不使用通用服务,我有一个服务的原因是因为它提供的一些数据访问包含的逻辑并不适用于通用DAL。

I could make an interface or abstract service to make my life a little easier for common operations between services but once you define say a concrete, a UserManagementSerive surely you are saying you want to manage an object that has Users, and Locations and Items each having its own specific functionality?

我可以创建一个接口或抽象服务,使我的生活对于服务之间的常见操作更容易一些,但是一旦你定义了具体的,UserManagementSerive肯定你说你要管理一个拥有用户的对象,以及每个拥有的Locations和Items它自己的特定功能?

#2


1  

I don't think there is only one possible answer to this question.

我不认为这个问题只有一个可能的答案。

I would recommend a simple, but not-so-generic approach. I would write what's called ViewModels, i.e. model classes that are related to your specific views. Then I would get your available locations from the controller, and populate an instance of the ViewModel in your controller using the fetched locations.

我会推荐一种简单但不那么通用的方法。我会编写所谓的ViewModels,即与您的特定视图相关的模型类。然后,我将从控制器获取您可用的位置,并使用获取的位置在控制器中填充ViewModel的实例。

Basically I would expose some services like:

基本上我会暴露一些服务,如:

IEnumerable<Location> GetAvailableLocationsForUser(string userName);

Do note I've used IEnumerable<T>, not IQueryable<T>. Because the implementation will actually request the database, as it's too much bug-prone (at least IMO) if it's the role of the controller to do so (remember the deferred execution of IQueryable<T>). And it returns a domain instance, i.e. an entity, not a mapped model. I wouldn't personally deal with anything but domain classes in the service layer. There could be domain classes that are not entities, but compositions of entities for example. This could help making efficient requests and avoiding using lazy-loading and deferred execution in the controllers. This is helpful when the controller needs a whole object graph and not only an entity.

请注意我使用了IEnumerable ,而不是IQueryable 。因为实现实际上会请求数据库,因为它太容易出错(至少是IMO),如果它是控制器的角色(记住延迟执行IQueryable )。它返回一个域实例,即实体,而不是映射模型。除了服务层中的域类,我不会亲自处理任何事情。可能存在不是实体的域类,而是例如实体的组合。这有助于提高请求并避免在控制器中使用延迟加载和延迟执行。当控制器需要整个对象图而不仅仅是实体时,这很有用。

Then I would write Models and ViewModels like the following, in the web application assembly:

然后我会在Web应用程序程序集中编写如下所示的Models和ViewModel:

public LocationModel
{
    ...
}

public CreateItemViewModel : ItemModel
{
    public List<LocationModel> AssociatedLocations { get; set; }
    public List<LocationModel> AvailableLocations { get; set; }
    ...
}
  • There are basically Models (ItemModel and LocationModel), which are objects related to the web application. This means there could be some web-related things in those models, for example computed read-only properties or attributes on properties (DisplayAttribute...etc.). I would write those models multiple times, actually, because I don't think this is something that could be generalized: for example, one view could require the use of a navigation property while another view wouldn't. So this changes the depth of the mapping process depending on the views that uses the model. And I wouldn't use AutoMapper at all (only hand-written mappers).
  • 基本上有模型(ItemModel和LocationModel),它们是与Web应用程序相关的对象。这意味着在这些模型中可能存在一些与Web相关的事物,例如计算的只读属性或属性上的属性(DisplayAttribute ...等)。实际上,我会多次编写这些模型,因为我不认为这是可以推广的东西:例如,一个视图可能需要使用导航属性而另一个视图则不需要。因此,这会根据使用模型的视图更改映射过程的深度。我根本不会使用AutoMapper(只有手写的映射器)。
  • There are also ViewModels (CreateItemViewModel), which are objects related to a single view (for example the view that allows to create an Item in this example). The difference between Model and ViewModel is that the ViewModel is related to a single view (and named according to this view). On the other hand, Models are related to multiple views (its namespace would help to know which views. For example, xxx.Item.Models for Models related to all views in the xxx.Item directory). ViewModels are built from scratch in the controller (or in a separate mapper) based on the domain classes.
  • 还有ViewModel(CreateItemViewModel),它们是与单个视图相关的对象(例如,在此示例中允许创建Item的视图)。 Model和ViewModel之间的区别在于ViewModel与单个视图相关(并根据此视图命名)。另一方面,模型与多个视图相关(其命名空间有助于了解哪些视图。例如,xxx.Item.Models用于与xxx.Item目录中的所有视图相关的模型)。 ViewModel是基于域类在控制器(或单独的映射器)中从头开始构建的。

In the above example, you could build a domain classes that would return AssociatedLocations and AvailableLocations, but it would require your service layer to be aware of the web part (I mean, your service interface and domain classes would know which properties are needed for a particular view). I'm not sure those properties are actually related to a single view in your application, but if it's not the case, you could also build a domain class as a composition of entities that would return AssociatedLocations and AvailableLocations:

在上面的示例中,您可以构建一个返回AssociatedLocations和AvailableLocations的域类,但是它需要您的服务层知道Web部件(我的意思是,您的服务接口和域类将知道需要哪些属性)特别观点)。我不确定这些属性是否与应用程序中的单个视图有关,但如果不是这样,您还可以将域类构建为将返回AssociatedLocations和AvailableLocations的实体组合:

public ItemExtended : Item
{
    public List<Location> AssociatedLocations { get; set; }
    public List<Location> AvailableLocations { get; set; }
}

ItemExtended GetItemExtendedById(long idItem);

#1


2  

I would do something like this:

我会做这样的事情:

    public IEnumerable<Location> GetLocations() {
        return db.GetAll();
    }

Then inside of your controller (I followed this from MVC scaffolding):

然后在你的控制器里面(我从MVC脚手架跟着这个):

 ViewBag.Locations = new SelectList(service.GetLocations, "name", "id");

(or your own checkbox list) and put a listing control on the HTML/View page.

(或您自己的复选框列表)并在HTML / View页面上放置一个列表控件。

The reason I believe this is the best method is because the logic all resides inside the service. If you put it in your DTO/data model you may come against this problem:

我认为这是最好的方法的原因是因为逻辑都驻留在服务中。如果你把它放在你的DTO /数据模型中,你可能会遇到这个问题:

What happens if you require additional logic to pull back locations? i.e. sub locations of locations.

如果您需要额外的逻辑来撤回位置会发生什么?即位置的子位置。

You change your service (or override) to reflect the new changes, and this logic would go inside of the service:

您更改服务(或覆盖)以反映新的更改,此逻辑将进入服务内部:

    public IEnumerable<Location> GetLocations(string parent) {
        return db.GetAll().Where(loc => loc.parentname = parent);
    }

p.s. I never use generic services, the reason I have a service is because some of the data access it provides contains logic that is not meant to sit with the generic DAL.

附:我从不使用通用服务,我有一个服务的原因是因为它提供的一些数据访问包含的逻辑并不适用于通用DAL。

I could make an interface or abstract service to make my life a little easier for common operations between services but once you define say a concrete, a UserManagementSerive surely you are saying you want to manage an object that has Users, and Locations and Items each having its own specific functionality?

我可以创建一个接口或抽象服务,使我的生活对于服务之间的常见操作更容易一些,但是一旦你定义了具体的,UserManagementSerive肯定你说你要管理一个拥有用户的对象,以及每个拥有的Locations和Items它自己的特定功能?

#2


1  

I don't think there is only one possible answer to this question.

我不认为这个问题只有一个可能的答案。

I would recommend a simple, but not-so-generic approach. I would write what's called ViewModels, i.e. model classes that are related to your specific views. Then I would get your available locations from the controller, and populate an instance of the ViewModel in your controller using the fetched locations.

我会推荐一种简单但不那么通用的方法。我会编写所谓的ViewModels,即与您的特定视图相关的模型类。然后,我将从控制器获取您可用的位置,并使用获取的位置在控制器中填充ViewModel的实例。

Basically I would expose some services like:

基本上我会暴露一些服务,如:

IEnumerable<Location> GetAvailableLocationsForUser(string userName);

Do note I've used IEnumerable<T>, not IQueryable<T>. Because the implementation will actually request the database, as it's too much bug-prone (at least IMO) if it's the role of the controller to do so (remember the deferred execution of IQueryable<T>). And it returns a domain instance, i.e. an entity, not a mapped model. I wouldn't personally deal with anything but domain classes in the service layer. There could be domain classes that are not entities, but compositions of entities for example. This could help making efficient requests and avoiding using lazy-loading and deferred execution in the controllers. This is helpful when the controller needs a whole object graph and not only an entity.

请注意我使用了IEnumerable ,而不是IQueryable 。因为实现实际上会请求数据库,因为它太容易出错(至少是IMO),如果它是控制器的角色(记住延迟执行IQueryable )。它返回一个域实例,即实体,而不是映射模型。除了服务层中的域类,我不会亲自处理任何事情。可能存在不是实体的域类,而是例如实体的组合。这有助于提高请求并避免在控制器中使用延迟加载和延迟执行。当控制器需要整个对象图而不仅仅是实体时,这很有用。

Then I would write Models and ViewModels like the following, in the web application assembly:

然后我会在Web应用程序程序集中编写如下所示的Models和ViewModel:

public LocationModel
{
    ...
}

public CreateItemViewModel : ItemModel
{
    public List<LocationModel> AssociatedLocations { get; set; }
    public List<LocationModel> AvailableLocations { get; set; }
    ...
}
  • There are basically Models (ItemModel and LocationModel), which are objects related to the web application. This means there could be some web-related things in those models, for example computed read-only properties or attributes on properties (DisplayAttribute...etc.). I would write those models multiple times, actually, because I don't think this is something that could be generalized: for example, one view could require the use of a navigation property while another view wouldn't. So this changes the depth of the mapping process depending on the views that uses the model. And I wouldn't use AutoMapper at all (only hand-written mappers).
  • 基本上有模型(ItemModel和LocationModel),它们是与Web应用程序相关的对象。这意味着在这些模型中可能存在一些与Web相关的事物,例如计算的只读属性或属性上的属性(DisplayAttribute ...等)。实际上,我会多次编写这些模型,因为我不认为这是可以推广的东西:例如,一个视图可能需要使用导航属性而另一个视图则不需要。因此,这会根据使用模型的视图更改映射过程的深度。我根本不会使用AutoMapper(只有手写的映射器)。
  • There are also ViewModels (CreateItemViewModel), which are objects related to a single view (for example the view that allows to create an Item in this example). The difference between Model and ViewModel is that the ViewModel is related to a single view (and named according to this view). On the other hand, Models are related to multiple views (its namespace would help to know which views. For example, xxx.Item.Models for Models related to all views in the xxx.Item directory). ViewModels are built from scratch in the controller (or in a separate mapper) based on the domain classes.
  • 还有ViewModel(CreateItemViewModel),它们是与单个视图相关的对象(例如,在此示例中允许创建Item的视图)。 Model和ViewModel之间的区别在于ViewModel与单个视图相关(并根据此视图命名)。另一方面,模型与多个视图相关(其命名空间有助于了解哪些视图。例如,xxx.Item.Models用于与xxx.Item目录中的所有视图相关的模型)。 ViewModel是基于域类在控制器(或单独的映射器)中从头开始构建的。

In the above example, you could build a domain classes that would return AssociatedLocations and AvailableLocations, but it would require your service layer to be aware of the web part (I mean, your service interface and domain classes would know which properties are needed for a particular view). I'm not sure those properties are actually related to a single view in your application, but if it's not the case, you could also build a domain class as a composition of entities that would return AssociatedLocations and AvailableLocations:

在上面的示例中,您可以构建一个返回AssociatedLocations和AvailableLocations的域类,但是它需要您的服务层知道Web部件(我的意思是,您的服务接口和域类将知道需要哪些属性)特别观点)。我不确定这些属性是否与应用程序中的单个视图有关,但如果不是这样,您还可以将域类构建为将返回AssociatedLocations和AvailableLocations的实体组合:

public ItemExtended : Item
{
    public List<Location> AssociatedLocations { get; set; }
    public List<Location> AvailableLocations { get; set; }
}

ItemExtended GetItemExtendedById(long idItem);