使用存储库模式和 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
,但这是一个使用模拟很容易解决的问题。
无论如何,根据您当前的一系列问题,您正在尝试学习有关单元测试和依赖项注入的知识。请拿起一本像样的书,不要再将博客文章中的片段拼凑在一起,它们几乎无法解释您需要了解的所有内容。
我有 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
,但这是一个使用模拟很容易解决的问题。
无论如何,根据您当前的一系列问题,您正在尝试学习有关单元测试和依赖项注入的知识。请拿起一本像样的书,不要再将博客文章中的片段拼凑在一起,它们几乎无法解释您需要了解的所有内容。