将特定形状的POCO返回到ASP.NET MVC操作

时间:2023-02-02 22:26:05

In my ASP.NET MVC project, my actions typically call a Service layer to get data. I use the same dozen or so POCOs for all my models. I also plan on using the Service layer in console applications and maybe expose a web api at some point.

在我的ASP.NET MVC项目中,我的操作通常调用服务层来获取数据。我为所有模特使用了相同的十几个POCO。我还计划在控制台应用程序中使用服务层,并可能在某些时候公开web api。

To make my database operations more efficient, my service layer only hydrates the properties in the model that are relevant to the particular method (which at this point is mostly driven by the needs of my controller actions).

为了使我的数据库操作更有效,我的服务层只保留模型中与特定方法相关的属性(此时这些属性主要由我的控制器操作的需要驱动)。

So for example I might have a class Order with properties Id, Name, Description, Amount, Items. For a given service call I might only need to populate Id, Name, Items. A consumer of that service won't necessarily know that Amount is 0 only because it didn't populate the property.

因此,例如,我可能有一个具有属性Id,Name,Description,Amount,Items的Order。对于给定的服务调用,我可能只需要填充Id,Name,Items。该服务的消费者不一定知道Amount是0,因为它没有填充该属性。

Similarly, the consumer won't know whether Items is empty b/c there actually aren't any items, or whether this particular service method just doesn't populate that property.

类似地,消费者不会知道Items是否为空b / c实际上没有任何项目,或者这种特定的服务方法是否只是不填充该属性。

And for a third example, say one of my views displays an ItemCount. I don't want to fully populate my Items collection, I just need an additional property on my "model". I don't want to add this property to my POCO that other service methods will be using because it's not going to be populated anywhere else.

而对于第三个例子,假设我的一个视图显示了一个ItemCount。我不想完全填充我的Items集合,我只需要在我的“模型”上添加一个属性。我不想将此属性添加到我的POCO中,其他服务方法将使用它,因为它不会在其他任何地方填充。

So the natural solution is to make a POCO designed specifically for that method with only those 3 properties. That way the consumer can know that all properties will be populated with its real values. The downside to this is that I'll end writing tons of similarly shaped models.

因此,自然的解决方案是制作专门为该方法设计的POCO,仅具有这3种属性。这样,消费者就可以知道所有属性都将填充其实际值。不利的一面是,我将结束大量类似形状的模型。

Any advice on which method works best?

关于哪种方法效果最好的建议?

8 个解决方案

#1


10  

You could use Nullable Types to indicate the missing properties with a null.

您可以使用Nullable Types来指示缺少的属性。

For example:

class Order {
    public int Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public decimal? Amount {get;set;}
    public List<Item> Items {get;set;}
}

And then if Items == null, it wasn't set. If it's an empty new List<Item>(), it's set but empty. Same for Amount. If Amount.HasValue == false, it wasn't set. If Amount.Value is 0.0d, it's set and the item is free.

然后如果Items == null,则没有设置。如果它是一个空的新List (),则它已设置但为空。金额相同。如果Amount.HasValue == false,则表示未设置。如果Amount.Value为0.0d,则设置该项并且该项是免费的。

#2


3  

Why don't you use LINQ projection?

你为什么不使用LINQ投影?

One service method does something like:

一种服务方法可以做到:

return DbContext.Orders.Select(o => new { Id = o.Id, Name = o.Name, Description = o.Description });

while the other service method does something like:

而其他服务方法做的事情如下:

return DbContext.Orders.Select(o => o);

I'm not sure how your application is architected, but this may be a way around creating 100's of POCO's.

我不确定你的应用程序是如何构建的,但这可能是创建100个POCO的方法。

Hope this helps! Good luck.

希望这可以帮助!祝你好运。

#3


1  

You could pass in a selector Func that returns dynamic:

你可以传入一个返回动态的选择器Func:

public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector) { ... }

I'm not sure how you are accessing data, but the following shows how this would work using a List<T>:

我不确定您是如何访问数据的,但以下显示了如何使用List :

class Program
{
    static void Main(string[] args)
    {
        var service = new Service();
        var orderNames = service.GetOrders(o => new { o.Name });

        foreach (var name in orderNames)
            Console.WriteLine(name.Name);

        Console.ReadLine();
    }
}

public class Service
{
    private List<Order> _orders = new List<Order>
        {
            new Order { Id = 1, Name = "foo", Description = "test order 1", Amount = 1.23m },
            new Order { Id = 2, Name = "bar", Description = "test order 1", Amount = 3.45m },
            new Order { Id = 3, Name = "baz", Description = "test order 1", Amount = 5.67m }
        };

    public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector)
    {
        return _orders.Select(selector);
    }
}

public class Order
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Amount { get; set; }
}

#4


1  

The use of nullable values is a good solution, however it has the downside you have no way to matk required fields. That is you cannot use a required attribute on any property. So if there is field that is obligatory in some views you have no way to represent it. If you don't need required fileds validation this is ok. Otherwise, you need a way to represent which fileds are actually used, and then to write a custom validation provider.

使用可空值是一个很好的解决方案,但它有一个缺点,你无法匹配必填字段。也就是说,您无法在任何属性上使用必需属性。因此,如果在某些视图中存在强制性字段,则无法表示它。如果您不需要必要的文件验证,这是可以的。否则,您需要一种方法来表示实际使用的文件,然后编写自定义验证提供程序。

A simple way to do this is to use a "Mask" class with the same property names of the original class, but with all fields boolean: a true values means the field is in use.

一个简单的方法是使用一个“Mask”类,它具有与原始类相同的属性名,但所有字段都是boolean:true值表示该字段正在使用中。

I used a similar solution in a system where the properties to be shown are configured in a configuration files...so it was the unique option for me since I had no possibility to represent all combination of properties. HOWEVER, I used the "Mask" class also in the View, so I was able to do all the job with just one View..with a lot of ifs.

我在一个系统中使用了类似的解决方案,其中要显示的属性在配置文件中配置...所以它是我的唯一选项,因为我不可能代表所有属性组合。但是,我在View中也使用了“Mask”类,所以我只用一个View来完成所有工作..有很多ifs。

Now if your 150 service methods and probably about 150 Views...are all different, then maybe it is simpler to use also several classes ...that is in the worst case 150 classes..the extra work to write them is negligible if compared to the effort of preparing 150 different Views. However this doesnt mean you need 150 POCO classes. You might use an unique POCO class that is copied into an adequate class just into the presentation Layer. The advantage of this approach is that you can put different validation attributes on the various classes and you don't need to write a custom Validation provider.

现在,如果你的150个服务方法和大约150个视图......都是不同的,那么也许使用几个类更简单......在最坏的情况下150个类......编写它们的额外工作可以忽略不计与准备150个不同视图的努力相比。然而,这并不意味着您需要150个POCO课程。您可以使用唯一的POCO类,将其复制到表示层中的适当类中。此方法的优点是您可以在各个类上放置不同的验证属性,而无需编写自定义验证提供程序。

#5


1  

Return the entire POCO with nullable types as mentioned by @sbolm. You can then create a ViewModel per MVC page view that receives a model with the specific properties it needs. This will take more performance (insignificant) and code, but it keeps your service layer clean, and keeps your views "dumb" in that they are only given what they need and have no direct relation to the service layer.

使用@sbolm提到的可空类型返回整个POCO。然后,您可以为每个MVC页面视图创建一个ViewModel,该视图接收具有所需特定属性的模型。这将需要更多的性能(无关紧要)和代码,但它会保持您的服务层清洁,并使您的视图“哑”,因为它们只被给予他们所需的并且与服务层没有直接关系。

I.e. (example class from @sbolm)

即(来自@sbolm的示例类)

class Order {
    public int Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public decimal? Amount {get;set;}
    public List<Item> Items {get;set;}
}

// MVC View only needs to know the name and description, manually "map" the POCO properties into this view model and send it to the view

class OrderViewModel {
    public string Name {get;set;}
    public string Description {get;set;}
}

#6


0  

I would suggest that instead of modifying the models or creating wrapper models, you have to name the service methods such that they are self-explanatory and reveals the consumer what they returns.

我建议不要修改模型或创建包装模型,而是必须命名服务方法,使它们不言自明,并向消费者展示它们返回的内容。

The problem with the nullable approach is it makes the user to feel that the property is not required or mandatory and they try inserting instances of those types without setting those properties. Is it won't be bad having nullables every-where?

可空方法的问题是它使用户感觉不需要或强制该属性,并且他们尝试插入这些类型的实例而不设置这些属性。每个地方都有无效的情况会不会很糟糕?

It won't be a good approach to change the domain models since all you want is just to populate some of the properties instead of that you create service with names and descriptions that are self-explanatory.

改变域模型不是一个好方法,因为您只需要填充一些属性,而不是使用不言自明的名称和描述创建服务。

Take the Order class itself as the example, say one service method returns the Order with all the items and the other one returns only the details of the Order but not the items. Then obviously you may have to create two service methods GetOrderItems and GetOrderDetail, this sounds so simple, yes it is! but notice the service method names itself tells the client what it is going to return. In the GetOrderDetail you can return an empty items or null (but here I suggest a null) that doesn't matter much.

以Order类本身为例,假设一个服务方法返回包含所有项目的Order,另一个服务方法仅返回Order的详细信息而不返回项目。那么显然你可能需要创建两个服务方法GetOrderItems和GetOrderDetail,这听起来很简单,是的!但请注意,服务方法名称本身告诉客户端它将返回什么。在GetOrderDetail中,您可以返回一个空项或null(但在这里我建议为null)并不重要。

So for new cases you don't need to frequently change the models but all you got to do is add or remove the service methods and that's fine. Since you are creating a service you can create a strong documentation that says what method does what.

因此,对于新案例,您不需要经常更改模型,但您需要做的就是添加或删除服务方法,这很好。由于您正在创建服务,因此您可以创建一个强大的文档,说明哪种方法可以做什么。

#7


0  

I would not performance optimize this to much unless you realy get performance problems.

除非你真的遇到性能问题,否则我不会对性能进行优化。

I would only distinguish between returning a flat object and an object with a more complete object graph.

我只会区分使用更完整的对象图返回平面对象和对象。

I would have methods returning flat objects called something like GetOrder, GetProduct.

我会有方法返回称为GetOrder,GetProduct之类的扁平对象。

If more complete object graphs are requested they would be called : GetOrderWithDetails.

如果请求更完整的对象图,则会调用它们:GetOrderWithDetails。

Do you use the POCO classes for the typed views? If yes: try to make new classes that serve as dedicated ViewModels. These ViewModels would contain POCO classes. This will help you keeping the POCO classes clean.

您是否对输入的视图使用POCO类?如果是:尝试创建用作专用ViewModel的新类。这些ViewModel将包含POCO类。这将帮助您保持POCO类的清洁。

#8


0  

To expand on the nullable idea, you could use the fluentvalidation library to still have validation on the types dependent on whether they are null or not. This would allow you to have a field be required as long as it was not null or any other validation scheme you can think of. Example from my own code as I had a similar requirement:

要扩展可空的想法,您可以使用fluentvalidation库来依赖于它们是否为null来对类型进行验证。这将允许您只需要一个字段,只要它不是null或您可以想到的任何其他验证方案。我自己的代码示例,因为我有类似的要求:

Imports FluentValidation

Public Class ParamViewModelValidator
    Inherits AbstractValidator(Of ParamViewModel)

    Public Sub New()
        RuleFor(Function(x) x.TextBoxInput).NotEmpty.[When](Function(x) Not (IsNothing(x.TextBoxInput)))
        RuleFor(Function(x) x.DropdownListInput).NotEmpty.[When](Function(x) Not (IsNothing(x.DropdownListInput)))
    End Sub

End Class

#1


10  

You could use Nullable Types to indicate the missing properties with a null.

您可以使用Nullable Types来指示缺少的属性。

For example:

class Order {
    public int Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public decimal? Amount {get;set;}
    public List<Item> Items {get;set;}
}

And then if Items == null, it wasn't set. If it's an empty new List<Item>(), it's set but empty. Same for Amount. If Amount.HasValue == false, it wasn't set. If Amount.Value is 0.0d, it's set and the item is free.

然后如果Items == null,则没有设置。如果它是一个空的新List (),则它已设置但为空。金额相同。如果Amount.HasValue == false,则表示未设置。如果Amount.Value为0.0d,则设置该项并且该项是免费的。

#2


3  

Why don't you use LINQ projection?

你为什么不使用LINQ投影?

One service method does something like:

一种服务方法可以做到:

return DbContext.Orders.Select(o => new { Id = o.Id, Name = o.Name, Description = o.Description });

while the other service method does something like:

而其他服务方法做的事情如下:

return DbContext.Orders.Select(o => o);

I'm not sure how your application is architected, but this may be a way around creating 100's of POCO's.

我不确定你的应用程序是如何构建的,但这可能是创建100个POCO的方法。

Hope this helps! Good luck.

希望这可以帮助!祝你好运。

#3


1  

You could pass in a selector Func that returns dynamic:

你可以传入一个返回动态的选择器Func:

public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector) { ... }

I'm not sure how you are accessing data, but the following shows how this would work using a List<T>:

我不确定您是如何访问数据的,但以下显示了如何使用List :

class Program
{
    static void Main(string[] args)
    {
        var service = new Service();
        var orderNames = service.GetOrders(o => new { o.Name });

        foreach (var name in orderNames)
            Console.WriteLine(name.Name);

        Console.ReadLine();
    }
}

public class Service
{
    private List<Order> _orders = new List<Order>
        {
            new Order { Id = 1, Name = "foo", Description = "test order 1", Amount = 1.23m },
            new Order { Id = 2, Name = "bar", Description = "test order 1", Amount = 3.45m },
            new Order { Id = 3, Name = "baz", Description = "test order 1", Amount = 5.67m }
        };

    public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector)
    {
        return _orders.Select(selector);
    }
}

public class Order
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Amount { get; set; }
}

#4


1  

The use of nullable values is a good solution, however it has the downside you have no way to matk required fields. That is you cannot use a required attribute on any property. So if there is field that is obligatory in some views you have no way to represent it. If you don't need required fileds validation this is ok. Otherwise, you need a way to represent which fileds are actually used, and then to write a custom validation provider.

使用可空值是一个很好的解决方案,但它有一个缺点,你无法匹配必填字段。也就是说,您无法在任何属性上使用必需属性。因此,如果在某些视图中存在强制性字段,则无法表示它。如果您不需要必要的文件验证,这是可以的。否则,您需要一种方法来表示实际使用的文件,然后编写自定义验证提供程序。

A simple way to do this is to use a "Mask" class with the same property names of the original class, but with all fields boolean: a true values means the field is in use.

一个简单的方法是使用一个“Mask”类,它具有与原始类相同的属性名,但所有字段都是boolean:true值表示该字段正在使用中。

I used a similar solution in a system where the properties to be shown are configured in a configuration files...so it was the unique option for me since I had no possibility to represent all combination of properties. HOWEVER, I used the "Mask" class also in the View, so I was able to do all the job with just one View..with a lot of ifs.

我在一个系统中使用了类似的解决方案,其中要显示的属性在配置文件中配置...所以它是我的唯一选项,因为我不可能代表所有属性组合。但是,我在View中也使用了“Mask”类,所以我只用一个View来完成所有工作..有很多ifs。

Now if your 150 service methods and probably about 150 Views...are all different, then maybe it is simpler to use also several classes ...that is in the worst case 150 classes..the extra work to write them is negligible if compared to the effort of preparing 150 different Views. However this doesnt mean you need 150 POCO classes. You might use an unique POCO class that is copied into an adequate class just into the presentation Layer. The advantage of this approach is that you can put different validation attributes on the various classes and you don't need to write a custom Validation provider.

现在,如果你的150个服务方法和大约150个视图......都是不同的,那么也许使用几个类更简单......在最坏的情况下150个类......编写它们的额外工作可以忽略不计与准备150个不同视图的努力相比。然而,这并不意味着您需要150个POCO课程。您可以使用唯一的POCO类,将其复制到表示层中的适当类中。此方法的优点是您可以在各个类上放置不同的验证属性,而无需编写自定义验证提供程序。

#5


1  

Return the entire POCO with nullable types as mentioned by @sbolm. You can then create a ViewModel per MVC page view that receives a model with the specific properties it needs. This will take more performance (insignificant) and code, but it keeps your service layer clean, and keeps your views "dumb" in that they are only given what they need and have no direct relation to the service layer.

使用@sbolm提到的可空类型返回整个POCO。然后,您可以为每个MVC页面视图创建一个ViewModel,该视图接收具有所需特定属性的模型。这将需要更多的性能(无关紧要)和代码,但它会保持您的服务层清洁,并使您的视图“哑”,因为它们只被给予他们所需的并且与服务层没有直接关系。

I.e. (example class from @sbolm)

即(来自@sbolm的示例类)

class Order {
    public int Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public decimal? Amount {get;set;}
    public List<Item> Items {get;set;}
}

// MVC View only needs to know the name and description, manually "map" the POCO properties into this view model and send it to the view

class OrderViewModel {
    public string Name {get;set;}
    public string Description {get;set;}
}

#6


0  

I would suggest that instead of modifying the models or creating wrapper models, you have to name the service methods such that they are self-explanatory and reveals the consumer what they returns.

我建议不要修改模型或创建包装模型,而是必须命名服务方法,使它们不言自明,并向消费者展示它们返回的内容。

The problem with the nullable approach is it makes the user to feel that the property is not required or mandatory and they try inserting instances of those types without setting those properties. Is it won't be bad having nullables every-where?

可空方法的问题是它使用户感觉不需要或强制该属性,并且他们尝试插入这些类型的实例而不设置这些属性。每个地方都有无效的情况会不会很糟糕?

It won't be a good approach to change the domain models since all you want is just to populate some of the properties instead of that you create service with names and descriptions that are self-explanatory.

改变域模型不是一个好方法,因为您只需要填充一些属性,而不是使用不言自明的名称和描述创建服务。

Take the Order class itself as the example, say one service method returns the Order with all the items and the other one returns only the details of the Order but not the items. Then obviously you may have to create two service methods GetOrderItems and GetOrderDetail, this sounds so simple, yes it is! but notice the service method names itself tells the client what it is going to return. In the GetOrderDetail you can return an empty items or null (but here I suggest a null) that doesn't matter much.

以Order类本身为例,假设一个服务方法返回包含所有项目的Order,另一个服务方法仅返回Order的详细信息而不返回项目。那么显然你可能需要创建两个服务方法GetOrderItems和GetOrderDetail,这听起来很简单,是的!但请注意,服务方法名称本身告诉客户端它将返回什么。在GetOrderDetail中,您可以返回一个空项或null(但在这里我建议为null)并不重要。

So for new cases you don't need to frequently change the models but all you got to do is add or remove the service methods and that's fine. Since you are creating a service you can create a strong documentation that says what method does what.

因此,对于新案例,您不需要经常更改模型,但您需要做的就是添加或删除服务方法,这很好。由于您正在创建服务,因此您可以创建一个强大的文档,说明哪种方法可以做什么。

#7


0  

I would not performance optimize this to much unless you realy get performance problems.

除非你真的遇到性能问题,否则我不会对性能进行优化。

I would only distinguish between returning a flat object and an object with a more complete object graph.

我只会区分使用更完整的对象图返回平面对象和对象。

I would have methods returning flat objects called something like GetOrder, GetProduct.

我会有方法返回称为GetOrder,GetProduct之类的扁平对象。

If more complete object graphs are requested they would be called : GetOrderWithDetails.

如果请求更完整的对象图,则会调用它们:GetOrderWithDetails。

Do you use the POCO classes for the typed views? If yes: try to make new classes that serve as dedicated ViewModels. These ViewModels would contain POCO classes. This will help you keeping the POCO classes clean.

您是否对输入的视图使用POCO类?如果是:尝试创建用作专用ViewModel的新类。这些ViewModel将包含POCO类。这将帮助您保持POCO类的清洁。

#8


0  

To expand on the nullable idea, you could use the fluentvalidation library to still have validation on the types dependent on whether they are null or not. This would allow you to have a field be required as long as it was not null or any other validation scheme you can think of. Example from my own code as I had a similar requirement:

要扩展可空的想法,您可以使用fluentvalidation库来依赖于它们是否为null来对类型进行验证。这将允许您只需要一个字段,只要它不是null或您可以想到的任何其他验证方案。我自己的代码示例,因为我有类似的要求:

Imports FluentValidation

Public Class ParamViewModelValidator
    Inherits AbstractValidator(Of ParamViewModel)

    Public Sub New()
        RuleFor(Function(x) x.TextBoxInput).NotEmpty.[When](Function(x) Not (IsNothing(x.TextBoxInput)))
        RuleFor(Function(x) x.DropdownListInput).NotEmpty.[When](Function(x) Not (IsNothing(x.DropdownListInput)))
    End Sub

End Class