ASP.NET MVC Best Practices for Static Classes for Database Access

The way to use the MVC pattern at the moment in my ASP.NET application (using Entity Framework) is as follows:

1) My Models folder contains all EF objects, as well as my ViewModels

2) I have Helpers folders where classes created for the purposes of a specific application are stored.

3) In my Helpers folder, I have a static class called MyHelper , which contains methods that access the database using 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 = xA, B = xB }).FirstOrDefaultAsync(); } } } } 

4). Then my controllers call these functions:

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

Usually I come across comments in SO like "don't use a static class, static classes are evil, etc.". Will this apply in such a scenario? If so, why? Is there a better β€œstructure” that my application should follow best practices and avoid such errors?

+5
source share
4 answers

You cannot use a static class for this. An Entity Framework context must have one and only one instance for each request. Your methods here create a new context for each method, which will cause a lot of problems with the Entity Framework.

The general concept is fine, but your MyHelper class should be a normal class. Add a constructor that takes an instance of your context, and then use the DI container to insert the context into the helper class and helper class in your controller.

UPDATE

Assistant

 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 = xA, B = xB }).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 configure the DI container to input everything. The code for this completely depends on which container you enter, so I can not help you further. However, this is usually quite straightforward. Just read the documents for the container. You will want to set the life of your objects upon request. Again, it is different for different containers, but everyone will have some kind of query area.

+7
source

I was thinking of adding a comment to ChrisPratt, but it ended too long, so let me add a separate answer.

In principle, this is not a life / death choice. Of course, static methods are not as flexible as classes for accessing db. But they are not bad per-se. One DbContext for the request is that whose purpose is . This is not an absolute necessity. It's kind of like dependency injection - you get more flexibility and, in turn, increase the complexity of the code.

Look at these three questions and their answers, taking into account everything that they say, I'm sure that you yourself can answer your question:

EDIT: Chris left a good comment on my answer, and I changed the answer a bit to take into account what he said.

+1
source

Your idea is right, and I always use it. But the style looks like this: 1) For each object (for example, the user), we have a static class inside the Providers folder. In this class, we can do common methods (i.e. 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 located in the Models folder. This is where we can access the instance of the object:

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

}

0
source

I am using a static class that has a context entered in a static constructor to load a data cache that rarely changes. And it must be thread safe. Hope this helps you, it is very convenient 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"); } 
0
source

Source: https://habr.com/ru/post/1242891/


All Articles