6

Суть моего вопроса состоит в том, как скомпоновать эти объекты (см. Ниже) с MVC3 и Ninject (хотя я не уверен, что DI должен играть роль в решении). Я не могу раскрывать реальные детали моего проекта, но здесь приведено приближение, которое иллюстрирует проблему/вопрос. Ответы в VB или C# оцениваются!Составление полиморфных объектов в проекте ASP.NET MVC3

У меня есть несколько различных продуктов с разнообразными свойствами, но все они должны быть представлены в каталоге. Каждый класс продукта имеет соответствующую таблицу в моей базе данных. В записи каталога есть несколько свойств, характерных для входа в каталог, и, следовательно, их таблица. Я определил интерфейс для записей в каталоге с намерением, что вызов свойства DescriptionText даст мне очень разные результаты, основанные на базовом конкретном типе.

Public Class Clothing 
    Property Identity as Int64 
    Property AvailableSizes As List(Of String) 
    Property AvailableColor As List(Of String) 
End Class 

Public Class Fasteners 
    Property Identity as Int64 
    Property AvailableSizes As List(Of String) 
    Property AvailableFinishes As List(Of String) 
    Property IsMetric As Boolean 
End Class 

Public Interface ICatalogEntry 
    Property ProductId as Int64 
    Property PublishedOn As DateTime 
    Property DescriptionText As String 
End Interface 

Учитывая, что DescriptionText является уровень представления беспокойство, которое я не хочу, чтобы реализовать интерфейс ICatalogEntry в моих классах продукции. Вместо этого я хочу делегировать это формату.

Public Interface ICatalogEntryFormatter 
    Property DescriptionText As String 
End Interface 

Public Class ClothingCatalogEntryFormatter 
    Implements ICatalogEntryFormatter 

    Property DescriptionText As String 
End Class 

Public Class FastenerCatalogEntryFormatter 
    Implements ICatalogEntryFormatter 

    Property DescriptionText As String 
End Class 

В контроллере где-то будет такой код:

Dim entries As List(Of ICatalogEntry) 
        = catalogService.CurrentCatalog(DateTime.Now) 

В свете где-то будет такой код:

<ul> 
@For Each entry As ICatalogEntry In Model.Catalog 
    @<li>@entry.DescriptionText</li> 
Next 
</ul> 

Таким образом, вопрос, что делать конструкторы выглядят? Как настроить его, чтобы соответствующие объекты создавались в правильных местах. Похоже на дженерики, или, может быть, DI может помочь в этом, но у меня, похоже, есть ментальный блок. Единственная идея, которую я придумал, чтобы добавить свойство ProductType в ICatalogEntry, а затем реализовать завод, как это:

Public Class CatalogEntryFactory 
    Public Function Create(catEntry as ICatalogEntry) As ICatalogEntry 
     Select Case catEntry.ProductType 
     Case "Clothing" 
      Dim clothingProduct = clothingService.Get(catEntry.ProductId) 
      Dim clothingEntry = New ClothingCatalogEntry(clothingProduct) 
      Return result 
     Case "Fastener" 
      Dim fastenerProduct = fastenerService.Get(catEntry.ProductId) 
      Dim fastenerEntry = New FastenerCatalogEntry(fastenerProduct) 
      fastenerEntry.Formatter = New FastenerCatalogEntryFormatter 
      Return fastenerEntry 
    ...  
    End Function 
End Class 

Public ClothingCatalogEntry 
    Public Sub New (product As ClothingProduct) 
     Me.Formatter = New ClothingCatalogEntryFormatter(product) 
    End Sub 

    Property DescriptionText As String 
     Get 
      Return Me.Formatter.DescriptionText 
     End Get 
    End Property 
End Class 

...FastenerCatalogEntry is omitted but you get the idea... 

Public Class CatalogService 
    Public Function CurrentCatalog(currentDate as DateTime) 
     Dim theCatalog As List(Of ICatalogEntry) 
            = Me.repository.GetCatalog(currentDate) 

     Dim theResult As New List(Of ICatalogEntry) 

     For Each entry As ICataLogEntry In theCatalog 
      theResult.Add(factory.Create(entry)) 
     Next 

     Return theResult 
    End Function 
End Class 

ИМХО, я действительно не доставалось запахи от этого кода, кроме того, чтобы изменить фабрика для каждого нового класса продукта, который поставляется вместе. Тем не менее, моя кишка говорит, что это старый способ делать что-то, и в наши дни DI и/или дженерики могут сделать это лучше. Предложения о том, как с этим справиться, очень ценятся (как и предложения по лучшему названию ...)

ответ

1

Итак, сделав несколько небольших изменений, я получил это для работы с использованием расширения Ninject Factory. Самое большое изменение в том, что у моих сущностей достаточно информации для отображения любого типа (одежды или крепежа в моем надуманном примере), если элемент на самом деле является одеждой, тогда конкретные свойства крепежа будут нулевыми и наоборот.

Public Interface IDescribable 
    ReadOnly Property DescriptionText As String 
End Interface 

Public Enum ProductType 
    CLOTHING 
    FASTENER 
End Enum 

Public Interface ICatalogEntry 
    Inherits IDescribable 
    ReadOnly Property ProductId As Int64 
    ReadOnly Property PublishedOn As DateTime 
    ReadOnly Property ProductType As ProductType 
End Interface 

Public Class CatalogEntryEntity 
    Public Property ProductId As Long 
    Public Property ProductType As ProductType 
    Public Property PublishedOn As Date 
    Public Property DescriptionText As String 
    Public Property Color As String 
    Public Property Finish As String 
    Public Property IsMetric As Boolean 
End Class 

Тогда с этим на месте я могу определить мой каталог услуги следующим образом:

Public Class CatalogService 
    Private ReadOnly _factory As ICatalogEntryFactory 
    Private ReadOnly _repository As CatalogRepository 

    Public Sub New(entryFactory As ICatalogEntryFactory, repository As CatalogRepository) 
     Me._factory = entryFactory 
     Me._repository = repository 
    End Sub 

    Public Function CurrentCatalog(currentDate As DateTime) As List(Of ICatalogEntry) 
     Dim items = Me._repository.GetCatalog() 
     Return (From item In items Select _factory.Create(item.ProductType.ToString(), item)).ToList() 
    End Function 
End Class 

Public Interface ICatalogEntryFactory 
    Function Create(bindingName As String, entity As CatalogEntryEntity) As ICatalogEntry 
End Interface 

Ninject обеспечит завод при условии, мне настроить привязки, как это (что является удивительным!):

theKernel.Bind(Of ICatalogEntry)().To(Of ClothingCatalogEntry)().Named("CLOTHING") 
theKernel.Bind(Of ICatalogEntry)().To(Of FastenerCatalogEntry)().Named("FASTENER") 
theKernel.Bind(Of ICatalogEntryFactory)().ToFactory(Function() New UseFirstParameterAsNameInstanceProvider()) 

Я кратко пропустил FastenerCatalogEntry; ClothingCatalogEntry как это:

Public Class ClothingCatalogEntry 
    Public Sub New(ByVal entity As CatalogEntryEntity) 
... 

Это было this post, что помогло мне больше всего, чтобы понять это. Я использовал UseFirstParameterAsNameInstanceProvider точно так, как показано на рисунке.

1

Мне нравится просто использовать конструктор по умолчанию для моделей для представления и заполнить их через Automapper.

Я бы модель представления, как это:

public interface IHasDescription 
{ 
    public string DescriptionText { get; set; } 
} 

public class ViewModelType : IHasDescription 
{ 
    [DisplayName("This will be rendered in the view")] 
    public string SomeText { get; set; } 

    public string DescriptionText { get; set; } 
} 

И у меня есть модель из DAL, как это:

public class DALModelType 
{ 
    public string SomeText { get; set; } 
} 

Так у вас есть что-то вроде этого в контроллере:

var dalModel = someRepository.GetAll(); 
var viewModel = Mapper.Map<DALModelType, ViewModelType>(dalModel); 

И у вас есть код настройки Automapper в каком-то файле. Таким образом, у вас есть только код преобразования в одном месте, а не в нескольких методах/контроллерах. У вас есть custom resolver, который использует инъекцию зависимостей (вместо() => new CustomResolver()), и в этом будет ваша логика для получения текста на экране.

Mapper.CreateMap<IHasDescription, ViewModelType>() 
    .ForMember(dest => dest.DescriptionText, 
       opt => opt.ResolveUsing<CustomResolver>().ConstructedBy(() => new CustomResolver())); 

Не уверен, если это работает с рабочим процессом, но он должен быть в состоянии получить, что вы хотите.

+0

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

+0

Хорошо, теперь, когда я прочитал указанную вами ссылку, я пришел к выводу, что должен разумно ожидать, что мой контейнер DI (Ninject) получит ответ на этот сценарий. Кроме того, похоже, что это так, и мне нужна контекстная привязка https://github.com/ninject/ninject/wiki/Contextual-Binding, и мне это тоже нужно: https://github.com/ninject/ninject.extensions. factory/wiki – schmidlop

+0

Да, это действительно должно решить вашу проблему. В вашей инициализации ninject вы можете загрузить из файла ресурсов. –

Смежные вопросы