Asp.net mvc 在ASP.NET MVC3项目中组合多态对象

Asp.net mvc 在ASP.NET MVC3项目中组合多态对象,asp.net-mvc,dependency-injection,ninject-extensions,object-composition,Asp.net Mvc,Dependency Injection,Ninject Extensions,Object Composition,我的问题的实质是如何用MVC3和Ninject以合理的方式组合这些对象(见下文)(尽管我不确定DI是否应该在解决方案中发挥作用)。我不能透露我的项目的真实细节,但这里有一个大概的例子来说明这个问题。欢迎用VB或C语言回答 我有几种不同的产品,它们的特性千差万别,但它们都需要在目录中表示出来。每个产品类在我的数据库中都有一个对应的表。目录项有一些特定于目录项的属性,因此有自己的表。我为目录条目定义了一个接口,目的是调用DescriptionText属性将根据底层的具体类型给出非常不同的结果 Pub

我的问题的实质是如何用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>
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
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; }
}
public class DALModelType
{
    public string SomeText { get; set; }
}
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())
Public Class ClothingCatalogEntry   
    Public Sub New(ByVal entity As CatalogEntryEntity)
...
在某个视图中,会出现如下代码:

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>
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
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; }
}
public class DALModelType
{
    public string SomeText { get; set; }
}
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())
Public Class ClothingCatalogEntry   
    Public Sub New(ByVal entity As CatalogEntryEntity)
...

IMHO,除了不得不为每一个新产品类别更改工厂之外,我并没有从这个代码中得到任何气味。然而,我的直觉告诉我,这是做事情的老方法,现在DI和/或泛型可以做得更好。非常感谢关于如何处理此问题的建议(以及关于更好标题的建议…

我只想在视图的模型上使用默认构造函数,并通过以下方式填充它们

我会有这样一个视图模型:

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>
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
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; }
}
public class DALModelType
{
    public string SomeText { get; set; }
}
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())
Public Class ClothingCatalogEntry   
    Public Sub New(ByVal entity As CatalogEntryEntity)
...
我有一个DAL的模型如下:

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>
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
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; }
}
public class DALModelType
{
    public string SomeText { get; set; }
}
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())
Public Class ClothingCatalogEntry   
    Public Sub New(ByVal entity As CatalogEntryEntity)
...
所以你的控制器里有这样的东西:

var dalModel = someRepository.GetAll();
var viewModel = Mapper.Map<DALModelType, ViewModelType>(dalModel);
var-dalModel=someRepository.GetAll();
var viewModel=Mapper.Map(dalModel);
在一些文件中有自动映射设置代码。这样,转换代码只在一个位置,而不是在多个方法/控制器中。您有一个使用依赖项注入(而不是()=>new CustomResolver())的应用程序,这将包含获取显示文本的逻辑

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

不确定这是否适用于您的工作流程,但它应该能够满足您的需要。

因此,我通过使用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将提供工厂(这太棒了!),假设我像这样设置绑定:

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>
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
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; }
}
public class DALModelType
{
    public string SomeText { get; set; }
}
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())
Public Class ClothingCatalogEntry   
    Public Sub New(ByVal entity As CatalogEntryEntity)
...
为了简洁起见,我省略了FastenerCatalog条目;ClothingCatalog条目如下所示:

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>
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
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; }
}
public class DALModelType
{
    public string SomeText { get; set; }
}
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())
Public Class ClothingCatalogEntry   
    Public Sub New(ByVal entity As CatalogEntryEntity)
...

正是这一点让我明白了这一点。我使用的UseFirstParameterAsNameInstanceProvider与图中所示完全相同。

因此,自定义解析器处理多个类映射到同一接口的事实?这就是在我的问题中将我推到工厂类的原因,这也是我最不舒服的地方,也完全不清楚如何摆脱。好了,现在我已经阅读了您提供的链接,我得出结论,我应该合理地期望我的DI容器(Ninject)对此场景有一些答案。此外,它看起来是这样的,我需要上下文绑定,我可能也需要这个:是的,这应该可以解决你的问题。在ninject初始化中,您应该能够从资源文件加载。