【问题标题】:Using the Repository Pattern and DI to switch between SQL and XML ASP.Net MVC使用存储库模式和 DI 在 SQL 和 XML ASP.Net MVC 之间切换
【发布时间】:2015-09-14 14:59:01
【问题描述】:

我有 XMLProductRepository 和 SQLProductRepository。现在我怎么能在它们之间动态切换。我是 DI 的新手。所以在谷歌上搜索相同的内容并找到了一个讨论一下的链接。但仍然不明白存储库将在什么基础上更改以及如何更改。这是代码

public interface IProductRepository
{
    IEnumerable<Product> GetAll();
    Product Get(int id);
    Product Add(Product item);
    void Remove(int id);
    bool Update(Product item);
}

public class XMLProductRepository : IProductRepository
{

    public XMLProductRepository() {}
    public IEnumerable<Product> GetAll() {}
    public Product Get(int id) {}
    public Product Add(Product item) {}
    public void Remove(int id) {}
    public bool Update(Product item) {}
}

public class SQLProductRepository : IProductRepository
{
    public SQLProductRepository() {}
    public IEnumerable<Product> GetAll() {}
    public Product Get(int id) {}
    public Product Add(Product item) {}
    public void Remove(int id) {}
    public bool Update(Product item) {}
} 

Unity.Mvc3 is using as Di

public static class Bootstrapper
{
    public static void Initialise()
    {
        var container = BuildUnityContainer();
        DependencyResolver.SetResolver(new UnityDependencyResolver(container));
    }

    private static IUnityContainer BuildUnityContainer()
    {
        var container = new UnityContainer();
        //Register the repository
        container.RegisterType<IProductRepository, SQLProductRepository>();
        return container;
    }
}

protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        AuthConfig.RegisterAuth();
        Bootstrapper.Initialise();

    }

public class HomeController : Controller
{
    private readonly IProductRepository productRepository;

    public HomeController(IProductRepository productRepository)
    {
        this.productRepository = productRepository;
    }

我了解动态 SQLProductRepository 实例正在注入控制器的代码。所以我的问题是如何注入XMLProductRepository

我想以这种方式设计一些基于 url 依赖的东西将被注入。 如何实现它。寻找指导线。谢谢

【问题讨论】:

  • 为什么需要在两个存储库之间动态切换?
  • 与@Steven。这完全不是依赖注入的用途。
  • @CodeCaster:是的,但在实践中,我发现真的不太可能在运行时更改实体的存储机制。更有可能需要在应用程序启动时设置存储机制,或者通过在运行时使用装饰器或拦截器包装实现来添加横切关注点(例如日志记录和审计跟踪)。
  • @Steven 看起来 OP 实际上不需要存储库可以互换。你通常需要它来进行单元测试。在这里,使用 DI 注入 IXmlRepositoryIDatabaseRepository 似乎是一种更明智的做法。
  • @CodeCaster,我宁愿说 SqlProductRepository 应该在你的容器中配置(如果有的话),而 XmlProductRepository 应该在需要它的单元测试中手动注入。

标签: asp.net-mvc dependency-injection repository-pattern


【解决方案1】:

一种可能的解决方案是注入 IProductRepositoryFactory 而不是 IProductRepository 本身。它看起来像这样:

interface IProductRepositoryFactory
{
    IProductRepository GetRepository(string url);
}

那么您的HomeController 将如下所示:

public class HomeController : Controller
{
    private readonly IProductRepositoryFactory productRepositoryFactory;

    public HomeController(IProductRepositoryFactory productRepositoryFactory)
    {
        this.productRepositoryFactory = productRepositoryFactory;
    }
}

这样,您将能够在运行时在控制器操作中获得所需的IProductRepository 实现——您只需在IProductRepositoryFactory.GetRepository(url) 方法中实现所需的逻辑。

这是一个控制器操作示例(请注意,以这种方式获取当前请求 URL 会降低该方法的可测试性):

public Product Get(string id)
{
    return productRepositoryFactory
          .GetRepository(Request.Url.ToString())
          .GetById(id);
}

UPD:以下是IProductRepositoryFactory 的示例实现。只需实现您自己的决策逻辑,它会根据 URL 返回一个适当的 IProductRepository 实例:

public class ProductRepositoryFactory : IProductRepositoryFactory
{
    public IProductRepository GetRepository(string url)
    {
        if (url.Contains("xml")) { return new XMLProductRepository(); }
        if (url.Contains("sql")) { return new SQLProductRepository(); }
        throw new ArgumentException("url");

    }
}

【讨论】:

  • 感谢您的回复,但查看此代码productRepositoryFactory .GetRepository(Request.Url.ToString()).GetById(id); 我不知道它是如何工作的。您是否介意发布小而完整的工作,结果基于 url 正确依赖将被注入。
  • @Mou 我已经添加了IProductRepositoryFactory的示例实现,请看一下。
【解决方案2】:

我不知道你从哪里得到代码,也许是this question,但你展示的IProductRepository 的两个实现有两个目的。

  • SQLProductRepository 将读写数据库中的数据。
  • XMLProductRepository 可以读取 XML,也可以写入文件。

在单元测试中运行代码时,您通常不想连接到数据库,但有时您确实想在单元测试中使用数据。这就是 XML 存储库派上用场的地方。您在 XML 文件中准备了一个可以提交给版本控制的数据集,您注入了所请求接口的另一个实现 - 即读取 XML 文件的接口 - 您不再需要数据库。

这就是为什么您将 DI 容器配置为注入 SQLProductRepository,而在单元测试中,当应用程序请求 IProductRepository 时,您或 DI 容器将提供 XMLProductRepository

现在,如果您说您的控制器,您的业务逻辑,是根据 URI 为一个特定请求选择 SQLProductRepository,为另一个请求选择 XMLProductRepository,那么为此目的使用 IProductRepository 是错误的。这是一个不应该使用您的 DI 容器来解决的问题。

引入两个新接口并将它们应用到存储库:

public interface ISqlProductRepository : IProductRepository
{
}

public interface IXmlProductRepository : IProductRepository
{
}

SQLProductRepository : ISqlProductRepository
XMLProductRepository : IXmlProductRepository

然后注册并注入那些:

// Application startup
container.RegisterType<ISqlProductRepository, SQLProductRepository>();
container.RegisterType<IXmlProductRepository, XMLProductRepository>();

// Controller
private readonly ISqlProductRepository _sqlProductRepository;
private readonly IXmlProductRepository _xmlProductRepository;

public HomeController(ISqlProductRepository sqlProductRepository, IXmlProductRepository xmlProductRepository)
{
    _sqlProductRepository = sqlProductRepository;
    _xmlProductRepository = xmlProductRepository;
}

public ActionResult SqlMethod1()
{
    // use _sqlProductRepository
}

public ActionResult XmlMethod2()
{
    // use _xmlProductRepository
}

当然,现在您不能再为SQLProductRepository 注入XMLProductRepository,但使用模拟很容易解决这个问题。

无论如何,根据您当前的一系列问题,您正在尝试学习有关单元测试和依赖注入的一些知识。请拿起一本像样的书,不要再将博客文章中的片段拼凑在一起,这些文章几乎无法解释您需要了解的所有内容。

【讨论】:

  • 你能告诉我参数化 HomeController 的 Ctor 什么时候会被调用吗?因为当我们通过 url 调用 homecontroller 时,它的参数 less ctor 将被调用。只是告诉在什么样的情况下 HomeController 的参数化 ctor 会被调用?它只发生在单元测试中吗?
  • @Mou 再次,请停止阅读不完整和过时的博客帖子,并根据这些帖子提出不知情的问题。重来。博客文章不是彻底学习的方法;它们适用于当您部分知道自己在做什么时。购买一本书或查找您使用的 DI 框架的制造商提供的详尽教程。每个带有 MVC 的 DI 教程都会向您解释 DI 框架(在本例中为 Unity)将注册一个控制器工厂,该工厂实例化具有参数化构造函数的控制器,或设置 DependencyResolver.SetResolver()
猜你喜欢
  • 2016-04-26
  • 1970-01-01
  • 1970-01-01
  • 2012-03-26
  • 2014-08-07
  • 1970-01-01
  • 1970-01-01
  • 2011-04-19
  • 1970-01-01
相关资源
最近更新 更多