存储库模式和本地化查找表

时间:2013-12-27 13:35:59

标签: c# entity-framework repository-pattern

我试图抓住模式存储库的想法,并尝试在我过去已经设置的数据库结构中实现它。我现在正在努力获得使用查找表的最佳实践。我已经创建了一个测试项目,这是我的数据库模型:

enter image description here

你可以看到我有三个查找表:Lookup,Language和LookupLanguage。语言表只包含语言。

enter image description here

查找表包含整个模型中使用的不同类型。 enter image description here

LookupLanguage将两个表链接在一起: enter image description here

我创建了一个新项目,所有模型都是1到1的数据库表: enter image description here

我还创建了一个通用存储库和一个通用的CrudService接口:

public interface ICrudService<T> where T : IsActiveEntity, new()
{
    int Create(T item);
    void Save();
    void Delete(int id);
    T Get(int id);
    IEnumerable<T> GetAll();
    IEnumerable<T> Where(Expression<Func<T, bool>> func, bool showDeleted = false);
    void Restore(int id);
}

现在,根据以下帖子:When implementing the repository pattern should lookup value / tables get their own Repository?,存储库应该隐藏底层数据库层。所以我认为我需要一个服务和/或存储库的新实现来获取查找,但是,我在哪里必须告诉我需要哪种语言进行查找?

让我们以公司的状态(新的,接受的,拒绝的)为例。

公司模式如下:

public partial class Company : IsActiveEntity
{
    [Required]
    [MaxLength(50)]
    public string CompanyName { get; set; }
    public System.Guid StatusGuid { get; set; }

    [ForeignKey("StatusGuid")]
    public virtual Lookup Status { get; set; }
}

我想我不需要单独实现存储库? 但是我需要一个单独的实现CompanyService。

interface ICompanyService : ICrudService<Company>
{
    IQueryable<LookupLanguage> GetStatuses(Guid languageguid);
    LookupLanguage GetStatus(Guid statusguid, Guid languageguid);
}

这是正确的做法,还是我错过了什么?

5 个答案:

答案 0 :(得分:2)

关于你的问题,“这是正确的方法” - 这完全取决于你的具体需求。

你所做的事似乎没有任何实际问题。您已经使用泛型实现了存储库模式,这非常棒。您正在使用存储库的接口,这样可以更轻松地进行单元测试,也很棒!

您的某个标签似乎表明您对Entity Framework感兴趣。你似乎没有使用它。 Entity Framework将通过为您创建锅炉板类来简化您的代码。您仍然可以将存储库模式代码与Entity Framework创建的类一起使用。

您似乎对服务和存储库的想法感到困惑。存储库是一个通用对象,它允许您从商店获取数据,而无需关心实现。在您的示例中,ICompanyService是一个存储库。

答案 1 :(得分:2)

在您的案例中以更好的选项创建Generic LookupRepository,因为您的表架构和维护角度。

由于名称ICompanyService,我不确定您是使用Service Locator和Repository模式还是只使用Repository。但无论如何,我同意存储库不应该总是代表表1-1,但他们大多数时间都这样做。

您提供的SO链接与您的表格结构不同。您有一个通用的查找表,而链接每个查找都有一个单独的表。如果您有单独的表,那么将查找存储库方法与实体存储库一起使用是有意义的,因为您将有一个单独的代码来获取每个查找的数据(因为它们具有不同模式的单独表)。

但在你的情况下,你有一个表存储每种语言的所有查找类型,并且有一个LookupRepository可以返回基于Language和LookupType的所有各种类型的查找。如果在单独的实体存储库中创建每个查找方法(如CompanyRepository中的GetStatuses和ContactRepository中的GetStatuses),则必须为每个存储库重复方法中的逻辑。

想想你是否改变了查找表的模式(比如添加一个列),并且你想要测试所有使用查找的地方,如果你在整个地方都有查找方法,这将是噩梦,如果你有一个查找方法就很容易LookupRepository中的方法。

interface ILookupService : ICrudService<Lookup>
{
    IQueryable<Lookup> GetStatuses(Guid languageguid, LookupType lookupType);
    Lookup GetStatus(Guid statusguid, Guid languageguid, LookupType lookupType);
}

答案 2 :(得分:1)

这是一个非常有争议的话题,这个问题有不同的方法。在我们的数据逻辑中,我们没有使用存储库模式,因为我们不希望抽象实体框架的大部分好处。相反,我们将上下文传递给业务逻辑,该业务逻辑已经是UoW / Repository模式的组合。如果您在所有公司服务上采用这种方式,那么您的方法是可以的。 然而到目前为止我所看到的,通过返回值将方法放到相关服务中是提醒它们的最佳方法。例如,如果您想要查找公司,请创建ILookupService并使用GetLookUpsByCompany(int companyId)方法检索公司查找。

答案 3 :(得分:1)

我会与相关的回复争论。存储库链接到数据库实体,考虑到实体框架本身作为uow /存储库实现是一个最好的例子。另一方面,服务是针对域关注的,如果您的数据库实体和域实体之间存在不匹配(您有两个单独的层),服务可以帮助粘合两者。

在您的特定情况下,您拥有存储库,尽管您将其称为服务。而且每个数据库实体需要一个存储库,它更容易实现和维护。并且它也有助于回答您的问题:是的,您需要链接表的额外存储库。

一个小建议。您似乎有一个只接受where子句

的通用查询函数
IEnumerable<T> Where(Expression<Func<T, bool>> func, bool showDeleted = false);

如果你已经遵循这条允许任意过滤表达式的路线(这本身就有点争论,因为有人会指出你可以'保证所有技术上可能的过滤器可以由数据库引擎),为什么不允许所有可能的查询,包括订购,分页等:

IQueryable<T> Query { get; }

这与您的版本一样容易实现(您只是公开dbset)但允许客户端执行更复杂的查询,同样可能担心此类合同可能过于宽泛。

答案 4 :(得分:0)

本地化是表示层事物。应用程序的较低层应该尽可能少地打扰它。

我看到两种不同类型的查询:编码概念的翻译(先生/小姐/夫人)和实体属性的翻译(公司名称可能,或者职称或产品名称)。

编码概念

我不会将查找表用于编码概念。完全没有必要打扰下层。您只需要为整个应用程序翻译一次,并创建包含翻译的简单资源文件。

但是如果您确实希望将翻译保留在数据库中,则代码甚至每个代码系统的单独查找存储库将排序替换资源文件并为您提供服务。

实体属性

当某些实体具有一个或多个以不同语言翻译的属性时,我可以想象出不同/更糟糕的本地化问题。然后,翻译成为实体的一部分。我希望存储库在字典中咳出包含描述的所有翻译的实体对象。因为查询,缓存和更新关系时业务层不应该担心语言。它不应该向公司存储库询问公司X的荷兰语版本。它应该只是询问公司X并且提供一个包含荷兰语,英语和法语名称的Company对象。


我还有一个关于实际数据库实现的评论: 我认为查找表会分散实际实体的注意力,甚至忘记在人与公司之间建立关系。 ;)我建议将实体属性的所有翻译放在一个XML类型列中。

这说明了存储库应该处理实体和翻译的原因。如果您要在某个时刻更改此存储层级实现,即从查找表转到xml列,则存储库接口应保持不变。