Laden...

Verwendung einer DLL via Reflection in verschiedenen Threads - Wird die selbe Instanz verwendet?

Erstellt von trib vor 7 Jahren Letzter Beitrag vor 7 Jahren 1.836 Views
T
trib Themenstarter:in
708 Beiträge seit 2008
vor 7 Jahren
Verwendung einer DLL via Reflection in verschiedenen Threads - Wird die selbe Instanz verwendet?

Hallo zusammen,

ich verwende eine DLL per Reflection.
Diese stellt Datenklassen bereit und eine Funktion, diese per Webservices zu befüllen.
Nun iteriere ich durch alle Klassen, führe den Webservice aus, serialisiere das Ergebnis nach Json und speichere es ab.

Funktioniert wunderbar!

Nun soll auf Basis einer zweiten Einrichtung, selbiges mit einer anderen URL für die Webservices und einem anderen Speicherort für die Dateien passieren. Zeitgleich. Es werden also zwei Threads erstellt, die beide meine DLL verwenden und eine unterschiedliche Einrichtung übergeben.
(Leider tut dies eine weitere externe Anwendung, auf die ich nur sehr wenig Einfluss habe)

Allerdings sind jetzt manche Dateien identisch, was aufgrund der Datenbasis nicht sein kann/darf.

Bis auf den Json-Serializer ist nichts static. Die Klasse die das Ganze startet wird definitiv neu instanziiert.

Daher meine Vermutung: Per Reflection wird die selbe Instanz wiederverwendet.
Ist das möglich?

Führe ich den Code Zeitversetzt aus, funktioniert auch wieder alles wie gewünscht.

Der Code etwas komprimiert:

        public void Download(string namespaceName)
        {
            //namespaceName = "Project_Name.Data.Master"
            var a = Assembly.GetExecutingAssembly();
            foreach (var an in a.GetReferencedAssemblies())
            {                
                if (an.Name.Equals(namespaceName.Split('.').First()))
                    InspectAssemblyName(an, namespaceName);
            }
        }
        private void InspectAssemblyName(AssemblyName an, string namespaceName)
        {
            var b = Assembly.Load(an);            

            foreach (var t in b.GetExportedTypes())
            {
                if (!t.Namespace.Equals(namespaceName))
                    continue;
                
                var buffer = new ReflectorBuffer();
                buffer.MainAssembly = b;
                buffer.NamespaceName = namespaceName;
                var genericListType = typeof(ThreadedBindingList<>).MakeGenericType(t);
                buffer.BindingList = Activator.CreateInstance(genericListType);
                buffer.Name = t.ToString().Split('.').Last();
                buffer.BindingListType = t;
                buffer.FileName = System.IO.Path.Combine(_setup.FileDirectory, buffer.Name + _setup.FileEnding);
                var myObj = buffer.MainAssembly.CreateInstance(String.Format(_setup.ServiceTypeNamespace, buffer.Name, buffer.Name));
                if (myObj == null)                
                    buffer.LastException = new WebException("Webservice not found!");
                var types = myObj.GetType();
                buffer.WsServiceObject = myObj;
                buffer.WsService = types.GetMethod("GetData");
                _bufferList.Add(buffer);
            }
        }

Anschließend wird die Buffer-Liste abgearbeitet, der Webservice ausgeführt und das Ergebnis komprimiert gespeichert.

        public void InvokeMethod(ReflectorBuffer buffer)
        {
            if (buffer.WsService == null)
                return;
            
            try
            {
                var result = buffer.WsService.Invoke(buffer.WsServiceObject, new object[] {string.Empty, _setup.BufferSize });
                foreach (var item in (IEnumerable)result)
                {
                    var bindingType = Activator.CreateInstance(buffer.BindingListType, new[] { item });
                    buffer.BindingList.GetType().GetMethod("Add").Invoke(buffer.BindingList, new[] { bindingType });                    
                }
                CompressData(buffer);
            }
            catch (Exception ex)
            {
                buffer.LastException = ex;                
            }
        }

        public void CompressData(ReflectorBuffer buffer)
        {
            var jstr = Serializer.SerializeToJson(buffer.BindingList);
            if (!String.IsNullOrEmpty(Serializer.LastExceptionMessage))
            {
                buffer.LastException = Serializer.LastException;
                return;
            }
            SaveFile(buffer, jstr);

            //Clear stored Data
            buffer.BindingList.GetType().GetMethod("Clear").Invoke(buffer.BindingList, new object[] { });
        }

        public void SaveFile(ReflectorBuffer buffer, string jstr)
        {
            if (!buffer.FileName.EndsWith(_setup.FileEnding))
                buffer.FileName += _setup.FileEnding;
            try
            {
                if (!Directory.Exists(Path.GetDirectoryName(buffer.FileName)))
                    Directory.CreateDirectory(Path.GetDirectoryName(buffer.FileName));
                var fileExists = File.Exists(buffer.FileName);
                using (var fs = new FileStream(buffer.FileName, fileExists ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Write))
                {
                    fs.Write(_setup.FileEncoding.GetBytes(jstr), 0, _setup.FileEncoding.GetByteCount(jstr));
                    fs.Close();
                }
            }
            catch (Exception ex)
            {
                buffer.LastException = ex;
            }
        }

In beiden Verzeichnissen sind am Ende alle Dateien vorhanden, nur manche (gerade wo der Webservice sehr lange benötigt, dass die Wahrscheinlichkeit der Überschneidung besonders groß ist), sind identisch.

Kann es an Reflection liegen, dass die Daten bei manchen Klassen identisch sind?

6.911 Beiträge seit 2009
vor 7 Jahren

Hallo trib,

an Reflection liegt es nicht. Per Activator.CreateInstance wird eine neue Instanz erstellt und sonst -- soweit ich das gesehen habe -- übergibst du die konkreten Typen.

Was ist ReflectorBuffer?

Aber kannst du den Code nicht noch weiter kürzen? Bzw. überhaupt trennen nach Zuständigkeiten (SoC + SRP). Die Bindinglist hat mit dem Holen der Typen und Erstellen der Instanzen nichts zu tun. Vllt. verschwindet dann das Problem schon, wenn das Programm so ein wenig "entrümpelt" wird.

Für die Fehlersuche noch als Tipps [Artikel] Debugger: Wie verwende ich den von Visual Studio? (mit Conditional Breakpoints kannst du hier auch Instanzvergleiche durchführen) und [Tutorial] Vertrackte Fehler durch Vergleich von echtem Projekt mit minimalem Testprojekt finden

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

T
trib Themenstarter:in
708 Beiträge seit 2008
vor 7 Jahren

Moin Gü,

danke für Deine Antwort!
Genau so hatte ich das mit Activator.CreateInstance auch erwartet.

Der ReflectionBuffer ist einfach eine Klasse mit ein paar string-, Type- & MethodInfo- Properties. Weiterhin ist dort die Liste mit den Ergebnissen aus der Webserviceanfrage enthalten. Das ist notwendig, da ich die Daten nicht am Stück abhole, sondern Paketweise
Habe ich in dem Code aber wegen der Übersichtlichkeit weggelassen. Daher auch die unsaubere Zuständigkeit, die Du ansprichst. Habe nur die wichtigen Teile zusammen kopiert.

Debuggen ist leider so eine Sache. Da leider eine externe Anwendung meinen Code ausführt ist das nicht so einfach. Dort vermute ich nun auch das Problem.

Deine Aussage bestätigt zumindest meine Vermutung, dass das Problem durch die externe Anwendung bedingt wird. Das hilft mir schonmal weiter! Danke 😃

6.911 Beiträge seit 2009
vor 7 Jahren

Hallo trib,

wenn der Verdacht bei der externen Anwendung liegt, so versuche diesen Verdacht zu erhärten.

Stell mit einer eigenen Anwendung die (grobe) Funktionalität der externen nach und wenn so das Problem nicht auftritt, was zu erwarten ist, so liegt die Ursache ziemlich sicher in der externen Anwendung.

Du könntest sonst noch eine Art Overkill betreiben, indem du mittels Profiling-Api die erzeugten Objekte aufzeichnest und dann auswertest. Das ist aber schon ein wenig aufwändiger.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

T
trib Themenstarter:in
708 Beiträge seit 2008
vor 7 Jahren

Nach langem Bitten und Betteln konnte ich eine Test-Form auf dem Server ausführen, wo sonst die externe Anwendung läuft.

Diese hat einfach eine Liste aus meiner Setup-Klasse gemacht und durchiteriert:

        private void btnStart_Click(object sender, EventArgs e)
        {
            List<Task> tasks = new List<Task>();

            foreach (var item in _setup.Items)
            {
                tasks.Add(Task.Factory.StartNew(() => { DataWrapper wrapper = new DataWrapper();
                    wrapper.Download(item);
                }));
            }

            Task.WaitAll(tasks.ToArray());
            MessageBox.Show("Fertig!");
        }

Mehrfach ausgeführt, Reihenfolge der Einträge geändert, absichtlich die Webservices etwas verzögert, Debug/Release-Modus, aus VS gestartet/Standalone, usw.
Und siehe da! Alle Dateien sind immer unterschiedlich und mit korrektem Inhalt 😃

Man kann genau dabei zusehen, wie die Dateien fast synchron in den Zielverzeichnissen abgelegt werden.

Denke damit ist klar, wer sich nun zurücklehnen und ins Wochenende verschwinden kann 😄