我应该使用泛型来简化我的 DAL 吗?

2023-12-11

我是 NHibernate 的新手,不太擅长 C#,但我正在学习。我有一个DataProvider类,它使用 NHibernate 3 为我的应用程序提供数据。它的结构几乎与Steve Bohlen 的 NHibernate 之夏视频.

我注意到我将要大量重复我的代码,并且我想简化我的代码DataProvider。例如,我有两个商务课程,名为Instrument and Broker。添加方法Instrument in my DataProvider is:

    public int AddInstrument(Instrument instrument)
    {
        using (ITransaction tx = _session.BeginTransaction())
        {
            try
            {
                int newId = (int)_session.Save(instrument);
                _session.Flush();
                tx.Commit();
                return newId;
            }
            catch (NHibernate.HibernateException)
            {
                tx.Rollback();
                throw;
            }
        }
    }

and the AddBroker类看起来非常相似(只需查找并替换)。所以我想也许我可以使用泛型来解决这个问题。就像是:

public class DataProvider <TEntity>
{
    public int AddEntity(TEntity entity)
    {
        using (ITransaction tx = _session.BeginTransaction())
        {
            try
            {
                int newId = (int)_session.Save(entity);
                _session.Flush();
                tx.Commit();
                return newId;
            }
            catch (NHibernate.HibernateException)
            {
                tx.Rollback();
                throw;
            }
        }
    }    
}

有了这个我可以传递一个Broker, an Instrument或者任何事情,我可以节省很多重复的代码。我遇到的问题是在我的Test类我创建一个新的DataProvider每次测试都像这样运行:

    #region Fields

    private DataProvider _provider;
    private SessionManager _sessionManager;
    private NHibernate.ISession _session;

    #endregion

    [SetUp]
    public void Setup()
    {
        DatabaseSetUp();

        _session = _sessionManager.GetSession();
        _provider = new DataProvider(_session);    // problem here
    }

使用泛型我必须将对象类型传递给我的DataProvider。你能想办法解决这个问题吗?我是一名新手程序员,我想知道我是否走在正确的道路上。我应该做一些完全不同的事情吗?

UPDATE

我尝试实施 Groo 的答案,但遇到了一些问题。这就是我所做的。

IRepo.cs

interface IRepo<T>
{
    int Add<Entity>(Entity entity);
    void Delete<Entity>(Entity entity);
    void GetById<Entity>(int Id);
}

BaseRepo.cs

public abstract class BaseRepo <T> : IRepo <T>
{
    private ISession _session;

    #region SessionManagement

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

    public ISession Session
    {
        set { _session = value; }
    }

    #endregion

    public int Add<Entity>(Entity entity)
    {
        using (ITransaction tx = _session.BeginTransaction())
        {
            try
            {
                int newId = (int)_session.Save(entity);
                _session.Flush();
                tx.Commit();
                return newId;
            }
            catch (NHibernate.HibernateException)
            {
                tx.Rollback();
                throw;
            }
        }
    }

    // other methods omitted for brevity
}

IRepoFactory.cs

interface IRepoFactory
{
    IInstrumentRepo CreateInstrumentRepo(ISession s);
}

RepoFactory.cs

public class RepoFactory : IRepoFactory
{
    public IInstrumentRepo CreateInstrumentRepo(ISession s) // problem here
    {
        return new InstrumentRepo(s);
    }

}

IInstrumentRepo.cs

interface IInstrumentRepo : IRepo<Instrument>
{

}

InstrumentRepo.cs

public class InstrumentRepo : BaseRepo<Instrument>, IInstrumentRepo
{
    public InstrumentRepo(ISession s) : base(s) { }
}

在 RepoFactory.cs 中我收到此错误:

Inconsistent accessibility: return type 'MooDB.Data.IInstrumentRepo' is less accessible than method 'MooDB.Data.RepoFactory.CreateInstrumentRepo(NHibernate.ISession)'

我有什么想法吗?


首先,为了解决您的测试设置问题:术语“存储库”可能表明它应该是一个长期存在的持久对象,但 DAL 操作中使用的存储库实际上应该是生命周期短的轻量级无状态对象:您在需要时实例化一个,并在完成后立即将其丢弃。当您考虑这是性能方面时,您可以轻松地每秒实例化数百万个它们。

结合NHibernate的短暂Session例如,当一切就位时,您的代码应如下所示:

using (var session = SessionManager.OpenSession())
{
    // create an instrument repo
    IInstrumentRepo instruments = DAL.RepoFactory.CreateInstrumentRepo(session);
    var guitar = instruments.Find(i => i.Type == "Guitar");

    // create a customer repo
    ICustomerRepo customers = DAL.RepoFactory.CreateCustomerRepo(session);
    var cust = customers.Find(c => c.Name == "Mark")

    // do something -> changes will be persisted by NH when session is disposed
    cust.Instruments.Add(guitar);
}

这就是总体思路。现在,让我更详细地解释一下:

  1. 您可能已经注意到,每个存储库都有自己的界面,并且是通过存储库工厂创建的。使用工厂创建存储库意味着您可以轻松创建模拟存储库工厂,这将创建存储库的任何自定义实现以进行测试。

  2. 每个repo接口都继承自base接口通用接口,IRepo<T>。这允许您在 99% 的情况下使用通用存储库,但仍然留有空间来实现特定于以下内容的自定义查询方法:Customer仅实体:

    public interface IInstrumentRepo : IRepo<Instrument>
    { 
        // nothing to do here
    }
    
    public interface ICustomerRepo : IRepo<Customer>
    {
        // but we'll need a custom method here
        void FindByAddress(string address);
    }
    
    public interface IRepo<T> 
    {
        T GetById(object id);
        T Save(T item);
    }
    
  3. 这意味着在大多数情况下,您的存储库实现将简单地继承自抽象基类(我将其命名为BaseRepo,但这本质上就是你的DataProvider类现在正在做):

    class InstrumentRepo : BaseRepo<Instrument>, IInstrumentRepo
    {
        // no need to implement anything here except pass the session downwards
        public InstrumentRepo(ISession s) : base(s) { }
    }
    
  4. 当被要求时,您的工厂只需要实例化正确的存储库:

    public class RepoFactory : IRepoFactory
    {
         public IInstrumentRepo CreateInstrumentRepo(ISession s)
         {
             return new InstumentRepo(s);
         }
    }
    
  5. 你需要在以下情况中使用单例模式:DAL类来保存工厂(有更好的方法可以使用 DI 来做到这一点,但现在就可以了):

    public static class DAL 
    {
        // repo factory is pretty lightweight, so no need for fancy
        // singleton patterns
    
        private static readonly IRepoFactory _repoFactory = new RepoFactory();
        public static IRepoFactory RepoFactory
        { 
            get { return _repoFactory; }
        }
    }
    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我应该使用泛型来简化我的 DAL 吗? 的相关文章

随机推荐

  • Prism 7 DI 中的 Register、RegisterInstance 与 RegisterSingleton

    我正在尝试在 Prism 7 中注册 DI 服务 我发现以下所有方法都有效 哪一个是正确的方法 各自的情况如何 public class AndroidInitializer IPlatformInitializer static Open
  • 使用 JavaScript 防止表单提交重定向/刷新

    我有一个页面 您可以在一系列文本框 从 php 生成 中输入数据 以及 2 个按钮 GetData 和 SaveData 我希望能够在编辑文本框时按 Enter 键 并且它将执行与单击 SaveData 按钮 onclick onSave
  • 如何将 Google 云端硬盘文件选择器与 Apps 脚本 HTML 服务结合使用

    有谁有使用示例Google 云端硬盘文件选择器与应用程序脚本HTML服务 有可能吗 我想用它来选择文件或使用 AppsScript HTML 服务从云端硬盘上传文件 不幸的是 由于 Caja 的限制 不可能在 HtmlService 中使用
  • 通过 AJAX 和 jQuery 从 PHP 数组获取数据

    我有一个页面如下
  • Android 设备年龄

    是否可以查询android设备的年龄 我想知道用户拥有他的设备多久 电池的寿命可能是一个很好的指标 但我找不到合适的 API 最佳的是第一次设备启动的时间戳 有任何想法吗 没有可靠的方法来找出设备的年龄 但我们可以通过某种方式找出设备的年龄
  • Android - 从远程服务器加载多个图像的有效方法

    我有一个 Android 应用程序 可以从 php 远程服务器检索数据 图像 文本 并将其显示在 GridView 中 我正在使用 Loaders 在后台进行操作 我对图像和文本有单独的连接 因为检索图像需要更长的时间 而且我想立即显示文本
  • 在 ASP.Net MVC 5 应用程序中使用多个 ASP Identity 2.0

    我有一个带有管理区域的 Web 应用程序 用于管理内容 但该网站的其余部分目前由 ASP Identity 保护 该身份验证我的公共用户 现在我需要对一些内部用户进行身份验证才能访问管理区域 这可能吗 您正在寻找的称为 SSO 单点登录 通
  • 在命令行上使用 Android lint 忽略库项目

    我将 Android lint 与 Jenkins 结合使用 需要忽略我的团队未修改的库项目 特别是 Action Bar Sherlock 以便我们可以从 Android lint 获得有用的结果 目前 我正在从命令行启动 lint 并将
  • 如何从 Google Container Engine 访问 HTTP 请求的客户端 IP?

    我正在使用 Google Container Engine 在 docker 容器中运行 Gunicorn flask 服务 我按照教程设置了集群http kubernetes io docs hellonode The REMOTE AD
  • HttpSessionListener.sessionCreated() 未被调用

    我有一个非常简单的 Servlet 和一个非常简单的 HttpSessionListener WebServlet HelloWorld public class HelloWorld extends HttpServlet private
  • 了解 Scala 中的柯里化

    我在理解柯里化概念或至少是 SCALA 柯里化符号时遇到了问题 维基百科说柯里化是一种将带有多个参数的函数的求值转换为求值一系列函数的技术 每个函数都有一个参数 按照这个解释 接下来的两行对于 scala 来说是一样的吗 def addCu
  • 多图片上传

    我正在制作画廊网站 并且想仅使用 PHP 和 MYSQLI 创建一个多图像上传器 我不太擅长编码 因此该网站上的多图像上传的其他示例对我不起作用 这是根据当前用户会话将数据发送到数据库的工作代码 html
  • 使用准备好的语句从 SQL 表中选择 *

    我正在使用准备好的声明SELECT 来自 MySQL 表 我不知道如何使用while row mysqli fetch array stmt 循环并从结果数组中选择项目 这是我的代码 我做错了什么 link mysqli connect h
  • 在php中捕获搜索引擎关键字

    在 awstats 中 我得到了一个表格 其中包含用于查找我的网站的所有关键词和短语 我想自己捕获这一点 但是每个搜索引擎网址的格式都不同 当 google 是引用者时 我可以使用查询字符串中的变量 q 作为搜索词 例如 google co
  • 美国国旗排序优化

    我正在尝试实现美式桶排序 维基百科说 首先计算每个垃圾箱中掉落的物体数量 然后将每个物体放入其桶中 第二阶段 将对象放入适当的桶中时 是否需要使用辅助数组 有没有办法通过在线性时间内交换数组元素来做到这一点 假设你的意思是http en w
  • 将 pygame 表面转换为图像?

    有没有办法将 pygame 表面转换为 png 图像 rgb content pygame surfarray array2d canvas cv2 imwrite file rgb content cv2 IMWRITE PNG COMP
  • 如何以标准/可移植且高效的方式编写int64=int32*int32? [关闭]

    Closed 这个问题需要细节或清晰度 目前不接受答案 有关的 int64 t 的这种处理是 GCC AND Clang 错误吗 我能想到的唯一解决方案是将操作数之一显式转换为int64 迫使产品也至少int64 但如果这样做的话 那么就取
  • 如何将两个 geoJSON 要素集合添加到两个图层组中

    我有两个 geoJSON 要素集合需要添加到地图中 并且我还希望通过图层可见性控制器打开和关闭它们 如下所示http leafletjs com examples layers control html 我怎样才能做到这一点 还有一个非常好
  • 如何批量添加过滤器到文件选择器?

    我正在使用此代码来创建带有批处理的文件选择器Windows 批处理脚本中的文件 文件夹选择器对话框 echo off set dialog about
  • 我应该使用泛型来简化我的 DAL 吗?

    我是 NHibernate 的新手 不太擅长 C 但我正在学习 我有一个DataProvider类 它使用 NHibernate 3 为我的应用程序提供数据 它的结构几乎与Steve Bohlen 的 NHibernate 之夏视频 我注意