在 ASP.NET Core 和 C# 中实现 Singleton DBManager

作者 : 慕源网 本文共6800个字,预计阅读时间需要17分钟 发布时间: 2022-02-11 共131人阅读

介绍

本文讨论了单例设计模式及其重要性,然后演示了如何使用 dotConnect for PostgreSQL 和 C# 构建单例 DBManager(一个包装对数据库的所有调用的类)。它还讨论了这种方法的优缺点。

先决条件

您将需要以下工具来处理代码示例:

  • Visual Studio 2019 社区版(下载
  • dotConnect for PostgreSQL (下载)

设计模式简介

设计模式可用于解决软件开发中反复出现的问题或复杂性。它们分为三类:结构性、行为性和创造性。创建模式可用于创建类的实例并管理它们。但是,结构模式定义了类型及其关系,并有助于理解实体之间的关系。行为设计模式强调对象如何协作以及如何在它们之间委派职责。

什么是单例设计模式?

顾名思义,单例设计模式将类的实例化限制为仅一个对象。换句话说,一个遵循单例设计模式的类将不允许创建一个以上的实例。

用例

单例设计模式的典型用例是:

  • 日志管理器
  • 数据库管理器
  • 服务注册中心

创建一个新的 ASP.NET Core Web API 项目

之前,我们提到了进入实际场景所需的工具。是时候使用这些工具了。

首先,我们需要创建一个新的 ASP.NET Core Web API 项目:

  1. 打开 Visual Studio 2019。
  2. 单击创建一个新项目
  3. 选择ASP.NET Core Web 应用程序,然后单击下一步。
  4. 指定项目名称和位置以将该项目存储在您的系统中。或者,选中将解决方案和项目放置在同一目录中复选框。
  5. 单击创建。
  6. 在“创建新的 ASP.NET Core Web 应用程序”窗口中,选择API作为项目模板。
  7. 选择ASP.NET Core 5更高版本作为版本。
  8. 禁用Configure for HTTPSEnable Docker Support选项(取消选中它们)。
  9. 由于我们在此示例中不使用身份验证,因此将身份验证指定为No Authentication
  10. 单击创建以完成该过程。

我们将在本文中使用这个项目。

在 ASP.NET Core 和 C# 中实现单例 DBManager

在本节中,我们将实现一个遵循单例设计模式的简单 DBManager 类。

创建数据库表

使用以下脚本创建一个新的数据库表:

CREATE TABLE books(
   book_id serial PRIMARY KEY,
   book_title VARCHAR (255) UNIQUE NOT NULL,
   book_pages INT NOT NULL
);

我们将在本文中使用这个数据库表来存储和检索数据。

安装 NuGet 包

要开始使用,您应该在项目中安装 dotConnect for PostgreSql 包。您可以从 Visual Studio 中的 NuGet 包管理器工具安装它,也可以使用以下命令从 NuGet 包管理器控制台安装它:

PM> Install-Package Devart.Data.PostgreSql

如果安装成功,您就可以开始在应用程序中使用 dotConnect for PostgreSQL。

配置应用程序

您应该在配置文件中指定数据库连接字符串,即 appsettings.json,然后在您的应用程序中读取连接字符串。将 appsettings.json 的默认生成代码替换为以下代码:

{
    "PostgreSqlConnectionString": {
      "DefaultConnection": "UserId = postgres; Password =
       mypass;host=localhost;database=Test;"
  },
  "AllowedHosts": "*"
}

您还应该将 IConfiguration 实例添加到服务容器,以便您可以从应用程序中的任何位置访问它。

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(Configuration);
    services.AddControllers();
}

创建 DBManager 类

为了简单起见,我们将构建一个功能最少的 DBManager。现在,在具有相同主名称且扩展名为 .cs 的文件中创建一个名为 DBmanager 的类,并在其中编写以下代码:

public class DBManager
{
   public int ExecuteNonQuery(string commandText)
   {
      return 1;
   }
   public int ExecuteReader(string commandText)
   {
       return 1;
   }
}

从数据库中读取数据

要使用 dotConnect for PostgreSQL 从数据库中读取数据,我们可以创建一个 PgSqlDataAdapter 实例,然后使用它来填充数据表并返回它。以下代码片段中显示的 ExecuteReader 方法说明了如何实现这一点:

public DataTable ExecuteReader(string commandText)
{
    DataTable dataTable = new DataTable();
    using (PgSqlConnection pgSqlConnection = new
    PgSqlConnection(ConnectionString))
    {
        using (PgSqlCommand pgSqlCommand = new PgSqlCommand())
        {
            pgSqlCommand.CommandText = commandText;
            pgSqlCommand.Connection = pgSqlConnection;
            if (pgSqlConnection.State != ConnectionState.Open)
                pgSqlConnection.Open();
            PgSqlDataAdapter pgSqlDataAdapter = new
                PgSqlDataAdapter(pgSqlCommand);
            pgSqlDataAdapter.Fill(dataTable);
            if(dataTable.Rows.Count > 0)
                return dataTable;
            return null;
        }
    }
}

DBManager 类的 ExecuteReader 方法返回一个 DataTable 的实例。现在,在 DBManager 类中添加以下属性来存储数据库连接字符串:

public string ConnectionString
{
    get;set;
}

从 ExecuteReader 方法返回一个列表

您还可以从 ExecuteReader 方法返回一个列表。创建一个名为 Book 的类,其中包含以下内容:

public class Book
{
    public int book_id { get; set; }
    public string book_title { get; set; }
    public int book_pages { get; set; }
}

将 DBManager 类的 ExecuteReader 方法的源代码替换为以下代码:

public List<Book> ExecuteReader(string commandText)
{
    DataTable dataTable = new DataTable();
    using (PgSqlConnection pgSqlConnection = new
    PgSqlConnection(ConnectionString))
    {
        using (PgSqlCommand pgSqlCommand = new PgSqlCommand())
        {
            pgSqlCommand.CommandText = commandText;
            pgSqlCommand.Connection = pgSqlConnection;
            if (pgSqlConnection.State != ConnectionState.Open)
                pgSqlConnection.Open();
            PgSqlDataAdapter pgSqlDataAdapter = new
            PgSqlDataAdapter(pgSqlCommand);
            pgSqlDataAdapter.Fill(dataTable);
            return dataTable.ToList<Book>();
        }
    }
}

请注意,返回类型已从 DataTable 更改为 List<Book>,以及如何使用名为 ToList<T>() 的扩展方法创建 List<Book> 的实例。

下面的代码清单说明了一个名为 Extensions 的类,其中包含 ToList 扩展方法。

public static class Extensions
{
    public static List<T> ToList<T>(this DataTable dataTable) where T : new()
    {
        List<T> data = new List<T>();
        foreach (DataRow row in dataTable.Rows)
        {
            T item = GetItemFromDataRow<T>(row);
            data.Add(item);
        }
        return data;
    }
    public static T GetItemFromDataRow<T>(DataRow dataRow)
    {
        Type temp = typeof(T);
        T obj = Activator.CreateInstance<T>();
        foreach (DataColumn column in dataRow.Table.Columns)
        {
            foreach (PropertyInfo propertyInfo in temp.GetProperties())
            {
                if (propertyInfo.Name == column.ColumnName)
                    propertyInfo.SetValue(obj, dataRow[column.ColumnName], null);
            }
        }
        return obj;
    }
}

请注意如何将 DataTable 的实例转换为 List<T> 的实例。GetItemFromDataRow 方法返回 Book 类的一个实例。

将数据插入数据库

要使用 dotConnect for PostgreSQL 向 PostgreSQL 数据库插入数据,可以使用以下方法:

public int ExecuteNonQuery(string commandText)
{
    using (PgSqlConnection pgSqlConnection = new
    PgSqlConnection(ConnectionString))
    {
        using (PgSqlCommand pgSqlCommand = new PgSqlCommand())
        {
            pgSqlCommand.CommandText = commandText;
            pgSqlCommand.Connection = pgSqlConnection;
            if (pgSqlConnection.State != ConnectionState.Open)
                pgSqlConnection.Open();
            return pgSqlCommand.ExecuteNonQuery();
        }
    }
}

完整的源代码

DBManager 类的完整源代码如下:

public class DBManager
{
    public string ConnectionString
    {
        get;set;
    }
    public List<Book> ExecuteReader(string commandText)
    {
        DataTable dataTable = new DataTable();
        using (PgSqlConnection pgSqlConnection = new
        PgSqlConnection(ConnectionString))
        {
            using (PgSqlCommand pgSqlCommand = new PgSqlCommand())
            {
                pgSqlCommand.CommandText = commandText;
                pgSqlCommand.Connection = pgSqlConnection;
                if (pgSqlConnection.State != ConnectionState.Open)
                    pgSqlConnection.Open();
                PgSqlDataAdapter pgSqlDataAdapter = new
                   PgSqlDataAdapter(pgSqlCommand);
                pgSqlDataAdapter.Fill(dataTable);
                return dataTable.ToList<Book>();
            }
        }
    }
    public int ExecuteNonQuery(string commandText)
    {
        using (PgSqlConnection pgSqlConnection = new
        PgSqlConnection(ConnectionString))
        {
            using (PgSqlCommand pgSqlCommand = new PgSqlCommand())
            {
                pgSqlCommand.CommandText = commandText;
                pgSqlCommand.Connection = pgSqlConnection;
                if (pgSqlConnection.State != ConnectionState.Open)
                    pgSqlConnection.Open();
                return pgSqlCommand.ExecuteNonQuery();
            }
        }
    }
}

单例 DBManager 类

要使 DBManager 类成为单例类,您应该有一个私有构造函数。私有构造函数会阻止类被扩展或实例化。由于我们不想要这个类的多个实例,因此需要一个私有构造函数。

然后,您应该在静态属性中创建 DBManager 的实例,如下面的代码片段所示:

public class DBManager
{
    private static DBManager instance;
    private DBManager() { }
    public static DBManager Instance
    {
        get
        {
           if (instance == null)
           {
               instance = new DBManager();
           }
           return instance;
        }
   }
   //Other methods removed for brevity
}

由于 DBManager 实例是静态的,因此在应用程序的整个生命周期中您将只有一个实例。您可以访问 DBManager 类及其方法,如以下代码片段所示:

DBManager.Instance.ConnectionString = connectionString;
return DBManager.Instance.ExecuteReader("select * from public.books");

使 DBManager 类线程安全

但是如果你想让它成为线程安全的,这样没有两个线程可以访问临界区呢?为了使此类线程安全,您可以利用 lock 关键字,如以下代码片段所示:

public class DBManager
{
   private static object lockObj = new object();
   private static DBManager instance;
   private DBManager() { }
   public static DBManager Instance
   {
       get
       {
           lock(lockObj)
           {
               if (instance == null)
               {
                   instance = new DBManager();
               }
           }
           return instance;
       }
   }
   //Other methods removed for brevity
}

在控制器类中使用 DBManager 类

您现在可以像这样在控制器类中使用 DBManager 实例:

[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
{
    readonly IConfiguration _configuration;
    readonly string connectionString;
    public BooksController(IConfiguration configuration)
    {
        _configuration = configuration;
        connectionString =
        _configuration["PostgreSqlConnectionString:DefaultConnection"];
    }
    [HttpGet]
    public List<Book> Get()
    {
        DBManager.Instance.ConnectionString = connectionString;
        return DBManager.Instance.ExecuteReader("select * from public.books");
    }
}

概括

DBManager 类用作连接数据库和执行所需的 CRUD 操作的助手。理想情况下,它应该是一个单例,因为您的应用程序中不需要它的多个实例。本文演示了我们如何在 C# 中构建线程安全的 DBManager 类并在 ASP.NET Core 应用程序中使用它。


慕源网 » 在 ASP.NET Core 和 C# 中实现 Singleton DBManager

常见问题FAQ

程序仅供学习研究,请勿用于非法用途,不得违反国家法律,否则后果自负,一切法律责任与本站无关。
请仔细阅读以上条款再购买,拍下即代表同意条款并遵守约定,谢谢大家支持理解!

发表评论

开通VIP 享更多特权,建议使用QQ登录