【问题标题】:Access DbContext from Inside of a Class从类内部访问 DbContext
【发布时间】:2021-04-27 06:49:46
【问题描述】:

在 Controller 中,我们可以这样做将 DbContext 注入到 Controller 中。这很好用。

public class HockeyPlayer : Controller
{
    private readonly MyDbContext _context;

    public HockeyPlayer(MyDbContext context)
    {
        _context = context;
    }
}

但是;我从未做过的一件事是在类文件中注入(或访问)DbContext。

例如,假设我有一个名为 HockeyPlayerData 的类。

public class HockeyPlayerData
{
    private readonly MyDbContext _context;

    public HockeyPlayerData(MyDbContext context)
    {
        _context = context;
    }
}

当我尝试使用 _context 时会引发错误(对象引用未设置为对象的实例)。

var db_query = _context.Set<HockeyPlayer>().FromSqlRaw("execute dbo.GetHockeyPlayer {0}, {1}", 0, 
userid).ToList();

我可以让它工作的唯一方法是将 DbContext 传递给类或每个方法。 例如:

HockeyPlayerData hp = new HockeyPlayerData(_context);
hp.GetPlayerData();

或者

HockeyPlayerData hp = new HockeyPlayerData();
hp.GetPlayerData(_context);

我真的不喜欢这样,但我也不确定这是否一定是错误的。似乎每个班级 应该能够像控制器一样轻松访问数据库。我已经阅读了很多关于此的内容,但我还没有真正得到任何地方。有没有简单的方法可以做到这一点?

你们中的一些人想要更多的代码。这有帮助吗?我的意思是,基本上就是这样。你可以看到我正在尝试在顶部进行注射。但是当我运行应用程序时,当我尝试在 _dbcontext.Set 处使用数据(对象引用未设置为对象实例)填充模型(曲棍球运动员)时出现错误。

public class HockeyPlayerData
{
    public static MyDbContext _dbcontext;

    public HockeyPlayerData(MyDbContext db)
    {
        _dbcontext = db;
    }


    public string GetHockeyPlayerData(string userid)
    {
        string firstname = "";
        var query = _dbcontext.Set<HockeyPlayer>().FromSqlRaw("execute 
        dbo.GetHockeyPlayer {0}, {1}", 0, Convert.ToInt32(userid)).ToList();

        for (int i = 0; i < query.Count; i++)
        {
            firstname = query[i].FirstName;
        }

    return firstname;
    }
}

【问题讨论】:

  • 请发帖minimal reproducible example。将 DbContext 传递给服务是 100% 可能的,而且非常普遍。
  • 同意@CamiloTerevinto,我们需要一些示例代码。您的 _context.Set 示例与上面的 HockeyPlayerData 不匹配,因此很难确切知道您要完成什么。
  • 我更新了帖子。看看是否有帮助。请记住,如果我从控制器传入 DbCoontext,它工作正常。但是当我尝试注入时,它没有。
  • 请添加一个完整的例子。首先如何获得 HockeyPlayerData 的实例?此外,该代码无法编译,因此edit 您的问题并包含从控制器到服务和调用的实际代码

标签: c# entity-framework .net-core dbcontext


【解决方案1】:

您应该使用依赖注入来解析您的类,而不是使用new 运算符创建实例。这是一个使用ServiceProvider 进行依赖注入的示例:

public class HockeyPlayerData : IHockeyPlayerData
{
 private readonly MyDbContext _context;

    public HockeyPlayerData(MyDbContext context)
    {
        _context = context;
    }
}
var provider = ServiceCollection()
    .AddTransient<IHockeyPlayerData, HockeyPlayerData >()
    .AddDbContext<MyDbContext>(options => options.UseSqlServer(connectionString))
    .BuildServiceProvider();

var hockeyPlayerData = provider.GetService<IHockeyProviderData>();

我建议阅读Dependency Inversion Principal。另外,从您使用实体框架的方式(使用FromSQLRaw 检索数据)来看,我认为您可能会受益于阅读实体框架的Getting Started Tutorial

如果您的 MyDbContext 类有一个 DBSet,例如:

class MyDbContext
{
    public DbSet<PlayerData> PlayerData { get; set; }
}

您可以使用 EF 进行查询,而不是生成原始 SQL。例如:

var data = await context.PlayerData.Where(p => p.PlayerDataID == id).ToListAsync();

【讨论】:

  • 你怎么知道这不是 OP 正在做的事情?这只是猜测,第二个代码 sn -p 已经显示MyDbContext 正在被注入。
  • 是的,它显示它正在被注入,但它会引发错误(对象引用未设置为对象的实例)。如果我传入 DbContext 那就没问题了。注入的方式就像注入它一样,但是它是空白的。
【解决方案2】:

有多种方法可以绕过依赖容器,如果您需要更多地控制 DbContext 的内存处理,有时是必要的。

通常你会有一个看起来像这样的初创公司, 常规示例:

// startup.cs - OnConfigure Method 
services.AddDbContext<AppDbContext>(options => options.UseSqlServer("/*Connection string here*/"));
services.AddScoped<IAppService, AppService>();

// class which uses the dbcontext
public AppService : IService
{
  private readonly AppDbContext _ctx; 

  public AppService(DbContext context)
  { 
     // because the DI container have both the dbcontext and the appservice registered it knows how to resolve the dependencies for the service. 
     _ctx = context;
  }

  public void DoStuff()
  { 
    // _ctx is not null at this point.
  }
}

// DbContext class
public class AppDbContext : DbContext
{
    // call to base constructor with options built from startup.cs class.
    public AppDbContext(DbContextOptions<AppDbContext> options):base(options)
    {}
}

替代模型,用于边缘情况


// class that uses DbContext without using standard Dependency injection

public class AppService : IAppService 
{
    private readonly AppDbContext _ctx;

    public AppService()
    {
      _ctx = new AppDbContext(@"/*connectionstring here*/");
    }

    public void DoStuff()
    {
      // _ctx was during instantiation of class manually injected. 
    }
}

// DbContextClass
public class AppDbContext 
{
   public AppDbContext(string connectionString): base()
   {
     var builder = new DbContextOptionsbuilder<AppDbContext>();
     builder.UseSqlServer(connectionString);

     OnConfiguring(builder);
   }
}

但是对于您的用例,听起来您不应该将上下文从控制器解析到您的 hockeyplayerdata / 存储库,但更多应该将您的 hockeyplayer 数据注册到 servicecollection,并在控制器中引用数据服务。并让容器照顾它。也没有对您的 DbContext 的静态引用。默认情况下,DbContext 注册了 Scoped Service Lifetime,将其添加为单通是另一种混乱程度,可能会导致并发问题。

// startup.cs onconfigure method 
services.AddScoped<HockeyPlayerData>();

// data repository 
public class HockeyPlayerData 
{
  
  private readyonly AppDbContext _ctx;
  public HockeyPlayerData(AppDbContext context)
  {
    _ctx = context;
  }

}

// in your controller 
public class MyController 
{
  private readonly HockeyPlayerData _dataService;
  public MyController(HockeyPlayerData dataService)
  {
    // again letting the DI container taking care of resolving services.
    _dataService = dataService;
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-25
    • 2018-01-13
    • 1970-01-01
    • 2014-02-11
    • 1970-01-01
    • 2011-11-10
    • 2011-01-02
    相关资源
    最近更新 更多