LINQ、仓储模式和数据库抽象化

3

我花了一些时间来解决一个问题,但到目前为止还没有解决方案。我有一个预定义的非常庞大的数据库,其结构是固定的。我使用仓库模式在服务代码和数据库逻辑之间创建抽象层。问题是,在将对象从仓库传递出去之前,我需要对数据库对象进行一些处理。因此,我不能直接使用 Linq 实体。

基本上,仓库方法看起来像这样:

public IList<Bookingcode> FindBookingcode(int id) {
   return (from b in _db.BOOKINGCODE
           where b.ID == id
           select new Bookingcode {
              Id = b.ID,
              Name = b.NAME.Trim()
           }).ToList();
}

目前为止,这个很好用。但是我有很多对象需要单独组合。FindBookingcode() 应该返回一个几乎完整的对象,包括其他对象,如目录等。我的问题现在是,我必须像这个例子中一样多次重写映射:

public IList<Bookingcode> FindBookingcode(int id) {
    return (from b in _db.BOOKINGCODE
            join c1 in _db.CATALOG on b.CATALOGID equals c1.ID
            where b.ID == id
            let refs = (
                from bc1 in _db.BOOKINGCODE
                join p in _db.PACKAGE on bc1.ID equals p.BOOKINGCODE
                join bc2 in _db.BOOKINGCODE on p.PACKAGEREF equals bc2.ID
                join c in _db.CATALOG on bc.CATALOGID on bc2.CATALOGID equals c.ID
                where bc1.ID == b.ID
                select new PackageInfo {
                   ID = p.ID
                   BookingcodeRef = new Bookingcode { 
                       ID = bc2.ID,
                       Catalog = new Catalog { ID = c.ID }
                   }
                })
            select new Bookingcode {
              ID = b.ID,
              PackageInfo = refs.ToList()
            }).ToList();

}

我还在代码库中进行了一些L2O处理,以组装返回的对象。另一个我没有很好的解决方案的问题是如何告诉代码库应该获取什么,比如FindBookingcode(id, includePackageInfo, includeCatalog)。

那么这里有几个问题:

1)这种方法是否完全愚蠢?

2)你能指导我找到一个使重映射更简单的解决方案吗?

3)如何实现DDD的标准机制?

4个回答

3

在我的代码库中,我有一个单独的构建方法,它接受一个LINQ to SQL实体并返回一个业务对象。

该构建方法大致如下(此处的this.Container是Unity IoC容器,对示例不重要):

private IGroup BuildGroup(Entities.Group group)
{
    IGroup result = this.Container.Resolve<IGroup>();
    result.ID = group.GroupID;
    result.Name = group.Name;

    return result;
}

然后每个方法都使用build方法返回业务对象:
public override IGroup GetByID(int id)
{
    try
    {
        return (from g in this.Context.Groups
                where g.GroupID == id && g.ActiveFlag
                select this.BuildGroup(g)).Single();
    }
    catch (InvalidOperationException)
    {
        return null;
    }
}

这个方法通过从数据库中获取每个LINQ to SQL实体并运行它们通过构建方法,因此在这种情况下,您的结果将是您业务对象的可枚举而不是LINQ to SQL实体。

1

你是在使用Linq to SQL 还是Entity Framework呢?

我这里假设你在使用Entity Framework,因为你在问题中提到了“LINQ Entity”。

你可以使用Entity Framework导航属性来实现这种映射。你需要给想要进行映射的每个实体添加导航属性。只需告诉可视化设计器哪个属性映射到另一个实体中的相应属性即可。

查看如何使用Entity Framework视频系列获取快速入门指南。我建议你观看整个系列,因为它们非常有启发性。


我现在已经设置了一个简单的测试项目,使用Entity Framework并重构了一些部分。导航属性正常工作,并且因为我现在可以正确地映射1:n和n:m关系,所以我可以使IQueryable可用。感谢您指引我朝这个方向前进。我正在与EF Designer V1进行斗争,并在视图上添加了一些手动关系来处理这个糟糕的数据库方案。 - Claus Trojahn

1

If I call a repository method like

var list = x.FindBookingcode(int id)
I would expect to be able to add items to the list and not worry about how it is going to get persisted to the database. In the sample implementation there is no way for your repository function to detect that someone added something to the list so it cannot do anything about it.

我也希望能够传递规范。规范基本上是代表您的存储库类型执行操作并返回布尔值以指示是否必须考虑查询的委托。通常,您会实现一个基本规范,该规范实现逻辑和、或和非运算符,以便您可以轻松地将规范组合成新规范。


正如我在我的问题答案中所说的,这确实是一个应该考虑的事情。因此,在测试EF实现时,我已经删除了存储库,因为基本上它只是带有一些扩展的EF模型。因此,现在可以编写以下代码: var customers = ctx.Customers.WithEmail().WithBookings().WithRole(CustomerRole.Private) ...等等,其中WithBookings是对Transactions对象集上过滤器的快捷方式。 - Claus Trojahn

0

首先,抱歉回复晚了^^ 我太忙了,以至于忘记回复了。

John,你的解决方案相当不错,可以解决我的多重映射问题。

DoctaJonez,我正在使用Linq2SQL。我已经对Entity Framework和NHibernate进行了一些测试。映射问题的根源在于我有一个给定的数据库架构,有时真的很丑陋。因此,在访问数据时,我需要做很多处理。NHibernate工作得相当不错,我认为我将在不久的将来将其添加到项目中。

Hans,你的观点非常正确。但是,由于该项目只是用于查询数据库的API,因此没有向数据库写入。它更像是一个带有服务的dll,其他应用程序使用该服务从我们公司使用的预订软件中获取信息。

你提到了“规格”,这也是一个很好的观点。因为我有很多带有许多过滤器的巨大搜索查询,所以我确实需要一个良好的存储库API。这个问题在于,例如,不同的过滤器组合也会启动不同的处理逻辑。这也应该创建不同的SQL以达到有效性。

因为我已经映射了对象并使用Linq to SQL,所以无法使规范像应该的那样工作。因此,我在存储库上有很多方法来获取具有不同条件的对象,如下:

Bookingcode FindBookingcode(int id);
Bookingcode FindBookingcode(string code, string catalog);
List<Bookingcode> FindBookingcode();
List<Bookingcode> FindBookingcode(int[] ids);
List<Bookingcode> FindBookingcode(string code);
List<Bookingcode> FindBookingcode(string[] codes);
List<Bookingcode> FindBookingcodes(Catalog catalog);

因为我没有使用IQueryable,所以无法像链式调用一样连接操作。

repo.GetBookingcodes().WithCatalog("WI09").WithCode("XYZ10001");

对于像搜索这样的较大接口,我使用过滤器对象,如下所示

srv.SearchTransactions(new TransactionSearchFilter { 
    Customer = 1234, Destination = "DXB", ExtractServices = true });

然后存储库有一些逻辑来从数据库中获取内容。在存储库内,我正在测试使其像这样工作的东西

    repo.QueryTransactions()
    .Include<Customer>()
    .Include<TransactionsService>()
    .FilterBy(TransactionSearchFilter.Customer, 1234)
    .FilterBy(TransactionSearchFilter.Destination, "DXB")
    .Execute();

它内部构建了一个标准/规范。但这真的看起来像Entity Framework,也许如果我深入其中,我可以使用它或NHibernate。

我的存储库中最棘手的部分是搜索,因为我需要根据性能创建某种动态SQL。我已经尝试过PredicateBuilder,但无法解决一些查询,例如在设置特定过滤器时更改/包含表连接。

我认为我必须“扫描”更多文档。感谢您的回复。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接