简体   繁体   English

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

[英]ASP.NET MVC guidelines for static classes for database access

The way I am utilising the MVC pattern at the moment in my ASP.NET application (using Entity Framework) is as follows: 我在ASP.NET应用程序(使用Entity Framework)中使用MVC模式的方式如下:

1) My Models folder contains all EF entities, as well as my ViewModels 1)My Models文件夹包含所有EF实体以及我的ViewModel

2) I have a Helpers folders where I store classes created for the purposes of the particular application. 2)我有一个Helpers文件夹,我存储为特定应用程序创建的类。

3) In my Helpers folder, I have a static class named MyHelper which contains methods that access the DB using EF. 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) My controllers then call these functions where necessary: 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);
        }
    }
}

I usually encounter comments in SO of the type "don't use a static class, static classes are evil, etc". 我经常在SO中遇到“不使用静态类,静态类是邪恶等”类型的注释。 Would this apply in such a scenario? 这适用于这种情况吗? If yes, why? 如果是,为什么? Is there a better 'structure' my app should follow for best practices and for avoiding such pitfalls? 是否有更好的“结构”我的应用程序应遵循最佳实践并避免此类陷阱?

You can't really use a static class for this. 你不能真正使用静态类。 Your Entity Framework context should have one and only one instance per request. 您的实体框架上下文应该每个请求只有一个实例。 Your methods here instantiate a new context for each method, which is going to cause a ton of problems with Entity Framework. 您的方法在这里为每个方法实例化一个新的上下文,这将导致Entity Framework出现大量问题。

The general concept is fine, but your MyHelper class should be a normal class. 一般概念很好,但你的MyHelper类应该是普通的类。 Add a constructor that takes an instance of your context, and then use a DI container to inject the context into the helper class and the helper class into your controller. 添加一个构造函数,该构造函数接受上下文的实例,然后使用DI容器将上下文注入到辅助类中,将辅助类注入控制器中。

UPDATE UPDATE

Helper 帮手

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();
        }
    }
}

Controller 调节器

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);
        }
    }
}

Then, you just need to set up a DI container to inject everything. 然后,您只需要设置一个DI容器来注入所有东西。 The code for that is entirely dependent on which container you end up going with, so I can't really help you further. 该代码完全取决于您最终使用的容器,因此我无法真正帮助您。 It's usually pretty straight-forward, though. 不过,这通常很简单。 Just read the docs for the container. 只需阅读容器的文档即可。 You'll want to set the life-time scope of your objects to the request. 您需要将对象的生命周期范围设置为请求。 Again, it's different for different containers, but they'll all have some sort of request-scope. 同样,它对于不同的容器是不同的,但它们都有某种请求范围。

I was thinking to add comment to ChrisPratt's answer, but it ended being too long, so let me add separate answer. 我想在ChrisPratt的答案中添加评论,但结尾太长了,所以让我添加单独的答案。

Basically, this is not a life/death choice. 基本上,这不是生死攸关的选择。 Sure, static methods are not as flexible as classes for db access. 当然,静态方法不像db访问类那样灵活。 But they are not bad per-se. 它们本身并不坏 One DbContext per request is a something to aim for . 每个请求一个DbContext是一个目标 It is not an absolute must. 这不是绝对必须的。 It is kinda like dependency injection - you get more flexibility and in turn increase code complexity. 有点像依赖注入 - 你获得更多的灵活性,反过来又增加了代码的复杂性。

Look at these three questions and their answers, by taking into account everything they say, I'm sure you'll be able to answer your question yourself: 看看这三个问题及其答案,考虑到他们所说的一切,我相信你能够自己回答你的问题:

EDIT: Chris left good comment on my answer and I've changed answer a bit to take into account what he said. 编辑:克里斯对我的回答留下了很好的评论,我已经改变了一点回答,考虑到他说的话。

Your idea is correct and I use it always. 你的想法是正确的,我总是使用它。 But the style is like this: 1) For each entity (ie User) we have a static class inside Providers folder. 但风格是这样的:1)对于每个实体(即用户),我们在Providers文件夹中有一个静态类。 In this class we can do general methods (ie create, Get, GetAll , ..) 在这个类中我们可以做一般方法(即create,Get,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) We have another class that is not static.It is inside Models folder. 2)我们有另一个不是静态的类。它在Models文件夹中。 This is the place that we can access to an instance of the entity : 这是我们可以访问实体实例的地方:

    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)
    {
        ...
    }
    ...

} }

I use a static class that has the context injected into a static constructor for the purposes of loading a cache of data that rarely changes. 我使用一个静态类,它将上下文注入到静态构造函数中,以便加载很少更改的数据缓存。 And it (should) be thread safe. 它(应该)是线程安全的。 I hope this helps you, it's very handy in my experience: 我希望这会对你有所帮助,这在我的经历中非常方便:

 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");
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM