对 Linq to SQL 和存储库感到困惑

发布于 2024-08-25 19:59:27 字数 3392 浏览 1 评论 0原文

在过去 4 年里,我一直在一家小公司担任 ASP.NET 开发人员,该公司的所有应用程序都是使用 .NET 2.0 中的智能 UI 反模式构建的。这意味着我没有 .NET 3.5 和 LINQ 等内容以及存储库和服务层等一般概念的经验。我意识到为了找到一份新工作,我需要升级我的知识,所以我开始阅读书籍、博客和很多SO问题,例如这个 现在是时候尝试用我应该学到的知识来制作一个简单的应用程序了。

我想构建一个小型应用程序来管理项目中的错误。

这是我想出的非常基本的数据库图表:

数据库
(来源:kristofclaes.be

我已经翻译了这个以下类的图表(我省略了 Linq to SQL 属性):

class Project
{
    public int ID { get; internal set; }
    public string Name { get; set; }
    public string Description { get; set; }

    private EntitySet<Bug> bugs;
    public EntitySet<Bug> Bugs
    {
        get { return this.bugs; }
        set { this.bugs.Assign(value); }
    }
}

class Bug
{
    public int ID { get; internal set; }
    public string Summary { get; set; }
    public string Description { get; set; }

    private EntityRef<Project> belongsTo;
    public Project BelongsTo
    {
        get { return this.belongsTo.Entity; }
        set { this.belongsTo.Entity = value; }
    }

    private EntityRef<Person> currentStatusSetBy;
    public Person CurrentStatusSetBy
    {
        get { return this.currentStatusSetBy.Entity; }
        set { this.currentStatusSetBy.Entity = value; }
    }

    public Datetime CurrentStatusSetOn { get; set; }
    public BugStatus CurrentStatus { get; set; }

    private EntitySet<BugStatusHistory> previousStatuses
    public EntitySet<BugStatusHistory> PreviousStatuses
    {
        get { return this.previousStatuses; }
        set { this.previousStatuses.Assign(value); }
    }
}

class BugStatusHistory
{
    public int ID { get; internal set; }
    public DateTime StatusSetAt { get; set; }   
    public BugStatus Status { get; set; }

    private EntityRef<Person> statusSetBy;
    public Person StatusSetBy
    {
        get { return this.statusSetBy.Entity; }
        set { this.statusSetBy.Entity = value; }
    }
}

class Person
{
    public ID { get; internal set; }
    public string Name {get; set; }
    public string Email { get; set; }
    public string Login { get; set; }
    public string Password { get; set; }
}

enum BugStatus { New, Confirmed, Solved }

我已将这些类放入名为 DomainModel 的类库中,并且我想从 ASP.NET MVC 2 应用程序引用该 DomainModel。根据我所读到的内容,我还应该向我的 DomainModel 添加存储库,甚至可能是一个服务层。这就是让我困惑的地方。

我读过您不应该为每个类/表创建一个存储库,但应该为聚合(类组)创建一个存储库。如果一个实体不能脱离另一个实体的上下文而存在,那么它就不应该拥有自己的存储库。在我的示例中,Bug 始终链接到项目。这是否意味着我应该为聚合项目错误创建一个存储库?如果我想呈现所有错误的列表,无论它们在哪个项目中,该怎么办?我应该向 IProjectsRepository 添加方法 GetAllBugs() 吗?或者我应该为该用途创建一个单独的 IBugsRepository ?

我认为创建单独的存储库在这里可能有其优势。根据我所读到的有关 Linq to SQL 的内容,您可以在 DataContext 上设置一个属性来指定如何处理延迟加载和急切加载。现在,当我获得项目列表或单个项目时,我想急切地加载错误列表。但我不想急切地加载该列表中每个 Bug 的项目。但是,如果我想加载所有 Bug 的列表(无论项目如何)或单个 Bug,我确实想要急切地加载项目,但在这种情况下我不想急切地加载该项目中的错误列表。我的 Linq to SQL 知识非常有限,但这不是只能通过设置 DataContext 属性来实现吗?难道这不需要我有一个用于项目的 DataContext 和一个用于 Bug 的 DataContext 以及两个存储库吗?除非可以以某种方式告诉 DataContext 急切地加载最多 2 个级别,并对更深层次的任何内容进行延迟加载?或者所有这些都因为延迟执行而变得无关紧要?

请原谅我的长问题(可能还不是那么清楚),但所有这些新信息真的让我感到困惑。

(如果您想评论我的数据库图/类结构,请不要饶恕我:-))

For the last 4 years I have been working as an ASP.NET developer in a small company where all applications are built using the Smart UI anti-pattern in .NET 2.0. This means I have no experience with .NET 3.5 and things like LINQ and general concepts like repository and service layers. I realize that in order to find a new job, I need to upgrade my knowledge so I have started reading books, blogs and a lot of SO questions like this one and the time has come to try and make a simple application with what I should have learned.

I want to build a small application to manage bugs in projects.

This is the very basic database diagram I've come up with:

The database
(source: kristofclaes.be)

I have translated this diagram to the following classes (I have omitted the Linq to SQL attributes):

class Project
{
    public int ID { get; internal set; }
    public string Name { get; set; }
    public string Description { get; set; }

    private EntitySet<Bug> bugs;
    public EntitySet<Bug> Bugs
    {
        get { return this.bugs; }
        set { this.bugs.Assign(value); }
    }
}

class Bug
{
    public int ID { get; internal set; }
    public string Summary { get; set; }
    public string Description { get; set; }

    private EntityRef<Project> belongsTo;
    public Project BelongsTo
    {
        get { return this.belongsTo.Entity; }
        set { this.belongsTo.Entity = value; }
    }

    private EntityRef<Person> currentStatusSetBy;
    public Person CurrentStatusSetBy
    {
        get { return this.currentStatusSetBy.Entity; }
        set { this.currentStatusSetBy.Entity = value; }
    }

    public Datetime CurrentStatusSetOn { get; set; }
    public BugStatus CurrentStatus { get; set; }

    private EntitySet<BugStatusHistory> previousStatuses
    public EntitySet<BugStatusHistory> PreviousStatuses
    {
        get { return this.previousStatuses; }
        set { this.previousStatuses.Assign(value); }
    }
}

class BugStatusHistory
{
    public int ID { get; internal set; }
    public DateTime StatusSetAt { get; set; }   
    public BugStatus Status { get; set; }

    private EntityRef<Person> statusSetBy;
    public Person StatusSetBy
    {
        get { return this.statusSetBy.Entity; }
        set { this.statusSetBy.Entity = value; }
    }
}

class Person
{
    public ID { get; internal set; }
    public string Name {get; set; }
    public string Email { get; set; }
    public string Login { get; set; }
    public string Password { get; set; }
}

enum BugStatus { New, Confirmed, Solved }

I have put these classes in a class library called DomainModel and I want to reference that DomainModel from an ASP.NET MVC 2 application. From what I have read, I should also add repositories and maybe even a service layer to my DomainModel. This is what is confusing me.

I have read that you shouldn't create a repository for each class/table, but that you should create a repository for aggregates (groups of classes). If an entity can't exist out of the context of another entity, it shouldn't have its own repository. In my example, a Bug is always linked to a Project. Does that mean I should create a repository for the aggregate Project-Bug? What if I want to render a list of all bugs, no matter in what project they are. Should I add a method GetAllBugs() to my IProjectsRepository? Or should I create a separate IBugsRepository for that usage?

I think creating separate repositories could have its advantages here. From what I've read about Linq to SQL, you can set a property on the DataContext to specify how to handle lazy and eager loading. Now, when I get a list of Projects or a single Project, I want to eagerly load the list of Bugs. But I don't want to eagerly load the Project of each Bug in that list. But, if I want to load a list of all Bugs (no matter the project) or a single Bug, I do want to eagerly load the Project, but in this case I don't want to eagerly load the list of Bugs in that Project. My Linq to SQL knowlegde is very limited, but isn't this something that can only be achieved by setting the DataContext properties? Wouldn't that require me to have a DataContext for Projects and a DataContext for Bugs and thus two repositories? Unless it somehow possible to tell the DataContext to eagerly load up to 2 levels and do lazy loading for anything that's deeper? Or is all of this irrelevant because of deferred execution?

Please excuse me for my long, long question (that maybe isn't even that clear) but all this new information is really confusing me.

(I case you like to comment on my database diagram / class structure, please don't spare me :-))

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

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

发布评论

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

评论(1

安人多梦 2024-09-01 19:59:27

如果我有其他方法来获取数据库中存储的数据,我个人只使用 L2S 的存储库模式 - 这通常意味着我还有一组用于 Project、Bug 等的接口。

存储库模式对于 L2S 来说是一个难题因为它使更新变得复杂 - 你从存储库中传递一个对象(它打开并关闭了一个 DataContext 来获取它 - 它们应该是短暂的,你看),你修改它的属性并将其发送回进行更新 - 但你可以不是,因为您需要原始的 DataContext。您需要从新的数据上下文中再次获取对象,复制修改后的值,然后更新第二个值。

为了避免这种情况,您必须开始将数据上下文传递到存储库中以进行大多数操作(让调用者确定数据上下文的生命周期);这会使你所有的方法签名变得臃肿,并造成全面的痛苦。

对我来说,L2S 的美妙之处在于它的速度 - 所以当我看到人们为了它而不必要地包装 DC 时,我感到不寒而栗。

不要误会我的意思——我现在正在编写一个系统,其中每个对象和“数据服务”都被抽象到接口后面;其中一些数据服务是使用Linq To Sql实现的;但这是因为整个系统旨在成为整个平台的热插拔后端。使用 L2S 很棘手,但仍然可行。

其次 - 就您对急切加载和延迟加载的担忧而言 - 您通常会使用 DataLoadOptions 类 和 DC 实例的“LoadOptions”成员。

如果您发现需要对延迟加载和急切加载的内容进行细粒度控制,那么您不需要单独的 DC - 您只需将数据上下文的创建卸载到针对不同需求定制的不同方法:

public MyDataContext GetDCForProjects()
{
  var DC = new MyDataContext();
  DataLoadOptions dlo = new DataLoadOptions();
  dlo.LoadWith<Project>(p => p.Bugs);
  DC.LoadOptions = dlo;
  return DC;
}

//add more methods for the different needs

I personally only use repository patterns with L2S if I will have alternative methods of obtaining the data stored within the database - which typically means that I also have a set of interfaces for Project, Bug etc.

The repository pattern is a ball-ache with L2S because it complicates Updates - you pass out an object from the repository (which opened and closed a DataContext to get it - they should be short-lived you see), you modify it's properties and send it back in for an update - but you can't, because you need the original DataContext. You need to go an get the object again from a new data context, copy the modified values, and update the second one.

To avoid this, you have to start passing in the data context into your repository for most operations (letting the caller determine the lifetime of the data context); which bloats all your method signatures and makes for an all-round pain in the a*s.

The beauty of L2S, for me, is it's speed - so I shudder when I see people needlessly wrapping the DC for the sake of it.

Don't get me wrong - I have a system I'm writing at the moment where every object and 'data service' is abstracted away behind interfaces; and some of those data services are implemented using Linq To Sql; but that's because the whole system is intended to be a hot-swappable back-end for a whole platform. Using L2S has been tricky, but it was still doable.

Secondly - in terms of your concerns about eager and lazy loading - you will typically control this with the DataLoadOptions class and the 'LoadOptions' member of the DC instance.

If you find that you need fine-grained control over which stuff to lazy and eager load, then you don't need separate DCs - you just offload the creation of the datacontext to different methods tailored for the different needs:

public MyDataContext GetDCForProjects()
{
  var DC = new MyDataContext();
  DataLoadOptions dlo = new DataLoadOptions();
  dlo.LoadWith<Project>(p => p.Bugs);
  DC.LoadOptions = dlo;
  return DC;
}

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