Linq to SQL Repository ~理论~ - 通用但现在使用 Linq to Objects?

发布于 2024-12-15 16:59:28 字数 2283 浏览 3 评论 0原文

我目前正在进行的项目使用Linq to SQL作为ORM数据访问技术。它是一个 MVC3 Web 应用程序。我遇到的问题主要是由于无法模拟(用于测试)由 DBML 设计器自动生成的 DataContext。

因此,为了解决这个问题(经过大量阅读),我重构了现有的存储库系统 - 每个表都有单独和重复的访问方法的单个存储库,最终得到了大约 300 个方法,其中只有 10 个是唯一的 - 到一个存储库中使用通用方法获取表并将更多通用类型返回到应用程序的上游。 DataContext 现在已被包装,并且可以轻松模拟。

[编辑:为了实现这一目标,我巧合地使用了下面由 Jacob 提供的链接!]

我的问题更多地围绕着我迄今为止所使用的设计以及我在应用程序结构中注意到的差异。

1) 重构了使用经典 Linq to SQL 查询的代码:

    public Billing GetBilling(int id)
    {
        var result = (
        from bil in _bicDc.Billings
        where bil.BillingId == id
        select bil).SingleOrDefault();
        return (result);
    }

它现在看起来像:

    public T GetRecordWhere<T>(Expression<Func<T, bool>> predicate) where T : class
    {
        T result;
        try
        {
            result = _dataContext.GetTable<T>().Where(predicate).SingleOrDefault();
        }
        catch (Exception ex)
        {
            throw ex;
        }
        return result;
    }

并由控制器使用以下查询:

_repository.GetRecordWhere<Billing>(x => x.BillingId == 1);

这很好,也正是我想要实现的目标。

...但是...我还必须执行以下操作才能精确获取控制器类中我需要的结果集(本质上是应用程序的最高点)...

viewModel.RecentRequests = _model.GetAllRecordsWhere<Billing>(x => x.BillingId == 1)
                .Where(x => x.BillingId == Convert.ToInt32(BillingType.Submitted))
                .OrderByDescending(x => x.DateCreated).
                Take(5).ToList();

据我的理解,这是正确 - 现在使用 Linq to Objects 而不是我之前使用的 Linq to SQL 查询?这样练习可以吗?我感觉不对,但我不知道为什么。可能是因为查询的逻辑位于应用程序的最高层,而不是最低层,但是......我听取你们好人的建议。我考虑的问题之一是将整个表放入内存中,但我知道使用 Iqeryable 返回类型会将 where 子句带到数据库并在那里进行评估。因此仅返回我需要的结果集...我可能是错的。

如果您已经做到了这一步,那么干得好。谢谢您,如果您有任何建议,我们将不胜感激!

更新:根据要求包含 GetAllRecordsWhere 方法

    public IQueryable<T> GetAllRecordsWhere<T>(Expression<Func<T, bool>> predicate) where T : class
    {
        return _dataContext.GetTable<T>().Where(predicate);
    }

该方法使用:

    public IQueryable<TName> GetTable<TName>() where TName : class
    {
        return _db.GetTable<TName>().AsQueryable();
    }

The project I am currently working on used Linq to SQL as an ORM data access technology. Its an MVC3 Web app. The problem I faced was primarily due to the inability to mock (for testing) the DataContext which gets autogenerated by the DBML designer.

So to solve this issue (after much reading) I refactored the repository system which was in place - single repository with seperate and duplicated access methods for each table which ended up with something like 300 methods only 10 of which were unique - into a single repository with generic methods taking the table and returning more generic types to the upper reaches of the application. The DataContext is now wrapped, and easily mocked.

[Edit: To achieve this i have used the link provided by Jacob below, coincidently!]

My question revolves more around the design I've used to get thus far and the differences I'm noticing in the structure of the app.

1) Having refactored the code which used classic Linq to SQL queries:

    public Billing GetBilling(int id)
    {
        var result = (
        from bil in _bicDc.Billings
        where bil.BillingId == id
        select bil).SingleOrDefault();
        return (result);
    }

it now looks like:

    public T GetRecordWhere<T>(Expression<Func<T, bool>> predicate) where T : class
    {
        T result;
        try
        {
            result = _dataContext.GetTable<T>().Where(predicate).SingleOrDefault();
        }
        catch (Exception ex)
        {
            throw ex;
        }
        return result;
    }

and is used by the controller with a query along the lines of:

_repository.GetRecordWhere<Billing>(x => x.BillingId == 1);

which is fine, and precisely what I wanted to achieve.

...however.... I'm also having to do the following to get precisely the result set i require in the controller class (the highest point of the app in essence)...

viewModel.RecentRequests = _model.GetAllRecordsWhere<Billing>(x => x.BillingId == 1)
                .Where(x => x.BillingId == Convert.ToInt32(BillingType.Submitted))
                .OrderByDescending(x => x.DateCreated).
                Take(5).ToList();

This - as far as my understanding is correct - is now using Linq to Objects rather than the Linq to SQL queries I was previously? Is this okay practise? It feels wrong to me but I dont know why. Probably because the logic of the queries is in the very highest tier of the app, rather than the lowest, but... I defer to you good people for advice. One of the issues I considered was bringing the entire table into memory but I understand that using the Iqeryable return type the where clause is taken to the database and evaluated there. Thus returning only the resultset i require... i may be wrong.

And if you've made it this far, well done. Thank you, and if you have any advice it is very much appreciated!!

Update: Inclusion of GetAllRecordsWhere method as requested

    public IQueryable<T> GetAllRecordsWhere<T>(Expression<Func<T, bool>> predicate) where T : class
    {
        return _dataContext.GetTable<T>().Where(predicate);
    }

which uses:

    public IQueryable<TName> GetTable<TName>() where TName : class
    {
        return _db.GetTable<TName>().AsQueryable();
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

聽兲甴掵 2024-12-22 16:59:28

如果 _model.GetAllRecordsWhere 返回一个 IQueryable 那么你的后续查询仍然只是构建一个表达式树(我认为这就是你使用 LinqToSql 的意思),它只会在你枚举时变成 SQL 并执行通过迭代或调用 ToList() 或 ToArray() 来获取集合。

顺便说一句,不要这样做:

catch (Exception ex)
{
    throw ex;
}

您所做的只是吞噬堆栈跟踪。如果你想重新抛出异常,只需调用throw ,而不要throw ex 。如果你在接球过程中除了重新投掷之外没有做任何事情,那么就不要接球。现在的正常模式是捕获、进行一些记录、重新抛出。

If _model.GetAllRecordsWhere returns an IQueryable then your subsequent querying is still just building up an expression tree (which is what i think you mean by using LinqToSql), it only gets turned into SQL an executed when you enumerate the collection by iterating over it or calling ToList() or ToArray().

As an aside don't do this:

catch (Exception ex)
{
    throw ex;
}

All you are doing is swallowing the stack trace. If you want to rethrow an exception just call throw, never throw ex. If you don't do anything in your catch other than rethrow then don't catch. The nowmal pattern for this would be catch, do some logging, rethrow.

猫腻 2024-12-22 16:59:28

这是一篇很好的文章,解释了如何模拟 DataContext:

伪造你的 LINQ提供商第 1 部分

Here is a good article that explains how to mock out your DataContext:

Faking your LINQ provider part 1

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文