Answered by:
Adding Using statement causes failure to serialize error

Question
-
User438705957 posted
I am using VS 2017.
I have a Web API 2 controller using EF. I and am slowly introducing repository methods to access the db.
I initialize a new database context whenever the controller is invoked.private CACS_GNS_DataContext db = new CACS_GNS_DataContext();
Whenever I wrap the call to the async repository method in a using statement that disposes of the database context (db), it falls over with the message:
"The type failed to serialize the response body for the content type 'application/json'.
It makes it to the return statement, so the error takes place somewhere in the pipeline after the task is returned to the client.If I don't wrap the call to the repository in a using statement, it works perfectly.
I am not sure why the using statement is interfering with the serialization process.public async Task<IHttpActionResult> GetAuditsAsync(int skip, int take)
{
using (db) { IAuditRepository auditRepository = new AuditRepository(db); var returnTask = await auditRepository.GetListAsync(skip, take, defaultMngrPosition, refClass); if (returnTask.Count() == 0) { return Ok(new { CustomErrorMessage = "No Audits were found that belonged to your designated Manager's position" }); } int allRecordCount = refClass.totalRecords; HttpContext.Current.Response.Headers.Add("X-Paging-TotalRecordCount", allRecordCount.ToString()); return Ok(returnTask); }
}Tuesday, November 27, 2018 11:09 PM
Answers
-
User1120430333 posted
Now, it could be that audits, a collection , is an attached collection to the DBcontect as you do the return audits that could be causing you problems.
In my example, the collection being sent back is a detached collection from the DB context through the WebAPi from the DAL using the DAO pattern
var dtos = new List<DtoProject>()
The query dumps the results into dtos.var audits = new List<audit>() // or however you have to make the collection a detached collection.
audits - contect.audits.ToList() // whatever you have to do and make it work with the detached collection .
try { int qryTake = take == 0 ? 200000 : take; // Changed from IQueryable so it could be awaited var audits = await context.Audits .Where(a => a.Position_Id == defaultMngrPosition && a.Active == true && a.Finalised == false) .OrderBy(l => l.Audit_Due_Date).ThenBy(i => i.Location.Location_Name).Skip(skip).Take(qryTake).ToListAsync(); var varTotalRecords = await context.Audits.Where(a => a.Position_Id == defaultMngrPosition && a.Active == true && a.Finalised == false).ToListAsync(); refClass.totalRecords = varTotalRecords.Count(); return audits;
public List<DtoProject> GetProjectsByUserId(string userid) { var dtos = new List<DtoProject>(); using (var context = new ProjectManagementContext(_options)) { dtos = (from a in context.Projects.Where(a => a.UserId.Contains(userid)) select new DtoProject { ProjectId = a.ProjectId, ClientName = a.ClientName, ProjectName = a.ProjectName, Technology = a.Technology, ProjectType = a.ProjectType, UserId = a.UserId, StartDate = a.StartDate, EndDate = a.EndDate, Cost = a.Cost }).ToList(); } return dtos; }
I see you have a try/catch there doing some logging. You don't have to have any try/catches not in the Repository or the controller too by doing global exception handling. The code you see that I have presented and the controller to have no try/catches anywhere.
What I did is at the bottom of the link, and I just made the adjustment for non Core WebAPI that happens to be in VB..NET it works like a charm.
https://stackify.com/csharp-catch-all-exceptions/
Clint-side ASP.NET MVC code knows the response is not 200 ok, and it too does its global exception handling.
Imports System.Net Imports System.Net.Http Imports System.Web.Http.Filters Imports log4net Public Class CustomExceptionFilter Inherits ExceptionFilterAttribute private ReadOnly _logger As ILog public sub New() _logger = LogManager.GetLogger(GetType(CustomExceptionFilter)) End sub Public Overrides Sub OnException(actionExecutedContext As HttpActionExecutedContext) MyBase.OnException(actionExecutedContext) Dim exceptionMessage as String = String.Empty If IsNothing(actionExecutedContext.Exception.InnerException ) Then exceptionMessage = actionExecutedContext.Exception.Message _ & " " & actionExecutedContext.Exception.StackTrace Else exceptionMessage = actionExecutedContext.Exception.Message _ & " " & actionExecutedContext.Exception.InnerException.Message _ & " " & actionExecutedContext.Exception.StackTrace End If _logger.Error(exceptionMessage) dim response = New HttpResponseMessage(HttpStatusCode.InternalServerError)With {.Content = New StringContent(“An unhandled exception was thrown by service.”), .ReasonPhrase = "Internal Server Error.Please Contact your Administrator."} actionExecutedContext.Response = response End Sub End Class
Imports System.Web.Http Imports DAL Imports Entities Namespace Controllers <CustomExceptionFilter> Public Class ProjectController Inherits ApiController Private ReadOnly _daoproject As IDaoProject public sub New (daoproject As IDaoProject) _daoproject = daoproject End sub <HttpGet> <ActionName("GetProjectById")> public Function GetProjectById(ByVal id As Int32) As DtoProject return _daoproject.GetProjectById(id) End Function <HttpGet> <ActionName("GetProjectsByUserId")> public Function GetProjectsByUserId(ByVal userid As String) As List(Of DtoProject) return _daoproject.GetProjectsByUserId(userid) End Function <HttpPost> <ActionName("CreateProject")> public sub CreateProject(ByVal dto As DtoProject) Call _daoproject.CreateProject(dto) End sub <HttpPost> <ActionName("UpdateProject")> public sub UpdateProject(ByVal dto As DtoProject) Call _daoproject.UpdateProject(dto) End sub <HttpPost> <ActionName("DeleteProject")> public sub DeleteProject(ByVal dto As DtoId) Call _daoproject.DeleteProject(dto.Id) End sub End Class End Namespace
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Thursday, November 29, 2018 5:48 AM
All replies
-
User1120430333 posted
To me, the definition of returntask would be out of using scope along with everything else that has nothing to do with db context and the using statment.
using (db) { IAuditRepository auditRepository = new AuditRepository(db); returnTask = await auditRepository.GetListAsync(skip, take, defaultMngrPosition, refClass); }
Wednesday, November 28, 2018 12:25 AM -
User438705957 posted
Thanks for your answer DA294.
If I place the using statement in the repository method ( auditRepository.GetListAsync(..)), and remove it from the controller method, I get the same result.
So I am not sure now how to deploy using and dispose of the context properly.Wednesday, November 28, 2018 2:53 AM -
User1120430333 posted
Can you show what yo have done?
Wednesday, November 28, 2018 7:29 AM -
User438705957 posted
The controller method code is as shown in the first part of the answer.
The repository method code is this:
public class AuditRepository : IAuditRepository, IDisposable { private CACS_GNS_DataContext context; public AuditRepository(CACS_GNS_DataContext context) { this.context = context; } public async Task<List<Audit>> GetListAsync(int skip, int take, int defaultMngrPosition, refClass refClass) { // Returns all Audits for the default position with skip,take settings // refClass is passed to get around the limitation of not being able to have out parameters or parameters passed by reference in an asynch method try { int qryTake = take == 0 ? 200000 : take; // Changed from IQueryable so it could be awaited var audits = await context.Audits .Where(a => a.Position_Id == defaultMngrPosition && a.Active == true && a.Finalised == false) .OrderBy(l => l.Audit_Due_Date).ThenBy(i => i.Location.Location_Name).Skip(skip).Take(qryTake).ToListAsync(); var varTotalRecords = await context.Audits.Where(a => a.Position_Id == defaultMngrPosition && a.Active == true && a.Finalised == false).ToListAsync(); refClass.totalRecords = varTotalRecords.Count(); return audits; } catch (Exception ex) { string exceptionMessage = ex.InnerException == null ? "Exception Message: " + ex.Message : "Exception Message: " + ex.Message + "Inner Exception Message: " + ex.InnerException.Message; string currentMethod = System.Reflection.MethodBase.GetCurrentMethod().Name.ToString(); WriteTransactions.WriteTran("Error occurred in Repository Method: " + currentMethod, true, exceptionMessage); throw ex; } } private bool disposed = false; protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { context.Dispose(); } } this.disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }
Thursday, November 29, 2018 4:20 AM -
User1120430333 posted
Now, it could be that audits, a collection , is an attached collection to the DBcontect as you do the return audits that could be causing you problems.
In my example, the collection being sent back is a detached collection from the DB context through the WebAPi from the DAL using the DAO pattern
var dtos = new List<DtoProject>()
The query dumps the results into dtos.var audits = new List<audit>() // or however you have to make the collection a detached collection.
audits - contect.audits.ToList() // whatever you have to do and make it work with the detached collection .
try { int qryTake = take == 0 ? 200000 : take; // Changed from IQueryable so it could be awaited var audits = await context.Audits .Where(a => a.Position_Id == defaultMngrPosition && a.Active == true && a.Finalised == false) .OrderBy(l => l.Audit_Due_Date).ThenBy(i => i.Location.Location_Name).Skip(skip).Take(qryTake).ToListAsync(); var varTotalRecords = await context.Audits.Where(a => a.Position_Id == defaultMngrPosition && a.Active == true && a.Finalised == false).ToListAsync(); refClass.totalRecords = varTotalRecords.Count(); return audits;
public List<DtoProject> GetProjectsByUserId(string userid) { var dtos = new List<DtoProject>(); using (var context = new ProjectManagementContext(_options)) { dtos = (from a in context.Projects.Where(a => a.UserId.Contains(userid)) select new DtoProject { ProjectId = a.ProjectId, ClientName = a.ClientName, ProjectName = a.ProjectName, Technology = a.Technology, ProjectType = a.ProjectType, UserId = a.UserId, StartDate = a.StartDate, EndDate = a.EndDate, Cost = a.Cost }).ToList(); } return dtos; }
I see you have a try/catch there doing some logging. You don't have to have any try/catches not in the Repository or the controller too by doing global exception handling. The code you see that I have presented and the controller to have no try/catches anywhere.
What I did is at the bottom of the link, and I just made the adjustment for non Core WebAPI that happens to be in VB..NET it works like a charm.
https://stackify.com/csharp-catch-all-exceptions/
Clint-side ASP.NET MVC code knows the response is not 200 ok, and it too does its global exception handling.
Imports System.Net Imports System.Net.Http Imports System.Web.Http.Filters Imports log4net Public Class CustomExceptionFilter Inherits ExceptionFilterAttribute private ReadOnly _logger As ILog public sub New() _logger = LogManager.GetLogger(GetType(CustomExceptionFilter)) End sub Public Overrides Sub OnException(actionExecutedContext As HttpActionExecutedContext) MyBase.OnException(actionExecutedContext) Dim exceptionMessage as String = String.Empty If IsNothing(actionExecutedContext.Exception.InnerException ) Then exceptionMessage = actionExecutedContext.Exception.Message _ & " " & actionExecutedContext.Exception.StackTrace Else exceptionMessage = actionExecutedContext.Exception.Message _ & " " & actionExecutedContext.Exception.InnerException.Message _ & " " & actionExecutedContext.Exception.StackTrace End If _logger.Error(exceptionMessage) dim response = New HttpResponseMessage(HttpStatusCode.InternalServerError)With {.Content = New StringContent(“An unhandled exception was thrown by service.”), .ReasonPhrase = "Internal Server Error.Please Contact your Administrator."} actionExecutedContext.Response = response End Sub End Class
Imports System.Web.Http Imports DAL Imports Entities Namespace Controllers <CustomExceptionFilter> Public Class ProjectController Inherits ApiController Private ReadOnly _daoproject As IDaoProject public sub New (daoproject As IDaoProject) _daoproject = daoproject End sub <HttpGet> <ActionName("GetProjectById")> public Function GetProjectById(ByVal id As Int32) As DtoProject return _daoproject.GetProjectById(id) End Function <HttpGet> <ActionName("GetProjectsByUserId")> public Function GetProjectsByUserId(ByVal userid As String) As List(Of DtoProject) return _daoproject.GetProjectsByUserId(userid) End Function <HttpPost> <ActionName("CreateProject")> public sub CreateProject(ByVal dto As DtoProject) Call _daoproject.CreateProject(dto) End sub <HttpPost> <ActionName("UpdateProject")> public sub UpdateProject(ByVal dto As DtoProject) Call _daoproject.UpdateProject(dto) End sub <HttpPost> <ActionName("DeleteProject")> public sub DeleteProject(ByVal dto As DtoId) Call _daoproject.DeleteProject(dto.Id) End sub End Class End Namespace
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Thursday, November 29, 2018 5:48 AM -
User438705957 posted
Thanks DA294, that is above and beyond and by far the most extensive answer I have had in any forum.<br>
I am going to study you’re code and create a solution of my own. I cleary have a lot to learn about the Repository pattern.<br>
Yes, I do use a custom exception filter in my other controller methods, with no try..catch. I haven’t got around to adding here yet.Saturday, December 1, 2018 8:17 AM -
User1120430333 posted
In using the repository pattern, you should understand it, becuase the repositroy is a domain object, a business object in Domain Driven Design, that uses a mapping layer for CRUD operations, which originated in DDD.
https://martinfowler.com/eaaCatalog/repository.html
<copied>
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction.
<end>
https://programmingwithmosh.com/entity-framework/common-mistakes-with-the-repository-pattern/
Just in looking at the repository object that you have presented, it is void of any business logic or anemic in using business logic. It's just a glorified DAO.
https://blog.sapiensworks.com/post/2012/11/01/Repository-vs-DAO.aspx
<copied>
A repository sits at a higher level. It deals with data too and hides queries and all that but, a repository deals with** business/domain objects**. That's the difference. A repository will use a DAO to get the data from the storage and uses that data to restore a business object. Or it will take a business object and extract the data that will be persisted. If you have an anemic domain, the repository will be just a DAO. Also when dealing with querying stuff, most of the time a specialized query repository is just a DAO sending DTOs to a higher layer.
<end>
In the code I have presented, the DAO is sitting behind the WebAPI. The DAO is in the classlib project called the DAL (Data Access Layer), and the WebAPI has reference to it. I am also using the DTO pattern and all projects, including client-side projects) have refernce to the DTO(s) that are kept in a classlib project called Entities.
https://en.wikipedia.org/wiki/Data_access_object
https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm
https://www.codeproject.com/Articles/1050468/Data-Transfer-Object-Design-Pattern-in-Csharp
Example...
mports Entities Public Interface IDaoProject Function GetProjectById(ByVal id As Int32) As DtoProject Function GetProjectsByUserId(ByVal userid As String) As List(Of DtoProject) Sub CreateProject(ByVal dto As DtoProject) Sub UpdateProject(ByVal dto As DtoProject) Sub DeleteProject(ByVal id As Int32) End Interface ================================================== Imports System.Data.Entity Imports Entities Public Class DaoProject Implements IDaoProject Private ReadOnly context As ProjectManagementEntities public sub New (dbcontext As ProjectManagementEntities) context = dbcontext End sub Public Function GetProjectById(ByVal id As Int32) As DtoProject Implements IDaoProject.GetProjectById Dim dto = New DtoProject() Dim project = (context.Projects.Where(Function(a) a.ProjectId = id)).SingleOrDefault() If IsNothing(project) Then Return dto End If dto.ProjectId = project.ProjectId dto.ClientName = project.ClientName dto.ProjectName = project.ProjectName dto.Technology = project.Technology dto.ProjectType = project.ProjectType dto.UserId = project.UserId dto.StartDate = project.StartDate dto.EndDate = project.EndDate dto.Cost = project.Cost Return dto End Function Public Function GetProjectsByUserId(ByVal userid As String) As List(Of DtoProject) Implements IDaoProject.GetProjectsByUserId Dim dtos = New List(Of DtoProject) dtos = (From a In context.Projects.Where(Function(a) a.UserId.Contains(userid)) Select New DtoProject With {.ProjectId = a.ProjectId, .ClientName = a.ClientName, .ProjectName = a.ProjectName, .Technology = a.Technology, .ProjectType = a.ProjectType, .UserId = a.UserId, .StartDate = a.StartDate, .EndDate = a.EndDate, .Cost = a.Cost}).ToList() Return dtos End Function Public Sub CreateProject(ByVal dto As DtoProject) Implements IDaoProject.CreateProject Dim project = New Project() With {.ClientName = dto.ClientName, .ProjectName = dto.ProjectName, .Technology = dto.Technology, .ProjectType = dto.ProjectType, .UserId = dto.UserId, .StartDate = dto.StartDate, .EndDate = dto.EndDate, .Cost = dto.Cost} context.Projects.Add(project) context.SaveChanges() End Sub Public Sub UpdateProject(ByVal dto As DtoProject) Implements IDaoProject.UpdateProject Dim project = New Project() project = (context.Projects.Where(Function(a) a.ProjectId = dto.ProjectId)).SingleOrDefault() If Not IsNothing(project) Then project.ClientName = dto.ClientName project.ProjectName = dto.ProjectName project.Technology = dto.Technology project.ProjectType = dto.ProjectType project.UserId = dto.UserId project.StartDate = dto.StartDate project.EndDate = dto.EndDate project.Cost = dto.Cost End If If IsNothing(project) Then Exit Sub End If context.Entry(project).State = EntityState.Modified context.SaveChanges() End Sub Public Sub DeleteProject(ByVal id As Int32) Implements IDaoProject.DeleteProject Dim project As Project project = (context.Projects.Where(Function(a) a.ProjectId = id)).Include("Tasks").SingleOrDefault() If IsNothing(project) Then Exit Sub End If For i As Integer = 0 To project.Tasks.Count - 1 Dim task = project.Tasks(i) context.Entry(task).State = EntityState.Deleted Next context.Entry(project).State = EntityState.Deleted context.SaveChanges() End Sub End Class
Public Class DtoProject Public Property ProjectId As Int32 Public Property ClientName As String Public Property ProjectName As String Public Property Technology As String Public Property ProjectType As String Public Property UserId As String Public Property StartDate As DateTime Public Property EndDate As DateTime? Public Property Cost As Decimal End Class
Saturday, December 1, 2018 1:06 PM