ASP.NET 用于数据库访问的静态类的 MVC 指南


目前

我在 ASP.NET 应用程序中使用MVC模式的方式(使用实体框架(如下:

1( 我的 Models 文件夹包含所有 EF 实体,以及我的视图模型

2(我有一个Helpers文件夹,用于存储为特定应用程序目的创建的类。

3(在我的Helpers文件夹中,我有一个名为MyHelper的静态类,其中包含使用EF访问数据库的方法。

namespace myApp.Helpers
{
    public static class MyHelper
    {
        public static async Task<ProductVM> GetProductAsync(int productId)
        {
            using (var context = new myEntities())
            {
                return await context.vwxProducts.Where(x => x.ProductId == productId).Select(x => new ProductVM { A = x.A, B = x.B }).FirstOrDefaultAsync();
            }
        }
    }
}

4( 然后,我的控制器在必要时调用这些函数:

namespace myApp.Controllers
{
    public class ProductController : Controller
    {
        [HttpGet]
        public async Task<ActionResult> Index(int productId)
        {
            var productVM = await MyHelper.GetProductAsync(productId);
            return View(productVM);
        }
    }
}

我通常在 SO 中遇到"不要使用静态类,静态类是邪恶的等"类型的评论。这是否适用于这种情况?如果是,为什么?我的应用是否应该遵循更好的"结构"来获得最佳实践并避免此类陷阱?

您实际上不能为此使用静态类。实体框架上下文的每个请求应有一个且只有一个实例。您在此处的方法为每个方法实例化一个新的上下文,这将导致实体框架出现大量问题。

一般概念很好,但MyHelper类应该是普通类。 添加一个采用上下文实例的构造函数,然后使用 DI 容器将上下文注入帮助程序类,并将帮助程序类注入控制器。

更新

助手

namespace myApp.Helpers
{
    public class MyHelper
    {
        private readonly DbContext context;
        public MyHelper(DbContext context)
        {
            this.context = context;
        }
        public async Task<ProductVM> GetProductAsync(int productId)
        {
            return await context.vwxProducts.Where(x => x.ProductId == productId).Select(x => new ProductVM { A = x.A, B = x.B }).FirstOrDefaultAsync();
        }
    }
}

控制器

namespace myApp.Controllers
{
    public class ProductController : Controller
    {
        private readonly MyHelper myHelper;
        public ProductController(MyHelper myHelper)
        {
            this.myHelper = myHelper;
        }
        [HttpGet]
        public async Task<ActionResult> Index(int productId)
        {
            var productVM = await myHelper.GetProductAsync(productId);
            return View(productVM);
        }
    }
}

然后,您只需要设置一个 DI 容器来注入所有内容。该代码完全取决于您最终使用的容器,因此我无法进一步帮助您。不过,这通常非常简单。只需阅读容器的文档。您需要将对象的生存期范围设置为请求。同样,对于不同的容器,情况有所不同,但它们都有某种请求范围。

我想在克里斯普拉特的答案中添加注释,但结果太长了,所以让我添加单独的答案。

基本上,这不是生死抉择。当然,静态方法不如数据库访问类灵活。但它们本身并不坏。每个请求一个 DbContext 是一个目标。这不是绝对必须的。这有点像依赖注入 - 您可以获得更大的灵活性,从而增加代码复杂性。

看看这三个问题和他们的答案,考虑到他们所说的一切,我相信你自己就能回答你的问题:

  • 为什么要使用静态方法进行数据库访问
  • 何时在 C# 中使用静态类
  • 每个 Web 请求一个 DbContext...为什么?

编辑:克里斯对我的回答留下了很好的评论,我已经改变了答案,以考虑到他说的话。

你的想法是正确的,我总是使用它。但风格是这样的:1(对于每个实体(即用户(,我们在提供者文件夹中都有一个静态类。在此类中,我们可以执行一般方法(即创建,获取,GetAll,..(

    public static class Users
{
    public static IEnumerable<kernel_Users> GetAll()
    {
        Kernel_Context db = new Kernel_Context();
        return db.kernel_Users;
    }
public static kernel_Users Get(int userId)
    {
        Kernel_Context db = new Kernel_Context();
        return db.kernel_Users.Where(c => c.UserId == userId).FirstOrDefault();
    }
    ...
}

2(我们有另一个非静态的类。它位于"模型"文件夹中。这是我们可以访问实体实例的地方:

    public partial class kernel_Users
{
    [Key]
    public int UserId { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    [NotMapped]
    public string FullName
    {
        get
        {
            return FirstName + " " + LastName;
        }
    }
    public bool Delete(out string msg)
    {
        ...
    }
    ...

}

我使用一个静态类,该类将上下文注入到静态构造函数中,目的是加载很少更改的数据缓存。它(应该(是线程安全的。我希望这对您有所帮助,根据我的经验,这非常方便:

 public static class StaticCache<T>  where T: class 
 {
    private static List<T> dbSet;
    public static Dictionary<string, List<T>> cache = new Dictionary<string, List<T>>();
    private static readonly object Lock = new object();
    public static void Load(DbContext db, string connStr, string tableName)
    {
        lock (Lock)
        {
            try
            {
                if (connStr != null)
                {
                    using (db)
                    {
                        dbSet = db.Set<T>().ToList();                            
                        cache.Add(tableName, dbSet);
                    }
                }
            }
            catch { }
        }
    }
 }
 void Testit() 
 {
    var context = new YourContextSubClass(connStr);       
    StaticCache<TableEntity>.Load(context, connstr, "tableEntityNameString");
 }

最新更新