Harry
Harry

Reputation: 11

Error while adding entity object to DBContext object called from Parallel.Foreach loop

Error : If the event originated on another computer, the display information had to be saved with the event.

The following information was included with the event:

Object reference not set to an instance of an object. at System.Data.Objects.ObjectStateManager.DetectConflicts(IList1 entries) at System.Data.Objects.ObjectStateManager.DetectChanges() at System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean force) at System.Data.Entity.Internal.Linq.InternalSet1.ActOnSet(Action action, EntityState newState, Object entity, String methodName) at System.Data.Entity.Internal.Linq.InternalSet1.Add(Object entity)
at System.Data.Entity.DbSet
1.Add(TEntity entity) at ESHealthCheckService.BusinessFacade.BusinessOperationsLayer.AddErrorToDbObject(Exception ex, Server serverObj, Service windowsServiceObj)

the message resource is present but the message is not found in the string/message table

public void CheckForServerHealth()
        {
            businessLayerObj.SetStartTimeWindowsService();
            List<ServerMonitor> serverMonitorList = new List<ServerMonitor>();
            serverList = businessLayerObj.GetServerList();
            Parallel.ForEach(
          serverList,
          () => new List<ServerMonitor>(),
          (server, loop, localState) =>
          {
              localState.Add(serverStatus(server, new ServerMonitor()));
              return localState;
          },
              localState =>
              {
                  lock (serverMonitorList)
                  {
                      foreach (ServerMonitor serverMonitor in localState)
                      {
                          serverMonitorList.Add(serverMonitor);
                      }
                  }
              });
            businessLayerObj.SaveServerHealth(serverMonitorList);
        }


public ServerMonitor serverStatus(Server serverObj, ServerMonitor serverMonitorObj)
        {
            if (new Ping().Send(serverObj.ServerName, 30).Status == IPStatus.Success)
            {
                serverMonitorObj.Status = true;
                try
                {
                    PerformanceCounter cpu = new PerformanceCounter("Processor", "% Processor Time", "_Total", serverObj.ServerName);
                    serverMonitorObj.CPUUtlilization = (cpu.NextValue());
                }
                catch (Exception ex)
                {
                    businessLayerObj.AddErrorObjectToStaticList(ex, serverObj);
                }

                serverMonitorObj.ServerID = serverObj.ServerID;
                try
                {
                    string[] diskArray = serverObj.DriveMonitor.ToString().Split(':');
                    if (diskArray != null && diskArray.Contains("NA"))
                    {
                        serverMonitorObj.DiskSpace = "NA";
                    }
                    else
                    {
                        serverMonitorObj.DiskSpace = ReadFreeSpaceOnNetworkDrives(serverObj.ServerName, diskArray);
                    }
                }
                catch (Exception ex)
                {
                    businessLayerObj.AddErrorObjectToStaticList(ex, serverObj);
                }

                serverMonitorObj.CreatedDateTime = DateTime.Now;
            }
            else
            {
                serverMonitorObj.Status = false;
                serverMonitorObj.ServerID = serverObj.ServerID;
                //return serverMonitorObj;
            }

            return serverMonitorObj;
        }



public void AddErrorObjectToStaticList(Exception ex, Server serverObj = null, Service windowsServiceObj = null)
        {
            EShelathLoging esLogger = new EShelathLoging();

            esLogger.CreateDatetime = DateTime.Now;
            if (ex.InnerException != null)
            {
                esLogger.Message = (windowsServiceObj == null ? ex.InnerException.Message : ("Service Name : " + windowsServiceObj.ServiceName + "-->" + ex.InnerException.Message));
                //esLogger.Message = "Service Name : " + windowsServiceObj.ServiceName + "-->" + ex.InnerException.Message;
                esLogger.StackTrace = (ex.InnerException.StackTrace == null ? "" : ex.InnerException.StackTrace);
            }
            else
            {
                esLogger.Message = (windowsServiceObj == null ? ex.Message : ("Service Name : " + windowsServiceObj.ServiceName + "-->" + ex.Message));
                //esLogger.Message = "Service Name : " + windowsServiceObj.ServiceName + "-->" + ex.Message;
                esLogger.StackTrace = ex.StackTrace;
            }

            if (serverObj != null)
            {
                esLogger.ServerName = serverObj.ServerName;
            }
            try
            {
                lock (lockObject)
                {
                    esHealthCheckLoggingList.Add(esLogger);
                }
            }
            catch (Exception exe)
            {
                string logEntry = "Application";

                if (EventLog.SourceExists(logEntry) == false)
                {
                    EventLog.CreateEventSource(logEntry, "Windows and IIS health check Log");
                }

                EventLog eventLog = new EventLog();
                eventLog.Source = logEntry;
                eventLog.WriteEntry(exe.Message + " " + exe.StackTrace, EventLogEntryType.Error);
            }
        }

And then the below function is called to add objects from static list to the db object.

public void AddErrorToDbObject() { try { foreach (EShelathLoging eslogObject in esHealthCheckLoggingList) { lock (lockObject) { dbObject.EShelathLogings.Add(eslogObject); } } } catch (DbEntityValidationException exp) { string logEntry = "Application";

            if (EventLog.SourceExists(logEntry) == false)
            {
                EventLog.CreateEventSource(logEntry, "Windows and IIS health check Log");
            }

            EventLog eventLog = new EventLog();
            eventLog.Source = logEntry;
            eventLog.WriteEntry(exp.Message + " " + exp.StackTrace, EventLogEntryType.Error);
        }
        catch (Exception exe)
        {
            string logEntry = "Application";

            if (EventLog.SourceExists(logEntry) == false)
            {
                EventLog.CreateEventSource(logEntry, "Windows and IIS health check Log");
            }

            EventLog eventLog = new EventLog();
            eventLog.Source = logEntry;
            eventLog.WriteEntry(exe.Message + " " + exe.StackTrace, EventLogEntryType.Error);
        }`enter code here`

    }

Upvotes: 1

Views: 768

Answers (2)

user8128167
user8128167

Reputation: 7676

Please note that I received the same exception with the application I was working on, and determined that the best way to resolve the issue was to add an AsyncLock, because of what @svick mentioned about how DbSet is not threadsafe. Thank you, @svick!

I'm guessing that your DbContext is inside your businessLayerObj, so here is what I recommend, using Stephen Cleary's excellent Nito.AsyncEx (see https://www.nuget.org/packages/Nito.AsyncEx/):

using Nito.AsyncEx;
// ...

private readonly AsyncLock _dbContextMutex = new AsyncLock();


public void CheckForServerHealth()
{
        using (await _dbContextMutex.LockAsync().ConfigureAwait(false))
        {
            await MyDbContextOperations(businessLayerObj).ConfigureAwait(false);
        }
}

private async Task MyDbContextOperations(BusinessLayerClass businessLayerObj)
{
        await Task.Run(() =>
        {
            // operations with businessLayerObj/dbcontext here...
        });
}

Upvotes: 0

svick
svick

Reputation: 244767

DbSet<T> is not thread-safe, so you can't use it from multiple threads at the same time. It seems you're trying to fix that by using a lock, but you're doing that incorrectly. For this to work, all threads have to share a single lock object. Having separate lock object for each thread, like you do now, won't do anything.

Upvotes: 1

Related Questions