I am using ASP.Net / WebForms / Entity Model / Framework 3.5
Here is my project's simple structure
Forms > BLL > DAL ( uses entity model )
Here is my DAL's snippet
public class MyDAL : IDisposable
{
private MyEntities db;
public BaseDAL()
{
db = new MyEntities();
}
public User GetUserByID(int userId)
{
try
{
IQueryable<User> objUser = null;
objUser = from res in db.Users
where res.UserId == userId
select res;
return objUser.FirstOrDefault();
}
catch
{
throw;
}
}
public void Dispose()
{
db.Dispose();
}
}
I call the DAL's function from my BLL like this
public class MyBLL
{
public User GetUserByID(int userId)
{
try
{
using (MyDAL objMyDAL = new MyDAL())
{
return objMyDAL.GetUserByID(userId);
}
}
catch
{
throw;
}
}
}
I am calling the DAL through using block so MyDAL's Dispose event will fire soon after the BLL returns the User object. So at this point ObjectContext instance gets disposed.
Now in my Web Form, I am calling this function like this to get user information and Group details which is a foreign key of user_Group Table in User table
protected void Page_Load(object sender, EventArgs e)
{
MyBLL objMyBll = new MyBLL();
User objUser = objMyBll.GetUserByID(123);
objUser.User_GroupReference.Load(); // ERROR LINE
int groupId = objUser.User_Group.Group_Id;
}
When the ode comes on line objUser.User_GroupReference.Load(); I get this exception
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
How to resolve this? If I do not do db.Dispose(); in my DAL's dispose method, it works fine and no exceptions comes. But if I do not dispose the db object there, when & where should I dispose it?
And how to access Reference Keys after disposed object context?
The exception is fired because lazy loading is fired when you access that navigation property but lazy loading works only within scope of context used to load the entity. If you dispose the context you will lose lazy loading ability. There is no way to use lazy loading after context disposal (except attaching the entity to the new context but it will only work if you detach it from the original context before you dispose it).
In your architecture you must use Include to explicitly load every relation you will need in upper layers. If you want to use lazy loading your context must live for the whole duration of the request. In case of web forms it can be handled for example in BeginRequest and EndRequest event handlers where you create context in BeginRequest and dispose it in EndRequest. The context will be stored in HttpContext.Items. You should get the context from this collection (you can make helper method for that) and pass it to constructor of BLL which will in turn pass it to DAL. Don't access HttpContext.Items from BLL or DAL.
Related
Given the following scenario...
I am concerned about two things...
1) Is it okay to inject a provider into a business model object? - like I did with the Folder implementation because I want to load Sub-folders on demand.
2) Since I am injecting the DbContext in the Sql implementation of IFolderDataProvider, the context could be disposed or it could live on forever, therefore should I instantiate the context in the constructor?
If this design is incorrect then someone please tell me how should business models be loaded.
//Business model.
interface IFolder
{
int Id { get; }
IEnumerable<IFolder> GetSubFolders();
}
class Folder : IFolder
{
private readonly int id_;
private readonly IFolderDataProvider provider_;
public Folder(int id, IFolderDataProvider provider)
{
id_ = id;
provider_ = provider;
}
public int Id { get; }
public IEnumerable<IFolder> GetSubFolders()
{
return provider_.GetSubFoldersByParentFolderId(id_);
}
}
interface IFolderDataProvider
{
IFolder GetById(int id);
IEnumerable<IFolder> GetSubFoldersByParentFolderId(int id);
}
class SqlFolderDataProvider : IFolderDataProvider
{
private readonly DbContext context_;
public SqlFolderDataProvider(DbContext context)
{
context_ = context;
}
public IFolder GetById(int id)
{
//uses the context to fetch the required folder entity and translates it to the business object.
return new Folder(id, this);
}
public IEnumerable<IFolder> GetSubFoldersByParentFolderId(int id)
{
//uses the context to fetch the required subfolders entities and translates it to the business objects.
}
}
Is it okay to inject a provider into a business model object? - like I did with the Folder implementation because I want to load Sub-folders on demand.
Yes, how else would you be able to call the provider and get the data?
However, the suffix DataProvider is very confusing because it is used for the provider that you use to connect to the database. I recommend changing it to something else. Examples: Repository, Context.
Since I am injecting the DbContext in the Sql implementation of IFolderDataProvider, the context could be disposed or it could live on forever, therefore should I instantiate the context in the constructor?
It won't necessarily live on forever. You decide its life span in your ConfigureServices function when you're adding it as a service, so you can change its scope from Singleton to whatever you like. I personally set the scope of my DBContext service to Transient and I also initiate it there with the connection string:
services.AddTransient<IDbContext, DbContext>(options =>
new DbContext(Configuration.GetConnectionString("DefaultDB")));
I then open and close the database connection in every function in my data layer files (you call it provider). I open it inside a using() statement which then guarantees closing the connection under any condition (normal or exception). Something like this:
public async Task<Location> GetLocation(int id) {
string sql = "SELECT * FROM locations WHERE id = #p_Id;";
using (var con = _db.CreateConnection()) {
//get results
}
}
Is it okay to inject a provider into a business model object
Yes if you call it "business" provider :). Actually do not take too serious all this terminology "inject", "provider". Till you pass (to business model layer's method/constructor) interface that is declared on business model layer (and document abstraction leaks) - you are ok.
should I instantiate the context in the constructor?
This could be observed as an abstraction leak that should be documented. Reused context can be corrupted or can be shared with another thread and etc -- all this can bring side effects. So developers tend to do create one "heavy" object like dbContext per "user request" (that usually means per service call using(var context = new DbContext()), but not always, e.g. Sometimes I share it with Authentication Service Call - to check is the next operation allowed for this user). BTW, DbContext is quite quick to create so do not reuse it just for "optimization".
I am implementing a service layer where I need to make sure that certain number of operations across multiple tables happen in a transaction. Here is the work flow.
I get an instance of HistoricalData object that need to be stored in HistoricalData table. This is accomplished in AddHistoricalData method.
I need to retrieve all records from HistoricalData table that include what’s inserted in #1 but can have more records. This is done in ProcessAllData method.
After processing all those records, the results are stored in two other tables, ProcessStatus and ProcessResults. If anything goes wrong, I need to rollback transaction include what’s inserted in operation #1.
This is how its implemented.
public class HistoricalDataService : IHistoricalDataService
{
private MyDbContext dbContext;
public HistoricalDataService(MyDbContext context)
{
this.dbContext = context;
}
void AddHistoricalData(HistoricalData hData)
{
// insert into HistoricalData table
}
void ProcessAllData()
{
// Here we process all records from HistoricalData table insert porcessing results into two other tables
}
void SaveData()
{
this.dbContext.SaveChanges();
}
}
Here is how this class methods are been called.
HistoricalDataService service = new HistoricalDataService (dbcontext);
service.AddHistoricalData(HistoricalData instance);
service.ProcessAllData();
service.SaveData();
Problem with this approach is that whatever is inserted in HistoricalData table during call to AddHistoricalData method is not visible in the ProcessAllData call, because dbContext.SaveChanges is called only at the end. I am thinking that I need to somehow bring transaction scope in here but not sure how to expose a function where that transaction scope should be started?
There are different ways you can do this. Try this (untested, but POC)
public class HistoricalDataService : IHistoricalDataService
{
private DbContext dbContext;
private DbContextTransaction dbContextTransaction;
public HistoricalDataService(DbContext context)
{
this.dbContext = context;
}
void AddHistoricalData(HistoricalData hData)
{
if (dbContext.Database.CurrentTransaction == null)
{
dbContextTransaction = dbContext.Database.BeginTransaction();
}
// insert into HistoricalData table
dbContext.SaveChanges();
}
void ProcessAllData()
{
// Here we process all records from HistoricalData table insert porcessing results into two other tables
}
void Rollback()
{
if (dbContextTransaction != null)
{
this.dbContextTransaction.Rollback();
}
}
void SaveData()
{
this.dbContextTransaction.Commit();
this.dbContextTransaction.Dispose();
}
}
Use as such:
HistoricalDataService service = new HistoricalDataService (dbcontext);
try
{
service.AddHistoricalData(HistoricalData instance);
service.ProcessAllData();
service.SaveData();
}
catch (Exception ex)
{
service.Rollback();
}
I would recommend refactoring the code so that the service has a single method (at a higher level of abstraction) and a single responsibility: handling the use case.
then, the client class would have
private readonly IHistoricalDataService _historicalDataService;
_historicalDataService.RearrangeSeatingArrangement(); //High level abstraction
the purpose of doing the above is to ensure your service class has all the operations occur within a single method, wrapped with either a transaction scope if using raw ADO.NET or context object if using EF. Dont have the client class call three methods when it could just call ONE. This is the purpose of the service class in the first place: Handle use cases and return response to client class ( perhaps controller in your case).
Now, when it comes to ensuring that some part of your code is aware of data that has been persisted, it brings up some additional questions: The commands that occur during ProcessAllData() above, why does it split the data exactly? Can that data that is split be split in memory ( in another domain class), added to the context, and saved in the SaveChanges() method? This would ensure you only make one call to the database ( which is the purpose of Entity Framework Unit Of Work. That is: accumulate changes in the context, adds, delete, udpates, then in one operation, talk with database).
I have a .NET 4 Web Application (Web Forms) that uses Entity Framework to manage data with a MySQL Database. For every Page, I create the context for this Model in the Page_Load.
string connString = ConfigurationManager.ConnectionStrings["dbconnection"].ToString();
MyModel = new MyEntities(connString);
Now, in subsequent actions on the Page I can use MyModel to retrieve and update data. This is clean and simple to me, but I always assumed .NET discarded the previous Page's MyModel when a new page request was made. I realize this may not be the case? and Memory may be being used inefficiently.
I have seen a good case being made for incorporating the using (MyEntities MyModel = new MyEntities (ConfigurationManager.ConnectionStrings["dbconnection"].ToString())) that handles disposal, but this does not seem clean if I have 6+ actions on a Page each needing to recreate the context when called (Not that my current method does any better).
Is there a clean way to create a context once on initial Page Load and dispose of it when a new page is called, non-postback is called, or the user's session ends?
You can override virtual Dispose method of System.Web.UI.Control and dispose of your context there:
public override void Dispose()
{
if (MyModel != null)
MyModel.Dispose();
base.Dispose();
}
Further, you can make MyModel into a property with the context created on demand:
private MyEntities fMyModel = null;
protected MyEntities MyModel
{
get
{
if (fMyModel == null)
{
string connString = ConfigurationManager.ConnectionStrings["dbconnection"].ToString();
fMyModel = new MyEntities(connString);
}
return fMyModel;
}
}
Then, in Dispose work with the field:
public override void Dispose()
{
if (fMyModel != null)
fMyModel.Dispose();
base.Dispose();
}
Furthermore, you can create a base Page class with the above property and Dispose override and inherit your pages from it - then you do not need to repeat this code in all your pages.
The easiest thing to do would be to add a call to Dispose() on your data context in the Unload event on the page.
protected void Page_Unload(object sender, EventArgs e)
{
MyModel.Dispose();
}
A better way that I think should be used as default for web applications (doesn't matter if it is WebForms or MVC is to create one data context per request and dispose it when the request ends. To do that easily you can use an IoC container framework such as Ninject.
If I use Microsoft implementation unit of work from this tutorial:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
public class UnitOfWork : IDisposable
{
private SchoolContext context = new SchoolContext();
private GenericRepository<Department> departmentRepository;
private GenericRepository<Course> courseRepository;
public GenericRepository<Department> DepartmentRepository
{
get
{
if (this.departmentRepository == null)
{
this.departmentRepository = new GenericRepository<Department>(context);
}
return departmentRepository;
}
}
public GenericRepository<Course> CourseRepository
{
get
{
if (this.courseRepository == null)
{
this.courseRepository = new GenericRepository<Course>(context);
}
return courseRepository;
}
}
public void Save()
{
context.SaveChanges();
}
//......
}
I don't need to use transactions when I must add related items? For example when I must add order and order positions to database I don't need to start transaction because if something will go wrong then method Save() won't execute yes? Am I right?
_unitOfWork.OrdersRepository.Insert(order);
_unitOfWork.OrderPositionsRepository.Insert(orderPosition);
_unitOfWork.Save();
??
SaveChanges itself is transactional. Nothing happens at the database level when you call Insert, which based on the tutorial merely calls Add on the DbSet. Only once SaveChanges is called on the context does the database get hit and everything that happened up to that point is sent in one transaction.
You need transactions if you have multiple save changes in one method ... or chain of method calls using the same context.
Then you can roll back over the multiple save changes when your final update fails.
An example would be multiple repositories wrapping crud for an entity under the unit of work (IE a generic class). You may have many functions inserting and saving in each repository. However at the end you may find an issue which causes you to roll back previous saves.
EG in a service layer that needs to hit many repositories and execute a complex operation.
I have a data layer that uses Entity Framework 5 to connects to Sql Server. I also have a LINQ query that gets some data. This query fails when used with Web API. I get the ObjectDisposedException. Here's the query:
using (MyContext container = new myContext())
{
return container.Sales
.Include("Products")
.Where(s => s.Id == id)
.FirstOrDefault();
}
The data layer is a dll, exposed by a business layer, also a dll, which is called by a web api controller. I had assumed that the JSON serializer was lazy loading the includes but none of my fixes have worked. Any ideas? I will update the question as need be if info is missing.
Here is the business layer call to the data layer:
public Sale GetSale(int id)
{
SaleRepository s = new SaleRepository();
return s.GetSale(id);
}
And finally the web api call to the business layer:
public Sale GetSale(int id)
{
SaleManager s = new SaleManager();
return s.GetSale(id);
}
Here is the exception:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
This happens because lazy loading is being performed after your DbContext has been disposed.
To resolve the issue you can disable lazy loading by doing this:
container.Configuration.LazyLoadingEnabled = false;
There must be an issue with lazy loading and the include not working as expected - try changing your code to select the actual object and the included entities.
using (MyContext container = new myContext())
{
var result = container
.Sales
.Include("Products")
.Where(s => s.Id == id)
.FirstOrDefault();
result.Products.ToList();
return result;
}
This is an issue with lazy loading. The more appropriate way to do this is to not dispose the DbContext till the request is done processing. You can do it in a couple of ways,
1) dispose it in the controller. framework ties the controller's life time with the requests life time.
public class MyController : ApiController
{
private SaleManager _saleManager = new SaleManager();
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_saleManager.Dispose();
}
}
}
2) explicitly tie the resource with the request, (useful in cases where you are not inside a controller) using Request.RegisterForDispose()
SaleManager saleManager = new SaleManager();
Request.RegisterForDispose(saleManager);