System invalidoperationexception как исправить

code is given below

Queue<int> queXpTrackerX = new Queue<int>(10);
Queue<int> queXpTrackerY = new Queue<int>(10);       

if (iCounterForXpTrack < 10)
{
    queXpTrackerX.Enqueue(X);
    queXpTrackerY.Enqueue(Y);
    iCounterForXpTrack++;
}//End IF
else
{
    queXpTrackerX.Dequeue();
    queXpTrackerY.Dequeue();
    queXpTrackerX.Enqueue(X);
    queXpTrackerY.Enqueue(Y);
}//End else

for (int indexXp = 0; indexXp < iCounterForXpTrack; indexXp++)
{
    gXpTracker.DrawEllipse(Pens.Cyan, queXpTrackerX.ElementAt(indexXp) , queXpTrackerY.ElementAt(indexXp), 5, 5);
}//end for

Darin Dimitrov's user avatar

asked Jan 1, 2010 at 10:39

Badr's user avatar

5

I suspect that the most likely cause for you InvalidOperationException is trying to Dequeue from a queue when it is empty. Did you have the exception message? Is it ‘Queue empty.’?

This can happen if your iCounterForXpTrack becomes out of sync with the number of elements in the queue. It would be better to just ask the queue directly to avoid this possible error:

    if (queXpTrackerX.Count < 10)
    {
        queXpTrackerX.Enqueue(X);
        queXpTrackerY.Enqueue(Y);
    }
    else
    {
        queXpTrackerX.Dequeue();
        queXpTrackerY.Dequeue();
        queXpTrackerX.Enqueue(X);
        queXpTrackerY.Enqueue(Y);
    }

A possible reason that your code fails is if you initialized iCounterForXpTrack to 10 thinking that new Queue<int>(10) creates a queue that starts with 10 elements. This is not the case. The queue is initially empty. Providing the capacity to the queue constructor is just a performance optimization and is not strictly needed.

Another issue with your code: instead of having two queues, one for x and one for y, you should use some sort of Point class and a Queue<Point>. This simplifies the code and eliminates possible errors from the two queues becoming out of sync.

answered Jan 1, 2010 at 10:48

Mark Byers's user avatar

Mark ByersMark Byers

805k190 gold badges1576 silver badges1450 bronze badges

Решение проблемы с ошибкой «System.InvalidOperationException: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления ‘имя_элемента_GUI’ не из того потока, в котором он был создан.» заключается в создании и вызове методов-делегатов. Статья написана на основе перевода статьи Rüdiger Klaehn, оригинал см. в [1].

Проблема обычно возникает, когда необходимо получить доступ на запись к одному и тому же ресурсу асинхронно из нескольких потоков. Например, Вы имеете на форме ListBox, в который задумали выводить сообщения из обработчика события, который обрабатывается в другом потоке. У меня такое произошло, когда понадобилось отображение данных в ListBox, принятых в обработчике события поступления данных по USB.

MultiThreadingIssue01.png

Почему такая проблема возникает: ListBox создается в основном потоке (thread), запускающем GUI окна программы и обрабатывающим сообщения главного окна формы. Другие потоки могут быть созданы для поддержки обмена данными с внешними устройствами (сеть Ethernet, COM-порт, USB и т. п.). Эти другие потоки могут обрабатывать события приема данных асинхронно с выполнением основного потока. Поэтому попытка разными потоками что-то записать в ListBox может привести к конфликту с записью в ListBox данных из основного потока GUI интерфейса программы.

Как проблему можно решить: для ListBox создаются подпрограммы (методы), через которые выводятся данные в ListBox. Данные выводятся в ListBox только через эти подпрограммы, больше никак. Эти подпрограммы делаются защищенными (thread-safe) для вызова из любых потоков через delegate, InvokeRequired, Invoke.

[Введение]

Программирование интерфейса пользователя через Windows Forms довольно простое, пока вы не используете несколько потоков. Однако всякий раз, когда Вашему приложению нужно выполнить какую-нибудь фактическую работу, становится необходимым использовать поточную обработку (threading), т. е. создать дополнительный поток (thread) или потоки, чтобы обеспечить работоспособность пользовательского интерфейса (скорость отклика UI). В этом случае программирование Windows Forms может стать довольно сложным.

[Описание проблемы]

Вы наверное уже знаете, что Windows Forms главным образом не являются потокозащищенными (not thread safe). Например, Вы не можете безопасно прочитать (get) или установить (set) свойство объекта управления (control) Windows.Forms из любого потока, за исключением того потока, который обрабатывает очередь сообщений формы (message queue). Это чрезвычайно важно, когда Вы делаете модификацию своих элементов управления (controls) Ваших Windows Forms из потока очереди сообщений. Могут быть и другие случаи, когда разные потоки вступают в конфликт при доступе к одному и тому же общему ресурсу – например, обработчик события получения данных от внешнего устройства (USB, сокет Ethernet и проч.) должен выводить данные в ListBox, который был создан в главном окне программы. В этом случае выполнение программы может вызывать исключение «System.InvalidOperationException: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления ‘имя_экземпляра_ListBox’ не из того потока, в котором он был создан.».

[Стандартное решение]

Само собой, имеется стандартный механизм для решения этой проблемы. Каждый элемент управления (control) Windows Forms имеет свойство InvokeRequired, которое вернет false, если текущий поток является «родным» для контрола, т. е. является потоком очереди сообщений. Также имеется метод Invoke, который делает возможным поставить выполнение делегата (delegate) с параметрами в очередь сообщений управления.

Поскольку делегат выполняется непосредственно из очереди сообщений, то не возникает проблемы с потоками. Однако такой стиль программирования может быть довольно утомительным. Чтобы сделать что-то столь же простое, как установка свойства текста или запрет/разрешение элемента управления, Вам нужно определить отдельный метод, который соответствует делегату.

[Пример: случайные строки (Random Strings)]

Чтобы проиллюстрировать этот метод, я написал маленькую программу на Windows Forms, которая генерирует случайные строки. Вот кусок кода, который показывает, как сделана синхронизация между рабочим потоком (worker thread) и потоком цикла очереди сообщений (message loop thread). 

char PickRandomChar(string digits)
{
    Thread.Sleep(100);
    return digits[random.Next(digits.Length)];
}
delegate void SetBoolDelegate(bool parameter);
void SetInputEnabled(bool enabled)
{
    if(!InvokeRequired)
    {
        button1.Enabled=enabled;
        comboBoxDigits.Enabled=enabled;
        numericUpDownDigits.Enabled=enabled;
    }
    else
        Invoke(new SetBoolDelegate(SetInputEnabled),new object[] {enabled});
}
delegate void SetStringDelegate(string parameter);
void SetStatus(string status) {
    if(!InvokeRequired)
        labelStatus.Text=status;
    else
        Invoke(new SetStringDelegate(SetStatus),new object[] {status});
}
void SetResult(string result) {
    if(!InvokeRequired)
        textBoxResult.Text=result;
    else
        Invoke(new SetStringDelegate(SetResult),new object[] {result});
}
delegate int GetIntDelegate();
int GetNumberOfDigits()
{
    if(!InvokeRequired)
        return (int)numericUpDownDigits.Value;
    else
        return (int)Invoke(new GetIntDelegate(GetNumberOfDigits),null);
}
delegate string GetStringDelegate();
string GetDigits()
{
    if(!InvokeRequired)
        return comboBoxDigits.Text;
    else
        return (string)Invoke(new GetStringDelegate(GetDigits),null);
}
void Work()
{
    try
    {
        SetInputEnabled(false);
        SetStatus("Working");        
        int n=GetNumberOfDigits();
        string digits=GetDigits();
        StringBuilder text=new StringBuilder();
        for(int i=0;i!=n;i++)
        {
            text.Append(PickRandomChar(digits));
            SetResult(text.ToString());
        }
        SetStatus("Ready");
    }
    catch(ThreadAbortException)
    {
        SetResult("");
        SetStatus("Error");
    }
    finally
    {
        SetInputEnabled(true);
    }
}
void Start()
{
    Stop();
    thread=new Thread(new ThreadStart(Work));
    thread.Start();
}
void Stop()
{
    if(thread!=null)
    {
        thread.Abort();
        thread=null;
    }
}

Здесь используется Thread.Abort, так как это самое простое решение. Если Вы делаете что-то, что не должно быть прервано ни при каких обстоятельствах, то вместо этого Вы должны использовать флаг сигнализации для потока. Здесь приведен простой, но очень повторяющийся код. Имейте в виду, что Вы всегда должны проверить InvokeRequired, потому что вызов Invoke перед созданием очереди сообщений может привести к ошибке. Скачать исходный код проекта (Visual Studio C# 2010) можно по ссылке [7].

[Генерирование потокозащищенных оберток (thread-safe wrappers)]

В предыдущей статье (см. [2]) я показал, как можно автоматически генерировать обертки для классов, чтобы заставить их «неявно» реализовать интерфейс. Тот же метод генерации кода можно расширить, чтобы создать обертки, автоматически обеспечивающие вызов метода в правильном потоке. Я позже подробно опишу, как это работает. В первую очередь рассмотрим, как используется весь механизм. Сначала Вы предоставляете соответствующие свойства в Вашей форме (чекбоксы, листбоксы, и проч.), не заботясь о проблемах с потоками. Это именно то, что Вы хотели бы сделать, если бы совсем не использовали многопоточность. 

public bool InputEnabled
{
    set
    {
        button1.Enabled=value;
        comboBoxDigits.Enabled=value;
        numericUpDownDigits.Enabled=value;
    }
}
public string Status
{
    set { labelStatus.Text=value;}
}
public int NumberOfDigits
{
    get { return numericUpDownDigits.Value; }
}
public string Digits
{
    get { return comboBoxDigits.Text; }
}
public string Result
{
    set { textBoxResult.Text=value; }
}

Затем определите интерфейс, который содержит все свойства и/или методы, к которым может быть получен доступ из других разных потоков. 

interface IFormState
{
    int NumberOfDigits { get; }
    string Digits { get; }
    string Status { set; }
    string Result { set; }
    bool InputEnabled { set; }
}

Теперь в методе worker все, что Вы должны сделать – создать thread-safe wrapper и использовать его. Для Вас опущен весь повторяющийся код.

void Work()
{
    IFormState state=Wrapper.Create(typeof(IFormState),this);
    try
    {
        state.InputEnabled=false;
        state.Status="Working";
        int n=state.NumberOfDigits;
        string digits=state.Digits;
        StringBuilder text=new StringBuilder();

        for(int i=0;i

        {
            text.Append(PickRandomChar(digits));
            state.Result=text.ToString();
        } 
        state.Status="Ready";
    }
    catch(ThreadAbortException)
    {
        state.Status="Error";
        state.Result="";
    }
    finally
    {
        state.InputEnabled=true;
    }
}

[Как это работает]

Генератор обертки (wrapper generator) использует System.Reflection.Emit для генерации proxy-класса, который содержит все требуемые для интерфейса методы. Это также включает методы доступа к свойствам, у которых есть специальная сигнатура.

Тело этих методов будет делать вызов оригинальных методов напрямую, если InvokeRequired вернул false. Это важно, чтобы удостовериться, что запрос методов также работает, если форма еще не подключена к потоку обработки сообщений.

Если InvokeRequired вернул true, создается делегат, указывающий на оригинальный метод, и созданный делегат передается в метод Invoke формы. Типы делегата кэшируются так, чтобы Вы не получили несколько типов делегата для той же самой сигнатуры метода.

Поскольку wrapper generator использует интерфейс для вызова синхронизации ISynchronizeInvoke, Вы можете применить это решение и для приложений, не использующих Windows-Forms. Все, что нужно сделать – самостоятельно реализовать интерфейс и возможно обработку очереди сообщений.

[Ограничения и возможные проблемы]

Важно понимать, что пока thread-safe wrapper скрывает издержки потоковой синхронизации, из него нельзя делать выход. К примеру, доступ к свойству через thread safe wrapper будет намного медленнее, чем прямой доступ к свойству, если InvokeRequired вернул true. Поэтому если Вам нужно делать многочисленные сложные изменения в Вашей форме из разных потоков, лучшим решением будет создать один отдельный метод для всех действий и вызывать его, вместо того чтобы использовать отдельные вызовы доступа к свойствам.

Также нужно иметь в виду, что не каждый объект может быть безопасно передан из одного потока в другой без синхронизации. В общем случае можно безопасно передавать только типы наподобие int, DateTime и т. п. и иммунные ссылочные типы наподобие string. Будьте особенно осторожны при передаче мутированных ссылочных типов (mutable reference types) наподобие StringBuilder из одного потока к другому. Это будет удачным только в том случае, если будет реально обеспечено, что объект не был модифицирован, пока имеется ссылка на этот объект в других потоках, или все будет удачно если объект является thread safe. Если есть сомнения, то просто передайте глубокую копию вместо ссылки.

[Whidbey]

Whidbey уменьшает проблемы многопоточности, так как он имеет дополнительную поддержку (см. [5]) для фоновой обработки в Windows Forms, и это тем более делает работу с делегатами намного проще благодаря поддержке анонимных методов (см. [4]), которые являются реально закрытыми (closure, см. [6]).

Установка свойства в Whidbey намного проще: 

Invoke(delegate { labelStatus.Text="Working"; });

Точно так же просто получить значение свойства: 

int n=(int)Invoke(delegate { return numericUpDownDigits.Value; });

К сожалению, Whidbey наверное выйдет в релиз одновременно с Duke Nukem Forever, где-нибудь в 2025 году.

[Ссылки]

1. Оригинал статьи Rüdiger Klaehn «Making Windows Forms thread safe».
2. Оригинальная статья о «неявных» интерфейсах «Autocaster — Implicit interfaces for .NET».
3. Статья о проблемах с потоками в Windows Forms «How to: Make Thread-Safe Calls to Windows Forms Controls «. Русская версия той же статьи.
4. Отличная статья об анонимных методах (anonymous methods) в системе Whidbey.
5. Статья о классе BackgroundWorker «A tryst with Whidbey: Background operations in Windows Forms».
6. Closure (computer science).
7. Демо-проект ThreadSafeWrapper (Visual Studio C# 2010).

Once running ASP.NET 4.x application built in Visual Studio 2013 I am getting the below exception.

I have tried to disable the PageInspector by removing page inspector assembly

<assemblies>
    <remove assembly="Microsoft.VisualStudio.Web.PageInspector.Loader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</assemblies>

and adding this configuration to app settings

<appSettings>
    <add key="PageInspector:ServerCodeMappingSupport" value="Disabled"/>
</appSettings>

Nothing helped.

[/Pages/TargetPage.aspx] System.Web.HttpException (0x80004005): Exception of type 'System.Web.HttpException' was thrown. ---> System.Web.HttpUnhandledException (0x80004005): Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.InvalidOperationException: Stack empty.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.Stack`1.Pop()
   at Microsoft.VisualStudio.Web.PageInspector.Runtime.WebForms.SelectionMappingRenderTraceListener.EndRendering(TextWriter writer, Object renderedObject)
   at System.Web.UI.RenderTraceListener.RenderTraceListenerList.EndRendering(TextWriter writer, Object renderedObject)
   at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
   at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.HandleError(Exception e)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.LegacyPageAsyncInfo.<CallHandlersPossiblyUnderLock>b__32(Object o)
   at System.Web.HttpContext.InvokeCancellableCallback(WaitCallback callback, Object state)
   at System.Web.UI.Page.LegacyPageAsyncInfo.CallHandlersPossiblyUnderLock(Boolean onPageThread)
   at System.Web.UI.Page.LegacyPageAsyncInfo.CallHandlers(Boolean onPageThread)
   at System.Web.HttpAsyncResult.End()
   at System.Web.UI.Page.LegacyAsyncPageEndProcessRequest(IAsyncResult result)
   at System.Web.UI.Page.AsyncPageEndProcessRequest(IAsyncResult result)

Is there any way how to avoid the exception?
Thanks a lot for any suggestions

I got this error recently while playing with EF Core 2. There’s very little on Google about it, and although it’s not a hugely difficult problem to solve, if I ever get it again, I can just Google it!

The error:

System.InvalidOperationException: «The seed entity for entity type ‘MyEntity’ cannot be added because another seed entity with the same key value for {‘Id’} has already been added. Consider using ‘DbContextOptionsBuilder.EnableSensitiveDataLogging’ to see the conflicting key values.»

It is effectively caused by a conflict in the primary key, and it gives you the first step towards solving it in the error (in OnModelCreating):

var options =
    new DbContextOptionsBuilder<ApplicationDbContext>()
         .UseSqlServer(configuration.GetConnectionString("DefaultConnection"))
         .EnableSensitiveDataLogging()
         .Options;

Now it says:

System.InvalidOperationException: «The seed entity for entity type ‘MyEntity’ cannot be added because another seed entity with the key value ‘Id:1′ has already been added.»

In my particular case, I’d been playing around with adding some seed data, and had left this in (OnModelCreating):

modelBuilder.Entity<MyEntity>().HasData(new Data.MyEntity()
{
    Id = 1,
    Name = "Test",
    CreatedDate = new DateTime(2018, 07, 03),
    UpdatedDate = new DateTime(2018, 07, 03)
});
  • Remove From My Forums
  • Question

  •  Hi,

    I got a program that needs to do operation on a DB, so it opens the connection, runs the querys and closes the connection. 
    For some reason I get sometimes a System.InvalidOperationException when I do a conn.Close(); 
    I managed to debug it an put a breakpoint in the exception, and I checked the following:
    1) The conn was in state open when it did the close
    2) I got this Exception message Internal .Net Framework Data Provider error 1
    3) Stack trace of the exception is along the lines:

    System.Data.ProviderBase.DbConnectionInternal.PrePush(Object expectedOwner)rn   
    System.Data.ProviderBase.DbConnectionPool.PutObject(DbConnectionInternal obj, Object owningObject)
    System.Data.ProviderBase.DbConnectionInternal.CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
    en System.Data.SqlClient.SqlConnection.Close()

    So it looks like .net had a problem in the pool connection trying to release the connection.

    Any idea of how could handle this more gracefully? Actually I got a common try/catch and log the exception in the catch.
    Maybe there is a condition that I could check and call close() only in certain circumstances? or any other way to avoid the «.Net Framework Data Provider error 1»?

Answers

  • Hi,

    This happening because you are calling Close() on a SqlConnection object before you call Close() on a SqlDataReader that using connection. It is said that you should not do this. You can avoid this problem is by implementing the IDisposable
    in your class and perform this Close() in the dispose function. Below article has a good explaination on implementing IDisposable. Hope this helps you.

    How To Use(Open/Close) Connections Correctly

    Thanks,

    Sabah Shariq

    • Edited by

      Wednesday, November 25, 2015 3:12 PM

    • Marked as answer by
      DotNet Wang
      Thursday, December 3, 2015 5:10 AM

  • It is not necessary to close a connection explicitly if: a) you’re using a using statement (the correct choice 90+% of the time, b) using a reader (it closes the reader automatically).

    using (var conn = new SqlConnection(...))
    {
       var cnd = ...;
    
       using (var reader = cmd.ExecuteReader())
       {
          while (reader.Read())
          {
             ...
          };
       };
    };

    Michael Taylor
    http://blogs.msmvps.com/p3net

    • Marked as answer by
      DotNet Wang
      Thursday, December 3, 2015 5:10 AM

Понравилась статья? Поделить с друзьями:
  • Как найти свои штрафы гибдд по фамилии
  • Как в сбербанк онлайн найти чек перевода
  • Как найти файл по фразе в нем
  • Как в ворде найти совпадающий текст
  • Как найти абсолютный экстремум функции