Skocz do zawartości

Błąd 0x80040E31 przy dodawaniu kontrahenta

Polecane posty

Dzień dobry,

 

mam program dodający zk przez sferę,

w celu dodania tego zk wykonuję następujące kroki:

Sprawdzam czy kontrahent istnieje


jeśli nie istnieje dodaje nowego

	InsERT.Kontrahent kon = this.sfera.sgt.Kontrahenci.Dodaj();
	kon.Typ = 0;
	kon.Symbol = "..";
	kon.Miejscowosc = "..";
	kon.Ulica = "..";
	kon.Telefony.Dodaj("..");
	kon.Zapisz();
	kon.Zamknij();

Jeśli istnieje to wczytuję po id i aktualizuję

	InsERT.Kontrahent kon = this.sfera.sgt.Kontrahenci.Wczytaj(ID);

	kon.Miejscowosc = "..";
	kon.Ulica = "..";
	kon.NrDomu = "..";
	if (kon.Telefony.Liczba == 0)
	{
		kon.Telefony.Dodaj("..");
	}
	else
	{
		kon.Telefony[1].Numer = "..";
	}
	kon.Zapisz();
	kon.Zamknij();

Następnie dodaje zk dla tego kontrahenta


	zk = this.sfera.sgt.SuDokumentyManager.DodajZK();

	zk.UwagiExt = "..";

	zk.NumerOryginalny = "..";
	zk.Podtytul = "...";
	zk.StatusDokumentu = 6;
	zk.KontrahentId = kontrahentId;
	zk.Zapisz();
	zk.Zamknij();

I to działa ale czym dłużej działa serwer tym częściej mam błędy 0x80040E31

Cytat

System.Runtime.InteropServices.COMException: Wyjątek od HRESULT: 0x80040E31\r\n   w InsERT.Kontrahent.Zapisz()

Błąd pojawia się dla nowych kontrahentów i tych edytowanych, dodatkowo dla tych edytowanych przed wczytaniem do edycji sprawdzam tabelę ins_blokada.

Najczęściej jest pusty ale czasami faktycznie pojawia się blokada

Cytat

<obid>1272492<\/obid><obextra>0<\/obextra><obtype>151<\/obtype><workstation>SERWER<\/workstation><username>Szef<\/username><locktime>2023-11-20T19:25:07.037<\/locktime><counter>5<\/counter>

 

Tylko odnoszę wrażenie że najpierw jest błąd bez wpisu, a później kilka takich z wpisem w blokadach.

 

Czy coś tu robię źle?

W przykładach w dokumentacji wygląda to podobnie ale nie wiem, może jakiś zasób źle zamykam albo coś w tym stylu?

 

Link to postu

Prosimy zapoznać się z rozdziałem FAQ zawartym w pomocy Sfery, szczególnie z poradą dotyczącą zwalniania obiektów:


Zwalnianie nieużywanych obiektów w .NET
Pytanie:
W jaki sposób zwalniać nieużywane obiekty?
Odpowiedź:
Nieużywane obiekty w środowisku .NET należy zwalniać metodą:
System.Runtime.InteropServices.Marshal.ReleaseComObject( obj );
 
Przy tworzeniu rozwiązań sferycznych należy zadbać o prawidłowe zwalnianie obiektów. Jeśli rozwiązanie dodaje kontrahentów/dokumenty w jakiejś pętli, to w każdym obiegu pętli.
Warto też nie stosować "tasiemcowych konstrukcji" typu sgt.Kontrahenci — najlepiej każdy obiekt czy kolekcję osobno i jawnie zadeklarować, a po użyciu usunąć metodą ReleaseComObject.

Link to postu
1 godzinę temu, Anna Sałacińska napisał:

I to działa ale czym dłużej działa serwer tym częściej mam błędy 0x80040E31

Istotne informacje, których brakuje:

  • jaka jest konfiguracja sprzętowa (zwłaszcza ilość pamięci RAM) i programowa (edycja serwera SQL) tego serwera
  • jakie są zasoby pamięci ram w momencie pojawiania się błędów
  • po jakim czasie od rozpoczęcia operacji zapisu pojawia się błąd (szczegółowe logi rozwiązania)

 

Link to postu
  • 3 tygodnie później...

 

W dniu 21.11.2023 o 12:57, Daniel Kozłowski napisał:

Istotne informacje, których brakuje:

  • jaka jest konfiguracja sprzętowa (zwłaszcza ilość pamięci RAM) i programowa (edycja serwera SQL) tego serwera
  • jakie są zasoby pamięci ram w momencie pojawiania się błędów
  • po jakim czasie od rozpoczęcia operacji zapisu pojawia się błąd (szczegółowe logi rozwiązania)

Xeon Gold 6248 (20c/40t). Pamięć to 128GB i dysk ssd raid10 (4dyksów ssd). Platforma Insert chodzi na Wirtualnej maszynie gdzie są przydzielone 32core i 60gb ram, SQL Runtime 2017 (14.0.2047.8), baza zajmuje ~30gb a miejsca wolnego jest około 100gb.

 

Problem pojawia się trochę losowo, działa kilka godzin po czym zaczyna sypać błędami.

Przykładowo, w sobotę było jakieś 4godziny dobrze, później były błędy jakieś dwie godziny z znowu chwilę działało.

W niedzielę działało około dwie godziny i od tej pory non stop błędy.

To znaczy wygląda to tak że dodaje jedno max dwa zamówienia ale zajmuje to 7-10sekund na każde i przy kolejnym wali błędem. Jak działa dobrze to zamówienie dodaje się dosłownie w 1-2sekundy.

 

 

W dniu 21.11.2023 o 12:17, Dariusz Nowak napisał:

Zwalnianie nieużywanych obiektów w .NET
Pytanie:
W jaki sposób zwalniać nieużywane obiekty?
Odpowiedź:
Nieużywane obiekty w środowisku .NET należy zwalniać metodą:
System.Runtime.InteropServices.Marshal.ReleaseComObject( obj );

Trochę szkoda że nie jest to podane wprost w przykładach...
Jest już dodane wszędzie, wydawało się że pomogło ale po jakichś dwóch godzinach znowu to samo.

 

W dniu 21.11.2023 o 12:17, Dariusz Nowak napisał:

Warto też nie stosować "tasiemcowych konstrukcji" typu sgt.Kontrahenci — najlepiej każdy obiekt czy kolekcję osobno i jawnie zadeklarować, a po użyciu usunąć metodą ReleaseComObject.

są tworzone dosłownie tak jak jest to podane wyżej.

 

Operacje jakie to dokładnie:

- Otwarcie lub utworzenie kontrahenta
- Uzupełnienie danych adresowych kontrahenta
- zamknięcie kontrahenta

 

- dodaje zk
-- dodaje pozycje do zk
- zamykam zk
 

dodawanie pozycji wygląda tak

foreach(p in produkty){
    poz = zk.Pozycje.Dodaj(prodId);
    poz.CenaBruttoPrzedRabatem = p.brutto;
    poz.RabatProcent = p.rabat;
    poz.IloscJm = p.ilosc;
    System.Runtime.InteropServices.Marshal.ReleaseComObject(poz);
}

 

Link to postu
21 godzin temu, Anna Sałacińska napisał:

dodawanie pozycji wygląda tak

Radzimy jeszcze dokonać następujących modyfikacji w kodzie programu:

  • zadeklarować osobną zmienną na kolekcję pozycji typu SuPozycje i korzystać z niej przez cały blok pętli foreach, a potem poza blokiem usunąć poprzez ReleaseComObject
  • zamienić foreach na zwykłą pętlę
Link to postu

suDodument.Pozycje jest tylko jako get, jak mam tam przekazać całą listę?

 

image.png.78ea5c2cea9f3784845978a46ed38ed7.png

I jak mam dodawać same pozycje? Bo w dokumentacji wszędzie jest że dodaje się je w taki sposób.

 

 

Trochę nie rozumiem czemu mam zmieniać foreach na for - bo jak się domyślam to jest "zwykła" pętla.

Przecież to jest pętla po danych które dostaje z mojego API, to nie coś co odczytuję ze sfery.

Link to postu

Proszę swój kod zorganizować zgodnie z poniższym przykładem. Zalecamy jawne deklarowanie wszystkich obiektów i kolekcji, unikanie "wielokrotnych przypisań" oraz wyeliminowanie wszystkich pętli foreach.

//każdy obiekt i kolekcja są jawnie zadeklarowane
            InsERT.SuDokument oFS = null;
            InsERT.SuDokumentyManager oFSmngr = null;
            InsERT.SuPozycje oPozycje = null;
 
            //blok try-catch-finally gwarantuje wykonanie kodu odpowiedzialnego
            //za usuwanie obiektów nawet w przypadku wystąpienia błędów
            try
            {
                //tworząc obiekty unikamy "tasiemcowych przypisań"
                //i posługiwania się niejawnie utworzonymi obiektami
                oFSmngr = oSubGT.SuDokumentyManager;
                oFS = oFSmngr.WczytajDokument(40);
                oPozycje = oFS.Pozycje;
 
                oFS.Wyswietl(false);
                for (int i = 1; i <= oPozycje.Liczba; i++)
                {
                    InsERT.SuPozycja oPoz = null;
                    try
                    {
                        oPoz = oPozycje.Wczytaj(i);
                        Debug.WriteLine((string)oPoz.TowarNazwa);
                    }
                    finally
                    {
                        if (oPoz != null)
                            System.Runtime.InteropServices.Marshal.ReleaseComObject(oPoz);
                    }
                }
            }
            finally
            {
                if (oPozycje != null)
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(oPozycje);
 
                if (oFS != null)
                {
                    oFS.Zamknij();
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(oFS);
                }
 
                if (oFSmngr != null)
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(oFSmngr);
            }
Edytowane przez Dariusz Nowak
Link to postu

Zaczęłam przerabiać ale to nie zadziała.

Ja nie edytuje dokumentu tylko dodaje nowe ZK.

Więc nie mogę pobrać produktów z dokumentu i przejść po nich pętlą. Tylko ja napycham nowy pusty dokument pozycjami.

 

I tak jak pisałam ten foreach nie przechodzi po danych ze sfery tylko po moich.

 

I może to głupie pytanie, ale czy źle dodawane pozycje mogą spowodować błąd kontrahenta?

Link to postu

Kod dodający kontrahenta wygląda dokładnie tak


InsERT.Kontrahent kon = null;

try
{ 
		kon = this.sfera.sgt.Kontrahenci.Wczytaj(kid);

		kon.Miejscowosc = klient.miasto;
		kon.Ulica = klient.ulica;
		kon.NrDomu = klient.dom;
		kon.NrLokalu = klient.lokal;
		kon.KodPocztowy = klient.kod;
		try
		{
			if (kon.Telefony.Liczba == 0)
			{
				kon.Telefony.Dodaj(klient.telefon);
			}
			else
			{
				kon.Telefony[1].Numer = klient.telefon;
			}

		}
		catch (Exception e)
		{
		}

		kon.Email = klient.email;
		kon.NIP = klient.nip;

		if (klient.imie.Trim() != "")
		{
			kon.OsobaImie = klient.imie;
		}

		if (klient.nazwisko.Trim() != "")
		{
			kon.OsobaNazwisko = klient.nazwisko;
		}
		kon.AdresDostawy = true;
		kon.AdrDostKodPocztowy = klient.dowysylki_kod;
		kon.AdrDostMiejscowosc = klient.dowysylki_miasto;
		kon.AdrDostNrDomu = klient.dowysylki_dom;
		kon.AdrDostNrLokalu = klient.dowysylki_lokal;
		kon.AdrDostUlica = klient.dowysylki_ulica;
		string nazwa = "";
		if (klient.firma != "")
		{
			kon.NazwaPelna = klient.firma;
			nazwa = klient.firma;
		}
		else
		{
			kon.NazwaPelna = klient.imie + " " + klient.nazwisko;
			nazwa = klient.imie + " " + klient.nazwisko;
		}
		kon.Nazwa = nazwa;

		kon.Zapisz();
		   
}
finally {
	if (kon != null)
	{
		kon.Zamknij();
		System.Runtime.InteropServices.Marshal.ReleaseComObject(kon);
	}
}

Wywala się na kon.Zapisz();

 

I dopiero jak doda kontrahenta to przechodzi do dodawania zk.

 

No ok zk, są dodawane w pętli ale i tak dodaje kontrahenta, a później zk.
Więc mam rozumieć że błąd w dodawaniu pozycji spowoduje zablokowanie kontrahentów?

 

Link to postu
3 godziny temu, Anna Sałacińska napisał:

Kod dodający kontrahenta wygląda dokładnie tak

Nie widzę tam dodawania kontrahenta jeśli w bazie brak kontrahenta o id = kid

Wg helpa:

Cytat

Metoda Wczytaj

 


Wywołanie metody spowoduje dodanie do kolekcji Kontrahenci nowego obiektu Kontrahent. Kontrahent dodawany tym sposobem do kolekcji musi istnieć wcześniej w bazie danych.

 

Edytowane przez Andrzej Kubik
Link to postu

Faktycznie mea culpa, tu jest edycja.

 

W każdym razie można powiedzieć że wykonuję metodę dodającą lub edytującą kontrahenta - obiekt kontrahenta zostaje zamknięty.

A dopiero później zaczynam dodawać zk, w którym kontrahenta poprzez jego id

Czyli to jest dosłownie

  zk.KontrahentId = 123;

 

A wywala się na zapisie kontrahenta.

 

foreach(zamówienia){
	kontrahentId = kontrahent.dodaj(...);


	zk = sgt.SuDokumentyManager.DodajZK();
	zk.KontrahentId = kontrahentId;
	zk.Zamknij();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(zk);
	
 }

to kontrahent.dodaj dodaje lub edytuje kontrahenta

kon = sgt.Kontrahenci.Dodaj(); lub kon = sgt.Kontrahenci.Wczytaj(kid);

kon.Miejscowosc = klient.miasto;
kon.Ulica = klient.ulica;

kon.Zapisz();
kon.Zamknij();
System.Runtime.InteropServices.Marshal.ReleaseComObject(kon);

 

To do dodawania zk nawet nie dochodzi.

 

Dlatego nie wiem czy dodawanie produktów w pierwszym zk, może popsuć zapis kontrahenta przy następnym.

 

Link to postu
  • 2 tygodnie później...
W dniu 14.12.2023 o 15:18, Anna Sałacińska napisał:

W każdym razie można powiedzieć że wykonuję metodę dodającą lub edytującą kontrahenta - obiekt kontrahenta zostaje zamknięty.

A dopiero później zaczynam dodawać zk, w którym kontrahenta poprzez jego id

Czyli to jest dosłownie

  zk.KontrahentId = 123;

 

A wywala się na zapisie kontrahenta.

Powyższy opis świadczy o tym, jakby zapis kontrahenta następował po tym, jak jest on wstawiany na ZK. Proszę się upewnić, że kolejność jest taka: operowanie na kontrahencie — zapis kontrahenta — wstawienie go na ZK. 

 

W dniu 14.12.2023 o 15:18, Anna Sałacińska napisał:
foreach(zamówienia){
	kontrahentId = kontrahent.dodaj(...);


	zk = sgt.SuDokumentyManager.DodajZK();
	zk.KontrahentId = kontrahentId;
	zk.Zamknij();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(zk);
	
 }

to kontrahent.dodaj dodaje lub edytuje kontrahenta

Nazwa zmiennej wskazuje na typ całkowitoliczbowy, a kontrahent to typ obiektowy.

 

W dniu 14.12.2023 o 15:18, Anna Sałacińska napisał:

Dlatego nie wiem czy dodawanie produktów w pierwszym zk, może popsuć zapis kontrahenta przy następnym.

Oczywiście, że tak nie powinno być. Należy szukać błędów w organizacji Pani kodu.

Link to postu

Wstawiam większy fragment kodu może będzie łatwiej.
Tu faktycznie może trochę w błąd wprowadzać to że moje klasy i klasy sfery mają te same nazwy. Te bez przestrzeni nazw InsERT są moje.

 

Klasa kontrahentów:

class Kontrahent
{
    public int dodaj(Klient klient)
    {
        InsERT.Kontrahent kon = null;
        try
        {
            using (SqlCommand qis = new SqlCommand(@"SELECT
                    isnull(
                    CONVERT(int, (SELECT TOP 1[kh_Id]
                        FROM[dbo].[kh__Kontrahent] JOIN[dbo].[adr__Ewid] on[adr_IdObiektu] =[kh_Id] and[adr_TypAdresu] = 1
                    WHERE adr_NIP = @nip AND adr_NIP != ''  AND kh_Zablokowany = 0))
                    , isnull(
                        CONVERT(int, (SELECT TOP 1 kh_Id FROM[dbo].kh__Kontrahent WHERE[kh_EMail] = @mail  AND kh_Zablokowany = 0))
                        , 0)) ")
            )
            {
                qis.Parameters.Add("@mail", SqlDbType.VarChar, 255).Value = klient.email;
                qis.Parameters.Add("@nip", SqlDbType.VarChar, 255).Value = klient.nip;
                kid = (int)qis.ExecuteScalar();
                if (kid == 0)
                {
                    kon = this.sfera.sgt.Kontrahenci.Dodaj();
                    kon.Typ = 0;
                    kon.Miejscowosc = klient.miasto;
                    kon.Ulica = klient.ulica;
                    kon.NrDomu = klient.dom;
                    kon.NrLokalu = klient.lokal;
                    kon.KodPocztowy = klient.kod;
                    kon.Telefony.Dodaj(klient.telefon);
                    kon.Email = klient.email;
                    kon.NIP = klient.nip;
                    ....
                    kon.Zapisz();
                    kid = kon.Identyfikator;
                }
                else
                {
                    kon = this.sfera.sgt.Kontrahenci.Wczytaj(kid);
                    kon.Miejscowosc = klient.miasto;
                    kon.Ulica = klient.ulica;
                    kon.NrDomu = klient.dom;
                    kon.NrLokalu = klient.lokal;
                    ...
                    kon.Zapisz();
                }
            }
        }
        catch (Exception ex)
        {
            ....
        }
        finally {
            if (kon != null)
            {
                kon.Zamknij();
                System.Runtime.InteropServices.Marshal.ReleaseComObject(kon);
            }
        }
        return kid;
    
    }
}

Klasa zamówień:

class Zamowienie
{
    public int dodaj(Zamowienie zamowienie)
    {
        int dokumentId = 0;
        InsERT.SuDokument zk = null;
        try
        {
            Kontrahent kontrahent = new Kontrahent();
            kontrahentId = kontrahent.dodaj(zamowienie.klient);
            if (kontrahentId == 0)
            {
                throw new Exception("Brak kontrahenta");
            }
            
            zk = this.sfera.sgt.SuDokumentyManager.DodajZK();

            zk.NumerOryginalny = zamowienie.id;
            zk.Podtytul = "";
            zk.StatusDokumentu = 6;
            zk.KontrahentId = kontrahentId;
            foreach (Produkt produkt in zamowienie.produkty)
            {
                using (SqlCommand qProd = new SqlCommand(@"SELECT TOP 1 [tw_Id] FROM [tw__Towar] WHERE [tw_Symbol]= @ob_TowId"))
                {
                    qProd.Parameters.Add("@ob_TowId", SqlDbType.VarChar, 20).Value = produkt.index;

                    int prodId = 0;
                    try
                    {
                        prodId = Convert.ToInt32(qProd.ExecuteScalar());
                    }
                    catch (Exception)
                    {

                    }
                    InsERT.SuPozycja poz = null;
                    if (prodId > 0)
                    {
                        poz = zk.Pozycje.Dodaj(prodId);
                    }
                    else
                    {
                        poz = zk.Pozycje.DodajUslugeJednorazowa();
                        poz.UslJednNazwa = produkt.nazwa;
                    }
                    poz.CenaBruttoPrzedRabatem = produkt.brutto;
                    poz.RabatProcent = produkt.rabat;
                    poz.IloscJm = produkt.ilosc;
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(poz);
                }
            }
            zk.Zapisz();
            dokumentId = zk.Identyfikator;
            zk.Zamknij();
            System.Runtime.InteropServices.Marshal.ReleaseComObject(zk);
        }

        catch (Exception ex)
        {
            ...
        }
        finally
        {
            if (zk != null)
            {
                zk.Zamknij();
                System.Runtime.InteropServices.Marshal.ReleaseComObject(zk);
            }
        }
        return dokumentId;
    }
    
}

 

Oraz dodawanie

Subiekt.Zamowienie zamowinie = new Zamowienie();

foreach (Zamowienie zamowienie in lista)
{
    zamowinie.dodaj(zamowienie);

}

 

 

Jak widać klasa Kontrahent dodaje lub otwiera do edycji InsERT.Kontrahent,
zmienia dane,
zapisuje i zamyka obiekt sfery.

 

I dopiero jak doda kontrahenta to tworzy InsERT.SuDokument


Ok, jest dokładniej

InsERT.SuDokument zk = null;
kontrahent.dodaj(zamowienie.klient);// tu moja klasa kontrahent która wywołuje sgt.Kontrahenci.Dodaj() lub sgt.Kontrahenci.Wczytaj()
sgt.SuDokumentyManager.DodajZK();

no ale skoro  zk na początku jest równe null to to nie powinno mieć żadnego wpływu.

 

Przed wystąpieniu błędu w tabeli [dbo].[ins_blokada] albo nie ma wpisu albo jest z workstation podanym na stanowisko na którym jest uruchamiany ten program. Więc wychodzi na to że on blokuje sam siebie.

Tego programu nie da się uruchomić kilka razy więc odpada opcja że niechcący został uruchomiony dwa razy i dwie instancje się na siebie nadkładają, próbując edytować tego samego kontrahenta.

 

Link to postu
  • 2 tygodnie później...
×
×
  • Dodaj nową pozycję...