6

私の質問の本質は、MVC3 と Ninject を使用してこれらのオブジェクト (以下を参照) を賢明な方法で構成する方法です (ただし、DI がソリューションで役割を果たすべきかどうかはわかりません)。私のプロジェクトの実際の詳細を開示することはできませんが、問題/質問を説明する近似値を次に示します。VB または C# での回答を歓迎します。

さまざまな特性を持ついくつかの異なる製品がありますが、それらすべてをカタログに表示する必要があります。データベースには、各製品クラスに対応するテーブルがあります。カタログ エントリには、カタログ エントリであることに固有のいくつかのプロパティがあり、その結果、独自のテーブルがあります。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 やジェネリックがこれをより適切に行うことができると言っています。これを処理する方法に関する提案は大歓迎です (より良いタイトルに関する提案も同様です...)

4

2 に答える 2

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セットアップコードがあります。このように、複数のメソッド/コントローラーではなく、1つの場所にのみ変換コードがあります。(()=> new CustomResolver()の代わりに)依存性注入を使用するカスタムリゾルバーがあり、これは表示テキストを取得するためのロジックを格納します。

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

これがワークフローで機能するかどうかはわかりませんが、必要なものを取得できるはずです。

于 2012-04-17T23:50:17.640 に答える
1

そのため、いくつかの小さな変更を加えて、Ninject Factory 拡張機能を使用してこれを機能させました。最大の変更点は、項目が実際に衣服である場合、エンティティがいずれかのタイプ (私の不自然な例では衣服または留め具) を表示するのに十分な情報を持っていることです。留め具固有のプロパティは null になり、逆の場合も同様です。

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)
...

これを理解するのに最も役立ったのはこの投稿でした。そこに示されているとおりに UseFirstParameterAsNameInstanceProvider を使用しました。

于 2012-04-24T15:45:40.273 に答える