【问题标题】:How can I use a stored procedure + repository + unit of work patterns in Entity Framework?如何在实体框架中使用存储过程 + 存储库 + 工作单元模式?
【发布时间】:2020-02-09 05:30:13
【问题描述】:

我首先有带有实体框架代码的 MVC Web 应用程序项目。在这个项目中,我将使用通用存储库和工作单元模式。另外我想使用存储过程来获取列表 by 和 get-list 方法。

如何将存储过程与通用存储库和工作单元模式一起使用?

【问题讨论】:

  • 代码优先的方法还不支持使用存储过程——这是一个将在今年晚些时候推出的实体框架 v6 中推出的功能
  • @marc_s:确切地说,它不支持将存储过程映射 到.NET 方法。 使用它们并将对SqlQuery(以SP名称作为参数)的调用包装到存储库方法中是可能的(msdn.microsoft.com/en-US/data/jj592907)。不是完美的方式,但可能......
  • @Slauma - 实际上,我认为您的意思是它不支持将 EF 模型映射到存储过程。这在 v6 中得到解决。您可以将存储过程映射到对象,并且可以将该存储过程映射到方法(即包装它)。但这主要是代码中的手动过程。
  • @marc_s 我用谷歌搜索,但我找不到任何使用存储过程的示例,感谢您的编辑和评论
  • @marc_s 为什么你说的方式不完美?你有什么理由吗?

标签: entity-framework entity-framework-4 ef-code-first repository-pattern unit-of-work


【解决方案1】:

向您的通用存储库添加

public IEnumerable<T> ExecWithStoreProcedure(string query, params object[] parameters)
{
        return _context.Database.SqlQuery<T>(query, parameters);
}

然后您可以使用任何工作单元/存储库来调用它,例如

IEnumerable<Products> products = 
             _unitOfWork.ProductRepository.ExecWithStoreProcedure(
             "spGetProducts @bigCategoryId",
             new SqlParameter("bigCategoryId", SqlDbType.BigInt) { Value = categoryId } 
      );

【讨论】:

  • 感谢您的回答,但您回答的第二部分中的一个问题我们在通用存储库中实现了 ExecWithStoreProcedure 的方法,我认为通过使用工作单元我不得不说这个 _unitOfWork.ProductRepository.ExecWithStoreProcedure(我们的查询,参数[]);如果我认为错了 peleas 说我
  • 对不起,应该是_unitOfWork.ProductRepository.ExecWithStoreProcedure T 会来自ProductRepository : GenericRepository&lt;Product&gt;
  • 但是如果您的 SP 不返回您的存储库映射到的实体怎么办?例如,我的 SP 可能会返回连接表的数据,或者只是返回一个称为“结果”的整数值。在这种情况下,将上述示例与 Product 实体一起使用是不够的,并且会破坏接口隔离原则。有什么想法吗?
  • 想看看上面的评论有没有回复
  • 这只是调用一个存储过程,这不会遵守 UOW。
【解决方案2】:

您不应该尝试使用具有 UoW/Repository 模式的 SP,因为它们在代码中难以控制并且通常不会映射回相同的实体类型。 UoW 和存储库模式更适合直接使用 ADO.NET 而不是实体框架,因为 EF 已经是存储库模式。在使用 SP 时,我建议将 CQRS 作为更好的模式。详细说明@sunil 的答案和我对此的评论,我创建了一个专门用于处理存储过程的类。它也很容易模拟和测试。

public class ProcedureManager : IProcedureManager
{
    internal DbContext Context;

    public ProcedureManager(DbContext context)
    {
        Context = context;
    }

    //When you expect a model back (async)
    public async Task<IList<T>> ExecWithStoreProcedureAsync<T>(string query, params object[] parameters)
    {
        return await Context.Database.SqlQuery<T>(query, parameters).ToListAsync();
    }

    //When you expect a model back
    public IEnumerable<T> ExecWithStoreProcedure<T>(string query)
    {
        return Context.Database.SqlQuery<T>(query);
    }

    // Fire and forget (async)
    public async Task ExecuteWithStoreProcedureAsync(string query, params object[] parameters)
    {
        await Context.Database.ExecuteSqlCommandAsync(query, parameters);
    }

    // Fire and forget
    public void ExecuteWithStoreProcedure(string query, params object[] parameters)
    {
        Context.Database.ExecuteSqlCommand(query, parameters);
    }
}

【讨论】:

  • 这只是调用一个存储过程,这不会遵守 UOW。
  • 当然不会。 SP 不能遵守 UoW 模式,因为 UoW 模式适用于跟踪更改并批量执行操作的 ORM。该接口是一种有用的方法,可以将 SP 执行与可以跟踪更改的 ORM 逻辑分开。在回答 OP 问题时,您不应使用具有 UoW 模式的 SP。 SP 也确实不适合存储库模式,可能更适合 CQRS。
  • 但问题是:如何使用具有通用存储库和工作单元模式的存储过程?
  • 我的回答是:你不应该。
  • 我认为您应该删除您的编辑,因为您的答案现在包含虚假信息,人们会否决您。如果我是你,我只会包括:带有存储过程的 UOW 需要手动事务处理。 然后像编辑前一样继续回答。
【解决方案3】:

对于通用存储库添加此:

  public IEnumerable<TEntity> GetdataFromSqlcommand(string command, System.Data.SqlClient.SqlParameter[] parameter)
    {
        StringBuilder strBuilder = new StringBuilder();
        strBuilder.Append($"EXECUTE {command}");
        strBuilder.Append(string.Join(",", parameter.ToList().Select(s => $" @{s.ParameterName}")));

        return Context.Set<TEntity>().FromSql(strBuilder.ToString(), parameter);
    }

您只需要发送存储过程名称和参数数组:

public IEnumerable<MainData> GetMainData(Param query)
{
    var param1 = new SqlParameter("param1", query.param1);
    var param2 = new SqlParameter("param2", query.param2);
    return GetdataFromSqlcommand("StoredProcedurename", parameter: new[] { param1, param2 }).ToList();
}

【讨论】:

  • 这只是调用一个存储过程,这不会遵守 UOW。
【解决方案4】:

如果您使用的是 .net core 3.1,则必须解决问题

  1. 您将创建一个将携带存储过程结果的类

  2. 您将从 DBcontext 创建另一个部分类,并将前一个类放入其中

  3. 您将创建IStoredProcedure 接口并使用泛型在存储过程中实现它

  4. 在启动类中注入你的存储过程类

  5. 不要忘记让你的结果类字段,和结果表单存储过程一样

  6. 执行存储过程

实施:

(1)第一步

public class TaskPercents
{
    public long Id { get; set; }
    public long SchoolRepId { get; set; }

    public string Name { get; set; }
}

(2)第二步

public partial class SchoolsPartnershipDBContext : DbContext
{
    public virtual DbSet<TaskPercents> TaskPercents { get; set; }
}

(3)第三步

public interface IStoredProcedure<T> 
{
    public List<T> ExecuteStored(string query);
}

{
    private SchoolsPartnershipDBContext _context;

    public StoredProcedure(SchoolsPartnershipDBContext Context)
    {
        _context = Context;
    }

    public List<T> ExecuteStored(string query)
    {
        //Context = new SchoolsPartnershipDBContext();
        var r = _context.Set<T>().FromSqlRaw(query);
        return r.ToList();
        // return null;
    }
}

最后一步

        var result = _storedProcedure.ExecuteStored("TaskExecPercentForSchoolRep");
        return result.ToList();

【讨论】:

    猜你喜欢
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多