在 ASP.NET Boilerplate 中使用存储过程、用户自定义函数和视图的自定义仓库





5.00/5 (10投票s)
如何在 ASP.NET Boilerplate 中创建自定义仓库,并在仓库中使用存储过程、视图和用户自定义函数
目录
从 Github 仓库 获取源码。
引言
在本文中,我将解释如何在 ASP.NET Boilerplate 中创建自定义仓库,并使用存储过程、视图和用户自定义函数。要了解更多关于 ASP.NET Boilerplate 框架的信息,请查看其 文档。
要开始使用 ASP.NET Boilerplate 框架,您可以从 这里 下载一个启动模板。我选择了 ASP.NET Core 和多页 Web 应用程序,项目名称为 Acme.PhoneBook。如果您需要帮助设置模板,请参阅 此链接。
在 Visual Studio 2017 中打开下载的解决方案后,我们看到如下解决方案结构

创建自定义仓库
我们将创建一个自定义仓库,使用存储过程、视图和用户自定义函数对 User 实体执行一些基本操作。要实现自定义仓库,只需从您的应用程序特定基础仓库类派生即可。
在领域层 (Acme.PhoneBook.Core) 中实现 interface。
    public interface IUserRepository:  IRepository<User, long> 
    {
      ...
      ...
    }
在基础设施层 (Acme.PhoneBook.EntityFrameworkCore) 中实现仓库。
    public class UserRepository : PhoneBookRepositoryBase<User, long>, IUserRepository 
    {
        private readonly IActiveTransactionProvider _transactionProvider;
        public UserRepository(IDbContextProvider<PhoneBookDbContext> dbContextProvider, 
                              IActiveTransactionProvider transactionProvider)
            : base(dbContextProvider)
        {
            _transactionProvider = transactionProvider;
        }
        
        ...
        ...
    }  
辅助方法 (Helper Methods)
首先,我们创建一些将在其他方法之间共享的辅助方法,以执行一些通用任务
private DbCommand CreateCommand
(string commandText, CommandType commandType, params SqlParameter[] parameters)
{
    var command = Context.Database.GetDbConnection().CreateCommand();
    command.CommandText = commandText;
    command.CommandType = commandType;
    command.Transaction = GetActiveTransaction();
    foreach (var parameter in parameters)
    {
        command.Parameters.Add(parameter);
    }
    return command;
}
private void EnsureConnectionOpen()
{
    var connection = Context.Database.GetDbConnection();
    if (connection.State != ConnectionState.Open)
    {
        connection.Open();
    }
}
private DbTransaction GetActiveTransaction()
{
    return (DbTransaction)_transactionProvider.GetActiveTransaction(new ActiveTransactionProviderArgs
    {
        {"ContextType", typeof(PhoneBookDbContext) },
        {"MultiTenancySide", MultiTenancySide }
    });
}
存储过程
这里有一个存储过程调用,用于获取所有用户的用户名。将其添加到仓库实现 (UserRepository) 中。
public async Task<List<string>> GetUserNames()
{
    EnsureConnectionOpen();
    using (var command = CreateCommand("GetUsernames", CommandType.storedProcedure))
    {
        using (var dataReader = await command.ExecuteReaderAsync())
        {
            var result = new List<string>();
            while (dataReader.Read())
            {
                result.Add(dataReader["UserName"].ToString());
            }
            return result;
        }
    }
}
并在 IUserRepository 中定义 GetUserNames 方法
public interface IUserRepository:  IRepository<User, long> 
{
  ...
  Task<List<string>> GetUserNames();
  ...
}
这是被调用的存储过程
USE [PhoneBookDb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[GetUsernames] 
AS
BEGIN
	SET NOCOUNT ON;
	SELECT UserName FROM AbpUsers
END
GO
现在我们实现了从数据库调用存储过程的函数。让我们在应用程序服务中使用它
public class UserAppService : AsyncCrudAppService<User, UserDto, 
long, PagedResultRequestDto, CreateUserDto, UserDto>, IUserAppService
{
    private readonly IUserRepository _userRepository;
	
    public UserAppService(..., IUserRepository userRepository)
        : base(repository)
    {
        ...
        _userRepository = userRepository;
    }
    
    ...
    
     public async Task<List<string>> GetUserNames()
    {
        return await _userRepository.GetUserNames();
    }
}
这里是另一个示例,它将一个参数发送到存储过程以删除用户
public async Task DeleteUser(EntityDto input)
{
await Context.Database.ExecuteSqlCommandAsync(
    "EXEC DeleteUserById @id",
    default(CancellationToken),
    new SqlParameter("id", input.Id)
);}
用于删除调用的存储过程
USE [PhoneBookDb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[DeleteUserById] 
	@id int  
AS
BEGIN
	SET NOCOUNT ON;
	DELETE FROM AbpUsers WHERE [Id] = @id
END
GO
还有另一个示例,它将一个参数发送到存储过程以更新用户的电子邮件地址
public async Task UpdateEmail(UpdateEmailDto input)
{
await Context.Database.ExecuteSqlCommandAsync(
    "EXEC UpdateEmailById @email, @id",
    default(CancellationToken),
    new SqlParameter("id", input.Id),
    new SqlParameter("email", input.EmailAddress)
);
}
用于 update 方法调用的存储过程
USE [PhoneBookDb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[UpdateEmailById]
@email nvarchar(256),
@id int
AS
BEGIN
	SET NOCOUNT ON;
	UPDATE AbpUsers SET [EmailAddress] = @email WHERE [Id] = @id
END
GO
视图
您可以像这样调用视图
public async Task<List<string>> GetAdminUsernames()
{
    EnsureConnectionOpen();
    using (var command = CreateCommand("SELECT * FROM dbo.UserAdminView", CommandType.Text))
    {
        using (var dataReader = await command.ExecuteReaderAsync())
        {
            var result = new List<string>();
            while (dataReader.Read())
            {
                result.Add(dataReader["UserName"].ToString());
            }
            return result;
        }
    }
} 
此方法的视图
SELECT        *
FROM            dbo.AbpUsers
WHERE        (Name = 'admin')
用户自定义函数
您可以像这样调用用户自定义函数
public async Task<GetUserByIdOutput> GetUserById(EntityDto input)
{
    EnsureConnectionOpen();
    
    using (var command = CreateCommand("SELECT dbo.GetUsernameById(@id)", 
           CommandType.Text, new SqlParameter("@id", input.Id)))
    {
        var username = (await command.ExecuteScalarAsync()).ToString();
        return new GetUserByIdOutput() { Username = username };
    }
}
此方法的用户自定义函数
USE [PhoneBookDb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[GetUsernameById] 
	@id int
)
RETURNS nvarchar(32)
AS
BEGIN
	DECLARE @username nvarchar(32)
	SELECT @username = [UserName] FROM AbpUsers WHERE [ID] = @id
	RETURN @username
END
GO
Github 上的源码
源码已发布在 github 这里。

