Возврат POCO определенной формы в действия ASP.NET MVC - PullRequest
5 голосов
/ 24 января 2012

В моем проекте ASP.NET MVC мои действия обычно вызывают уровень Service для получения данных. Я использую одинаковую дюжину POCO для всех моих моделей. Я также планирую использовать слой Service в консольных приложениях и, возможно, в какой-то момент выставлю веб-API.

Чтобы сделать операции с моей базой данных более эффективными, мой сервисный уровень увлажняет только те свойства в модели, которые относятся к конкретному методу (который в данный момент в основном определяется потребностями действий моего контроллера).

Так, например, у меня может быть класс Order со свойствами Id, Name, Description, Amount, Items. Для данного вызова службы мне может потребоваться только заполнить Id, Name, Items. Потребитель этой услуги не обязательно будет знать, что Amount равен 0 только потому, что он не заполняет свойство.

Аналогично, потребитель не будет знать, является ли Items пустым, потому что на самом деле нет никаких элементов, или этот конкретный метод обслуживания просто не заполняет это свойство.

А для третьего примера, скажем, на одном из моих представлений отображается ItemCount. Я не хочу полностью заполнять свою коллекцию Items, мне просто нужно дополнительное свойство для моей "модели". Я не хочу добавлять это свойство в мой POCO, которое будут использовать другие методы обслуживания, потому что оно не будет заполнено где-либо еще.

Таким образом, естественным решением является создание POCO, разработанного специально для этого метода, только с этими тремя свойствами. Таким образом, потребитель может знать, что все свойства будут заполнены его реальными значениями. Недостатком этого является то, что я закончу писать тонны моделей одинаковой формы.

Какой совет, какой метод работает лучше всего?

Ответы [ 8 ]

10 голосов
/ 24 января 2012

Вы можете использовать Nullable Types, чтобы указать недостающие свойства с нулем.

Например:

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;}
}

А потом, если Items == null, он не был установлен. Если это пустой new List<Item>(), он установлен, но пустой. То же самое для Amount. Если Amount.HasValue == false, это не было установлено. Если Amount.Value равно 0,0d, оно установлено, и элемент свободен.

3 голосов
/ 24 января 2012

Почему вы не используете проекцию LINQ?

Один метод обслуживания выполняет что-то вроде:

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

, в то время как другой метод обслуживания выполняет что-то вроде:

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

Я не уверен, как ваше приложение спроектировано, но это может быть способ создания сотен POCO.

Надеюсь, это поможет!Удачи.

1 голос
/ 07 июня 2012

Возвращает все POCO с обнуляемыми типами, как указано в @sbolm.Затем вы можете создать ViewModel для каждой страницы MVC, которая получает модель с определенными необходимыми свойствами.Это потребует большей производительности (незначительной) и кода, но при этом уровень вашего сервиса будет чистым, а ваши представления будут «глупыми», поскольку им дается только то, что им нужно, и они не имеют прямого отношения к уровню обслуживания.

Т.е. (пример класса из @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;}
}
1 голос
/ 03 июня 2012

Хорошим решением является использование значений Nullable, однако у него есть недостаток: вы не можете пометить обязательные поля.То есть вы не можете использовать обязательный атрибут ни для какого свойства.Таким образом, если есть поле, которое является обязательным в некоторых представлениях, вы не можете его представить.Если вам не требуется проверка необходимых полей, это нормально.В противном случае вам нужен способ представления того, какие файлы фактически используются, а затем написать пользовательский поставщик проверки.

Простой способ сделать это - использовать класс «Маска» с теми же именами свойстворигинальный класс, но со всеми полями: логические: истинные значения означают, что поле используется.

Я использовал аналогичное решение в системе, где отображаемые свойства настраиваются в файлах конфигурации ... так чтобыл для меня уникальным вариантом, так как у меня не было возможности представлять все комбинации свойств.ОДНАКО, я использовал класс «Маска» также в View, так что я смог выполнить всю работу только с одним View… с большим количеством ifs.

Теперь, если ваши 150 методов обслуживания и, вероятно, около150 представлений ... все разные, тогда, возможно, проще использовать также несколько классов ... это в худшем случае 150 классов ... дополнительная работа по их написанию незначительна по сравнению с усилиями по подготовке 150 различных представлений,Однако это не означает, что вам нужно 150 классов POCO.Вы можете использовать уникальный класс POCO, который копируется в соответствующий класс только на уровне представления.Преимущество этого подхода заключается в том, что вы можете помещать различные атрибуты проверки в различные классы, и вам не нужно писать собственный поставщик проверки.

1 голос
/ 24 января 2012

Вы можете передать селектор Func, который возвращает dynamic:

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

Я не уверен, как вы получаете доступ к данным, но ниже показано, как это будет работать при использовании List<T>:

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; }
}
0 голосов
/ 07 июня 2012

Чтобы расширить идею обнуляемости, вы можете использовать библиотеку fluentvalidation, чтобы все еще иметь проверку типов, зависящих от того, являются ли они пустыми или нет.Это позволит вам иметь обязательное поле, если оно не имеет значения 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
0 голосов
/ 07 июня 2012

Я бы не стал сильно оптимизировать производительность, если вы действительно не столкнетесь с проблемами производительности.

Я бы различал только возврат плоского объекта и объекта с более полным графом объектов.

Iбудут методы, возвращающие плоские объекты, называемые чем-то вроде GetOrder, GetProduct.

Если запрашиваются более полные графы объектов, они будут вызываться: GetOrderWithDetails.

Используете ли вы классы POCO для типизированных представлений?Если да: попробуйте создать новые классы, которые служат выделенными моделями представления.Эти ViewModels будут содержать классы POCO.Это поможет вам поддерживать чистоту классов POCO.

0 голосов
/ 05 июня 2012

Я бы предложил, чтобы вместо изменения моделей или создания моделей-оболочек вам пришлось назвать методы обслуживания так, чтобы они не требовали объяснений и показывали потребителю, что они возвращают.

Проблема с нулевым подходом состоит в том, что пользователь чувствует, что свойство не является обязательным или обязательным , и они пытаются вставить экземпляры этих типов без установки этих свойств. Разве не будет плохо иметь обнуляемые везде?

Это не будет хорошим подходом для изменения моделей предметной области, поскольку все, что вам нужно, это просто заполнить некоторые свойствавместо этого вы создаете сервис с именами и описаниями, которые не требуют пояснений.

Возьмите сам класс Order в качестве примера, скажем, один метод сервиса возвращает Order со всеми элементами, а другой -возвращает только детали Order, но не элементы.Тогда, очевидно, вам, возможно, придется создать два метода обслуживания GetOrderItems и GetOrderDetail, это звучит так просто, да, это так!но обратите внимание, что имена сервисных методов сами сообщают клиенту, что он собирается вернуть.В GetOrderDetail вы можете вернуть пустые элементы или ноль (но здесь я предлагаю ноль), который не имеет большого значения.

Так что для новых случаев вам не нужно часто менять модели, но всеВы должны сделать, это добавить или удалить методы обслуживания, и это нормально.Поскольку вы создаете сервис, вы можете создать надежную документацию, в которой говорится, что метод делает, что .

...