我正在阅读 Steven Sanderson 的书 Pro ASP.NET MVC Framework,他建议使用存储库模式:
public interface IProductsRepository
{
IQueryable<Product> Products { get; }
void SaveProduct(Product product);
}
他直接从他的 Controller 访问产品存储库,但由于我将同时拥有一个网页和 Web 服务,我想添加一个将由 Controller 和 Web 服务调用的“服务层”:
public class ProductService
{
private IProductsRepository productsRepsitory;
public ProductService(IProductsRepository productsRepository)
{
this.productsRepsitory = productsRepository;
}
public Product GetProductById(int id)
{
return (from p in productsRepsitory.Products
where p.ProductID == id
select p).First();
}
// more methods
}
这看起来一切都很好,但我的问题是我不能使用他的 SaveProduct(Product product) 因为:
1) 我只想允许在 Product 表中更改某些字段
2)我想保留对 Product 表的每个字段所做的每次更改的审核日志,因此我必须为每个允许更新的字段提供方法。
我最初的计划是在 ProductService 中有一个这样的方法:
public void ChangeProductName(Product product, string newProductName);
然后调用 IProductsRepository.SaveProduct(Product)
但是我看到了一些问题:
1)这样传入Product对象不是很“OO”吗?但是,我看不出这段代码如何进入 Product 类,因为它应该只是一个哑数据对象。我可以看到向部分类添加验证,但不是这个。
2)在我坚持更改之前,如何确保没有人更改除产品以外的任何其他字段?
我基本上被撕裂了,因为我不能将审计/更新代码放在 Product 中,而 ProductService 类的更新方法看起来不自然(但是,GetProductById 对我来说似乎很自然)。
我想即使我没有审计要求,我仍然会遇到这些问题。无论哪种方式,我都想限制在一个类中可以更改哪些字段,而不是在网站和 Web 服务中复制逻辑。
我的设计模式一开始是不是很糟糕,还是我可以以一种干净的方式使这项工作?
任何见解将不胜感激。
最佳答案
我将存储库分为两个接口(interface),一个用于读取,一个用于写入。
读取实现 IDisposeable,并在其生命周期内重用相同的数据上下文。它将 linq 生成的实体对象返回给 SQL。例如,它可能看起来像:
interface Reader : IDisposeable
{
IQueryable<Product> Products;
IQueryable<Order> Orders;
IQueryable<Customer> Customers;
}
iQueryable 很重要,所以我得到了 linq2sql 的延迟评估优点。这很容易用 DataContext 实现,而且很容易伪造。请注意,当我使用此接口(interface)时,我从不使用相关行的自动生成字段(即,直接使用 order.Products 不公平,调用必须加入适当的 ID 列)。这是一个限制,我不介意考虑它使伪造单元测试的读取存储库变得多么容易。
写入的每个写入操作使用单独的数据上下文,因此它不实现 IDisposeable。它不将实体对象作为输入或输出 - 它采用每个写入操作所需的特定字段。
当我编写测试代码时,我可以将可读接口(interface)替换为使用一堆我手动填充的 List<> 的假实现。我使用模拟作为写接口(interface)。到目前为止,这就像一个魅力。
不要养成传递实体对象的习惯,它们绑定(bind)到数据上下文的生命周期,这会导致存储库与其客户端之间的不幸耦合。
关于asp.net-mvc - 使用 LINQ2SQL 限制对特定字段的更改时的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1365218/