locked
Adding Using statement causes failure to serialize error RRS feed

  • 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