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
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
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
andLocationModel
), 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 thexxx.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
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
andLocationModel
), 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 thexxx.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);