使用存储库模式和 DI 在 SQL 和 XML ASP.Net MVC 之间切换

Using the Repository Pattern and DI to switch between SQL and XML ASP.Net MVC

我有 XMLProductRepository 和 SQLProductRepository。现在我怎么能动态地在它们之间切换。我是 DI 的新手。所以搜索 google 并找到一个 link 来讨论一下。但仍然不了解将在什么基础上更改存储库以及如何更改。这是代码

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 依赖将被注入。 如何实现它。寻找指导线。谢谢

一种可能的解决方案是注入 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 的示例实现。只需实施您自己的决策逻辑,即 returns 基于 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");

    }
}

我不知道你从哪里得到代码,也许 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,但这是一个使用模拟很容易解决的问题。

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