适用于中小型项目的 ASP.NET MVC、Nhibernate 和存储库

发布于 2024-10-13 09:03:17 字数 787 浏览 3 评论 0原文

我目前正在开发一个小型 ASP.NET MVC 项目。
我正在尝试实现 Nhibernate 以持久保存在 MS Sql Server 数据库上。 在花了很长时间研究 DDD 和在 Internet 上找到的其他项目后,我决定采用存储库模式。 现在我面临着两难的境地。
使用 Nhinbernate 时我真的需要存储库吗?
拥有一个与 Nhinbernate 交互的服务层(我目前没有服务层),避免多次编写类似的内容不是更好吗:

public Domain.Reminder GetById(Guid Code)
{
    return (_session.Get<Domain.Reminder>(Code));
}

public Domain.Reminder LoadById(Guid Code)
{
    return (_session.Load<Domain.Reminder>(Code));
}

public bool Save(Domain.Reminder Reminder)
{
    _session.SaveOrUpdate(Reminder);
    return (true);
}

public bool Delete(Domain.Reminder Reminder)
{
    _session.Delete(Reminder);
    return (true);
}

我发现了一个旧的 Ayende 的 POST,它是针对存储库的。
我知道围绕这些主题存在着巨大的争论,答案总是......取决于,但在我看来,由于抽象层太多,事情会变得更加复杂且难以理解。
我错了吗?

I am working on a small ASP.NET MVC project at the moment.
I am trying to implement Nhibernate to persist on a MS Sql Server database.
Having spent long hours studying DDD and other projects found on the Internet I have decided to go for the repository pattern.
Now I ma facing a dilemma.
Do I really need a repository when using Nhinbernate?
Wouldn't it be better to have a Service Layer (I don't have a Service Layer at the moment) which interacts with Nhinbernate avoiding to write many times something like that:

public Domain.Reminder GetById(Guid Code)
{
    return (_session.Get<Domain.Reminder>(Code));
}

public Domain.Reminder LoadById(Guid Code)
{
    return (_session.Load<Domain.Reminder>(Code));
}

public bool Save(Domain.Reminder Reminder)
{
    _session.SaveOrUpdate(Reminder);
    return (true);
}

public bool Delete(Domain.Reminder Reminder)
{
    _session.Delete(Reminder);
    return (true);
}

I found an old Ayende's POST which is against repositories.
I know there's a huge debate around these topics and the answer is always ... depends, but it seems to me that with too many layers of abstractions things get more complicated and hard to follow.
Am I wrong?

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

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

发布评论

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

评论(3

愚人国度 2024-10-20 09:03:17

Ayende 反对以您的方式编写存储库,因为您提出这个问题的原因是,它是重复的代码,而 NH 无论如何都可以处理所有这些代码。他主张像调用存储库一样直接调用 NH,而不必担心它。

我非常同意他的观点。除了付出更多的努力之外,确实没有什么收获。

Ayende was against writing a repository the way you did because of the reasons you asked this question, it is repetitive code and NH can handle all of it anyways. He advocates just calling NH directly as you would a repository, and stop worrying about it.

I pretty much agree with him. There really isn't much to gain except for more work.

太阳男子 2024-10-20 09:03:17

请改用通用存储库。每个类一个存储库很容易被过度杀伤。

我使用一个包含 Get、Load、Save 方法和各种匹配方法的存储库(一种用于 Linq,一种用于我的域查询)。

public class NHibernateRepository : IRepository
{
    private readonly ISession _session;

    public NHibernateRepository(ISession session)
    {
        _session = session;
    }

    public T Load<T>(Guid id)
    {
        return _session.Load<T>(id);
    }

    public T Get<T>(Guid id)
    {
        return _session.Get<T>(id);
    }

    public void Save<T>(T obj)
    {
        _session.SaveOrUpdate(obj);
    }

    public void Delete<T>(T obj)
    {
        _session.Delete(obj);
    }

    //Get all T
    public IEnumerable<T> Matching<T>() where T : DomainObject
    {
        return _session.CreateCriteria<T>().List<T>();
    }

    //NHibernate 3.0 Linq
    public IQueryable<T> Matching<T>(Expression<Func<T, bool>> predicate)
    {
        return _session.Query<T>().Where(predicate);
    }

    public IEnumerable<T> Matching<T>(ICreateCriteria<T> query, params IAppendCriterion[] extraCriterias)
    {
        var criteria = query.GetCriteria();
        foreach (var criterion in extraCriterias)
        {
            criterion.Append(criteria);
        }

        return criteria.GetExecutableCriteria(_session).List<T>();
    }
}

最后一个方法接受 ICreateCitiera 实现。下面是接口及其一种实现。

public interface ICreateCriteria<T> : ICreateCriteria
{
    DetachedCriteria GetCriteria();
}

public class ChallengesAvailableToRound : ICreateCriteria<Challenge>
{
    private readonly Guid _roundId;

    public ChallengesAvailableToRound(Round round)
    {
        _roundId = round.Id;
    }

    public DetachedCriteria GetCriteria()
    {
        var criteria = DetachedCriteria.For<Challenge>().
            CreateAlias("Event", "e").
            CreateAlias("e.Rounds", "rounds").
            Add(Restrictions.Eq("rounds.Id", _roundId));

        return criteria;
    }
}

这让我可以将查询分解到他们自己的类中,并轻松地在我的项目中重用它们。

Use a generic Repository instead. One repository per class can easily be overkill.

I use one repository with Get, Load, Save methods and various Matching-methods (one for Linq and one for my domain queries).

public class NHibernateRepository : IRepository
{
    private readonly ISession _session;

    public NHibernateRepository(ISession session)
    {
        _session = session;
    }

    public T Load<T>(Guid id)
    {
        return _session.Load<T>(id);
    }

    public T Get<T>(Guid id)
    {
        return _session.Get<T>(id);
    }

    public void Save<T>(T obj)
    {
        _session.SaveOrUpdate(obj);
    }

    public void Delete<T>(T obj)
    {
        _session.Delete(obj);
    }

    //Get all T
    public IEnumerable<T> Matching<T>() where T : DomainObject
    {
        return _session.CreateCriteria<T>().List<T>();
    }

    //NHibernate 3.0 Linq
    public IQueryable<T> Matching<T>(Expression<Func<T, bool>> predicate)
    {
        return _session.Query<T>().Where(predicate);
    }

    public IEnumerable<T> Matching<T>(ICreateCriteria<T> query, params IAppendCriterion[] extraCriterias)
    {
        var criteria = query.GetCriteria();
        foreach (var criterion in extraCriterias)
        {
            criterion.Append(criteria);
        }

        return criteria.GetExecutableCriteria(_session).List<T>();
    }
}

The last method accepts a ICreateCritiera implementation. Below is the interface and one implementation of it.

public interface ICreateCriteria<T> : ICreateCriteria
{
    DetachedCriteria GetCriteria();
}

public class ChallengesAvailableToRound : ICreateCriteria<Challenge>
{
    private readonly Guid _roundId;

    public ChallengesAvailableToRound(Round round)
    {
        _roundId = round.Id;
    }

    public DetachedCriteria GetCriteria()
    {
        var criteria = DetachedCriteria.For<Challenge>().
            CreateAlias("Event", "e").
            CreateAlias("e.Rounds", "rounds").
            Add(Restrictions.Eq("rounds.Id", _roundId));

        return criteria;
    }
}

This lets me break out the queries to their own classes and reuse them all over my project easily.

冷月断魂刀 2024-10-20 09:03:17

我发现了一些你应该使用 Repository/DAO/Whatever 的原因。

  1. 单元测试。我从未尝试过模拟/存根 ISession,但我想说它应该比模拟或存根存储库/DAO 接口复杂得多。
  2. 代码的重用。如果您将查询直接写入服务/控制器中,那么当您需要重用特定查询时,您最终会重复它。如果您将其包装到存储库中,只需调用它的方法即可。
  3. 单一职责原则。将查询分散到控制器中会让您打破这一原则。
  4. NHibernate 依赖。好吧,这很难发生,但如果您需要更改 ORM,存储库会让事情变得更容易(看,这更容易,而不是容易:))。或者甚至当您需要将用户数据源从数据库更改为 Microsoft Active Directory 时,也会更容易。

知道的就这些了,其他的想不起来了。

I found some reasons why you should use Repository/DAO/Whatever.

  1. Unit Testing. I never tried to mock/stub an ISession, but I would say that it should be a LOT more complex than mocking or stubbing a Repository/DAO Interface.
  2. Reuse of Code. If you write your query directly into your Services/Controllers you will end up duplicating it when you need to reuse a specific query. If you have it wrapper into a Repository, just call it's method.
  3. Single Responsibility Principle. Spreading your queries around your Controllers makes you break this principle.
  4. NHibernate Dependency. Ok, this is hard to happen, but if you need to change your ORM, the repositories make it easier ( look, it's easier, not easy :) ). Or even when you need to change the user DataSource from a Database to Microsoft Active Directory, it would be easier.

That's it for know, can't remember anything else.

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