Laden...

Forenbeiträge von Cornflake Ingesamt 141 Beiträge

30.03.2016 - 19:24 Uhr

Es ist zum Haare raufen.

Ich habe jetzt C_win ein Verweis auf A_win hinzugefügt.
Die Methode existiert jetzt auf einmal, allerdings kommt beim Ausführen der Fehler:> Fehlermeldung:

"Der Typeninitialisierer für "C_win.Program" hat eine Ausnahme verursacht."

{"Die Datei oder Assembly "C_win, Version=1.0.5933.33033, Culture=neutral, PublicKeyToken=null" oder eine Abhängigkeit davon wurde nicht gefunden. Es wurde versucht, eine Datei mit einem falschen Format zu laden."}

30.03.2016 - 17:16 Uhr

Fehlermeldung:
Fehler 52 "B_win.MyClassErbe" enthält keine Definition für "MyMethod", und es konnte keine Erweiterungsmethode "MyMethod" gefunden werden, die ein erstes Argument vom Typ "B_win.MyClassErbe" akzeptiert. (Fehlt eine Using-Direktive oder ein Assemblyverweis?) C:\Projekt_c_win\Program.cs 74 17 C_win

30.03.2016 - 17:00 Uhr

Sorry hänge aber immer noch 😦

Nochmal zu dieser dll Projektverknüpfungsgeschichte.

Also es gibt :

DLL-Project F_global
DLL-Projekt A_win : Verwendet F_global und erstellt:


public abstract class MyClass
{
 public virtual void MyMethod() {...}
}

DLL-Project B_win : Verweist auf A_win und erstellt:


public class MyClassErbe : MyClass
{ ... }

In einer weiteren Klasse funktioniert in diesem Projekt:


public class otherClass 
{ 
void  test()
{
MyClassErbe  mce = new MyClassErbe();
mce.MyMethod();   //Methode existiert und lässt sich aufrufen
}
}

Im Windows Project C_win : Verweist auf Projekt B_win
kommt es zum Fehler:


public class SomeOtherClass 
{ 
void  test()
{
MyClassErbe  mce = new MyClassErbe();
mce.MyMethod();   //!!!! Methode existiert nicht mehr !!!!
}
}

Irgend eine Idee, warum da die Methode auf einmal nicht mehr existiert?

Grüße Cornflake

30.03.2016 - 13:30 Uhr

Noch eine Frage zu den DLL Verweisen.

Wenn meine Anwendung eine DLL (x) einbindet, die wiederum eine andere DLL (y) einbindet, dann muss ich doch in der Anwendung normalerweise nur die DLL (x) einbinden?

Die dahintergelagerte DLL (y) muss ich doch nicht noch extra in der Anwendung einbinden/verweisen ?

Grüße Cornflake

29.03.2016 - 17:30 Uhr

Hallo Abt

Ich habe mal die Dateien von Netonsoft verglichen.

Wenn ich das richtig sehe, ist das eigentlich nur eine Solution die der da verwendet. Es unterscheiden sich anscheinend nur die GUIDs und Namen der Projektdateien (.csproj)
Die Projekte sind dann wie bei mir unterschiedlich Art z.B. Net35 und Portable.
In den Projektfiles werden dann verschiedene Konstanten gesetzt z.B. (TRACE;DEBUG;PORTABLE) vs (TRACE;DEBUG;CODE_ANALYSIS;NET35).
Im Code wird dann z.B. mit Präprozessorbefehlen:


#if !(NET20 || NET35 || PORTABLE40 || PORTABLE)
        private static string ToStringInternal(BigInteger value)
        {
            return value.ToString(null, CultureInfo.InvariantCulture);
        }
#endif

zwischen den verschiedenen Projektarten unterschieden.

Leider konnte ich nicht erkennen, wie er auf unterschiedliche externe DLL verweist. Anscheinend nutzt er keine externen Dlls.

Ich konnte jetzt auch nicht erkennen, ob er die Dateien als Link einbindet oder nicht.

Hast du da nochn Tipp?

Grüße Cornflake

29.03.2016 - 16:48 Uhr

Hallo Leute

Eine selbstgeschriebene DLL soll zum einen unter .NET 3.5 und .NET CF 3.5 verwendet werden. Also einmal für normale PC x86 Windows Anwendungen und zusätzlich auch eine Anwendung, für den Einsatz unter Windows CE Systemen.

Mein Problem ist der saubere Weg, wie ich meinen Code in einer Solution für diese unterschiedlichen Projekttypen sauber verknüpfen kann ohne alle doppelt zu schreiben.

Mein bisheriger Weg:

Solution: MySolution mit projekten...

Project F_global : Fremd DLL die unter x86 und WinCE funktioniert (.NET 3.5)

Projekt A_wce : Eigenes DLL Projekt, dass F_global verwendet und nur unter WindowsCE funktioniert.

Projekt A_win : Eigenes DLL Projekt, dass F_global verwendet und nur unter x86 Windows funktioniert. mit eigenen Codeteilen

Projekt B_wce : Eigenes Dll Projekt, dass auf A_wce verweist und unter WindowsCE funktioniert. Im prinzip funktioniert der Code auch unter x86

Jetzt kommt abschließend noch das Problemprojekt:
Project B_win : Eigenes DLL Projekt, dass per Link alle Dateien des B_wce einbindet unter x86 funktioniert und auf A_win verweist. ziel ist es nicht den ganzen Code doppelt zu schreiben.

Abschließend wird jetzt das DLL Projekt in einer Windows Anwendung verwendet.
Project C_win : windows Anwendung, die Projekt B_win verwendet.
Hier kommt beim ausführen eine Fehlermeldung, dass noch ein Verweis auf A_wce fehlen würde. Mir ist aber nicht klar warum und zudem würde die Windows Anwendung nur funktionieren, wenn der verweis auf A_win verweisen würde. 🤔
Daher habe ich den verweis auf A_win eingebaut. Dann kommt aber wieder eine Fehlermeldung, da anscheinend der B_wce Code nicht auf A_win verweist sonder auf A_wce.

Hat jemand von euch ein Tipp wie ich das mit den Projekten funktionsfähig verbinden kann? X(

Grüße Cornflake

01.03.2016 - 15:04 Uhr

Hallo Steffen

Danke für deinen Link.
Das war auch mein einziger den ich bisher dazu gefunden habe. Leider sind es doch recht viele Zeilen (wobei das unterschiedlich sein kann). Daher verwende ich den VirtualMode, damit vorhandene Einträge möglichst schnell angezeigt werden.

Grüße Cornflake

29.02.2016 - 13:44 Uhr

Auch hier gibts anscheinend keine Lösung dazu.

23.02.2016 - 11:28 Uhr

Hallo Leute

Ich verwende ein Datagridview in dem in der ersten Spalte ein Häkchenfeld ist und danach kommt u.a. eine Spalte in deren Zelle bis zu 20 Zeilenumbrüche sein können.
Damit wird die Zelle ziemlich hoch.

Jetzt kann das Datagridview aber nur Zellenweise springen. Bei niedrigen Zellen mit z.B. 4 Zeilenumbrüchen ist das kein Problem. Bei hohen Zellen mit 20 Zeilenumbrüchen, sehe ich immer nur den Anfang der Zelle.

Frage:
Wie kann ich in einem DataGridView "pixelweise" bzw innerhalb einer Zellenhöhe scrollen?

In der Forensuche habe ich gesehen, dass jemand anderes auch schon gefragt, aber keine Antwort bekommen hat.

Best Grüße
Cornflake

05.02.2016 - 18:24 Uhr

Ich habs FREU 😁

Es funktionieren damit zwei Themen:

  1. Die tool.exe beinhaltet alle *.dll's und der Ordner ist sauber.
  2. Das Verfahren (ResolveEventHandler) auch auf das Plugin angewendet behebt auch die Fehlermeldung mit den fehlenden Dll's der oben genannten ext.dll.

Sobald in der Main() irgendwo eine Instanz der eingebetteten Dll verwendet werden soll, will die CLR dies auflösen und es kommt zu einem Fehler. Daher kann er die Fehlerbehandlung auch noch gar nicht einrichten.

Der Trick ist folgender:
In der Main darf nur die Fehlerbehandlung stehen und danach kann eine zweite Main aufgerufen werden, in der der bisherige Code steht. Dann wird die Fehlerbehandlung eingerichtet und dann beim eigentlichen Main aufruf knallt die CLR, aber wird gleich von der eingerichteten Fehlerbehandlung aufgefangen.

⚠Sollte dennoch ein Fehler später kommen, wurde evtl. vergessen eine eingebundenen dll als eingebettete Ressource zu markieren. Zudem habe ich den Präfixtext beim ermittelten Dll Namen rausgenommen. Zusätzlich ist zu beachten, dass wenn man von Debug auf Release umstellt, evtl. die Dll neu als eingebettete Ressource eingebunden werden muss. Vllt. kennt dazu aber jemand noch nen Trick, damit bei änderungen an den Dlls oder Wechseln Debug/Release/etc immer alles auf dem neuesten Stand kommt.

🙂Anbei die Komplettlösung:

  1. In MainProjekt alle nicht MS Verweise (Dll's) als "Vorhandenes Element..." hinzufügen ("Als Link hinzufügen" reicht aus). (Habs direkt in den Rootpfad, geladen. Andere Pfade sollten auch gehen, aber dann sind im Code (ResolveEventHandler) die Pfade evtl. auch anzupassen)
  2. Jede Dll auf Buildvorgang="Eingebettete Ressource" bei den Eigenschaften umstellen.
  3. In der Program.cs Klasse darf nichts vor der Main an eingebundenen (Verweis) Dll's verwendet werden.
  4. Main Methode in Program.cs wie folgt anpassen:

namespace myTools.Tool
{
    class Program
    { 
        #region Main-Routine

        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += ResolveEventHandler;
            
            Main2();
        }

       // Behandelt die im Ordner nicht gefundenen Dlls und lädt sie über die eingebetteten Ressourcen
        private static Assembly ResolveEventHandler(Object sender, ResolveEventArgs args)
        {
            String dllName = new AssemblyName(args.Name).Name + ".dll";
            var assem = Assembly.GetExecutingAssembly();
            String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
            if (resourceName == null) return null; // Wenn nicht gefunden, wird evtl. ein anderes Handlerereignis diese Ressource auflösen

            using (var stream = assem.GetManifestResourceStream(resourceName))
            {
                Byte[] assemblyData = new Byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                return Assembly.Load(assemblyData);
            }
        }

       // Die ursprüngliche Main Methode mit dem bisherigen Code
        private static void Main2()
        {
            myTools.helper.Json myJsonHelper = null;
... (hier kommt weiterer Code)
         }
     } 
}

So ich hoffe das hilft auch allen anderen, die vor diesem Problem, viele Dll ein eine alleinstehende Exe einzubinden stehen.

Grüße Cornflake

05.02.2016 - 17:13 Uhr

Hi
Danke für eure Tipps. 😃
zlib, werde ich später ausprobieren, wenn das hier nicht klappen sollte.

Von ilMerge versuche ich jetzt weg zu kommen.
Habe dazu folgenden Link gelesen: Jeffrey Richter: Excerpt #2 from CLR via C#, Third Edition

Da geht es darum, die referenzierten Dll's als eingebettete Ressourcen innerhalb des Projektes zusätzlich hinzuzufügen und dann mit einem Codeblock zu laden, sobald die CLR einen Fehler meldet.
Leider funzt das bei mir noch nicht ganz.

Zum nachladen der DLL verwendet er den Code:


AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {

   String resourceName = "AssemblyLoadingAndReflection." +

      new AssemblyName(args.Name).Name + ".dll";

   using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {

      Byte[] assemblyData = new Byte[stream.Length];

      stream.Read(assemblyData, 0, assemblyData.Length);

      return Assembly.Load(assemblyData);

   }

}; 

Wenn ich nach dem ersten Kompilieren das Programm starte geht alles. Wenn ich dann für den zweiten start die im Ordner liegenden *.dll vorher entferne, die ich als eingebettete Ressource eingebunden haben und per Code laden lassen will, kommt es zu einer CLR Fehlermeldung, dass er die dlls nicht mehr findet. Ich glaube daher der Nachladecode ist noch falsch (Syntax / Position).

Aktuell steht der in meiner tool.exe Main Methode im Projekt ganz am Anfang.
Die Fehlermeldung kommt aber noch vor dem Debug Einsprungpunkt.


/// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {

            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                String resourceName = "AssemblyLoadingAndReflection." + new AssemblyName(args.Name).Name + ".dll";
                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                {
                    Byte[] assemblyData = new Byte[stream.Length];
                    stream.Read(assemblyData, 0, assemblyData.Length);
                    return Assembly.Load(assemblyData);
                }
            }; 

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
...

Habt ihr ne Idee?

04.02.2016 - 19:10 Uhr

Hallo Leute
vllt habt ihr ja einen Tipp. (VS2008, WinForms, .Net 3.5)

Ich hab eine Solution mit ein paar Projekten, die ungefähr wie folgt zusammenhängen.

tool.exe verweist auf: Iplugin.dll, ui.dll, data.dll, logic.dll, helper.dll, zip.dll
Da es ein kleiners Tool ist, dass ich einfach nur als Single-exe per Copy ausliefern will, habe ich mit ilmerge.exe alles zusammengemergt.
Ergebnis eine tool.exe ohne zusätzliche <x>.dll's im Ordner und es funktioniert.

Problem:
Jetzt will ich dazu als Plugin eine Erweiterung (ext.dll) per assembly.Load(byte[])... laden. wichtig ich nutze das per byte Array und nicht per Stringdateipfad Angabe.

ext.dll verweist auf: Iplugin.dll, helper.dll, zip.dll
Hier will ich auch wieder alles mit ilmerge zusammenpacken, damit ich zum Schluss maximal 2 Dateien in den Ordner kopieren brauche.

?( Das eigentliche Problem ist, wie verweise ich richtig aus der ext.dll auf die Iplugin.dll,helper.dll und zip.dll, wenn diese durch den ilmerge nicht im Ordner sondern in der tool.exe liegen.
Im VS kann ich ja auf die Projekte referenzieren, allerdings bringt ilmerge verständlicher weise bei der ext.dll ne Fehlermeldung im Logfile, dass es die anderen dll's nicht finden kann.

Kann ich da etwas in ext.csproj Datei anpassen, um auf die dll's innerhalb der tool.exe zu verweisen, bzw. gibt es in ilmerge.exe eine Konfigurationsmöglichkeit oder gibt es irgendwelche Klassenattribute die da weiterhelfen?

Grüße Cornflake

Anbei angepasster ilmerge Log Ausschnitt:


...
AssemblyResolver: Assembly 'Tool.Plugin.Ext' is referencing assembly 'Helper'.
	AssemblyResolver: Attempting referencing assembly's directory.
	AssemblyResolver: Did not find assembly in referencing assembly's directory.
	AssemblyResolver: Attempting input directory.
	AssemblyResolver: Did not find assembly in input directory.
	AssemblyResolver: Attempting user-supplied directories.
	AssemblyResolver: No user-supplied directories.
	AssemblyResolver: Attempting framework directory.
	AssemblyResolver: Did not find assembly in framework directory.
AssemblyResolver: Unable to resolve reference. (It still might be found, e.g., in the GAC.)
...
An exception occurred during merging:
Unresolved assembly reference not allowed: Helper.
   bei System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode assembly)
   bei System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)
   bei System.Compiler.Ir2md.VisitReferencedType(TypeNode type)
   bei System.Compiler.Ir2md.VisitClass(Class Class)
   bei System.Compiler.Ir2md.VisitModule(Module module)
   bei System.Compiler.Ir2md.SetupMetadataWriter(String debugSymbolsLocation)
   bei System.Compiler.Ir2md.WritePE(Module module, String debugSymbolsLocation, BinaryWriter writer)
   bei System.Compiler.Writer.WritePE(String location, Boolean writeDebugSymbols, Module module, Boolean delaySign, String keyFileName, String keyName)
   bei System.Compiler.Writer.WritePE(CompilerParameters compilerParameters, Module module)
   bei ILMerging.ILMerge.Merge()
   bei ILMerging.ILMerge.Main(String[] args)


12.01.2016 - 12:12 Uhr

@MrSparkle

Thx für deine Antwort, habe schon dazu (Bücher, Blogs) gelesen, leider nur noch nicht die saubere Version für C# WinForms gefunden.
Eher für ASP.net oder wiedersprüchliche/komplizierte Infos. Z.B. bei MVC wird ein Controllerobjekt von MS angelegt, dass ich aber in VS2008 nirgends finde, oder es werden Instanzen übergeben aber nirgends Events abonniert. Ideal wäre ja eine Webseite auf der für verschiedene Patterns C# Beispielcodes (Solutions) mit kleinem Umfang angeboten werden und in denen noch ein Klassendesigner Objekt die Zusammenhänge verdeutlicht.

Ich werde mal weiter Googeln, DuckDuckGoen und Ixquicken.

08.01.2016 - 22:03 Uhr

Ok MrSparkle thx
Den Beitrag mit der Schichtentrennung kannte ich schon. An sich ein guter Beitrag, leider geht er nicht auf das Objektpool Pattern ein, bzw. mir fehlt ein C# Beispiel, wie Ihr die Verwaltung von mehreres Formularen handhabt bzw. wie Ihr da das Vorgehen für richtig haltet.

Ich habe übrigens jetzt das mit dem Designer hinbekommen. Selbst wenn man die Vorlage in ein eigenes DLL Projekt auslagert, gabs am Anfang Fehlermeldungen. Aber vllt. für alle die auch vor dem Problem stehen.
Das besondere ist, wenn man den Verweis auf das BasisFormular einbinden, scheint nicht der absolute namespace übernommen zu werden, erst wenn man ein "Geerbtes Formular" Element erstellt und in dem auftauchenden Assistenten die DLL mit dem BasisFormular auswählt, wird der Verweis so eingebunden, dass alle anderen Formulare auch auf einmal auch im VS Designer funktionieren.

Grüße Cornflake

08.01.2016 - 20:23 Uhr

Ok also das mit der Designerunterstützung nur per seperater Assembly ist mir jetzt klar.

Aber abgesehen davon, was ist suboptimal an dem Aufbau mit einer Formularverwaltungsklasse? Ja ich habe mehrere Projekte angelegt. Dieses eine kümmert sich um die Ui bzw. Formularverwaltung.
Hast du einen Link zu einem Beispielaufbau oder Tutorial, dass es erklärt? Aktuell hilft mir da die blanke Kritik ohne konstruktive Hilfe leider nicht weiter.

Grüße Cornflake

08.01.2016 - 19:59 Uhr

Im Prinzip geht ja alles, nur dass der Visual studio Designer nicht mitmacht und ich für Änderungen an der Form immer als Basisklasse vorher "Form" angeben muss, statt "Basisformular".

Aber vllt. muss ich etwas ausholen.

Also es soll das Erzeugungsmuster Objektpool umgesetzt werden. Dazu gibts eine Klasse Formularverwalter, ein enum Formulare und eine Formularvorlage(Basisformular).
Das Basisformular erweitert das Standardformular um eine individuell vergebene FormularID, die einem Eintrag aus dem Enum Formulare entspricht und weitere wiederzuverwendende Methoden.

Zusätzlich werden die Formulare in einer generischen Liste vom Typ der Formularvorlage verwaltet.
Jetzt das Problem. Die Formulare sollen den Formularverwalter veranlassen können, ein bestimmtes anderes Formular aufzurufen. Dazu wird das aufzurufende Formular aus dem enum benannt. Hier etwas Code dazu:


  public enum Formulare
	{
        NULL,
        Anmelden,
        Editor,
        Abmelden,
        ...        
	}


 static class Formmanager
    {
        static List<Basisformular> forms = null;


        public static void Init()
        {

            forms = new List<Basisformular>();

            Basisformular an = new formularAnmelden();
            an.FormularID = Formulare.Anmelden;
            forms.Add(an);

            Basisformular ed = new formularEditor();
            ed.FormularID = Formulare.Editor;
            forms.Add(ed);

            Basisformular ab = new formularAbmelden();
            ab.FormularID = Formulare.Abmelden;
            forms.Add(ab);
 
        }

        public static void Show(Formulare Formular)
        { 
            Show(Formular, Formulare.NULL);
        }

        public static void Show(Formulare Formular, Formulare Previous)
        {
            foreach (Basisformular f in forms)
            {
                if (f.FormularID == Formular)
                {
                    if (Previous != Formulare.NULL)
                    { f.FormularVorheriges = Previous; }
                    f.Visible = true;
                    f.BringToFront();
                }
                else
                {
                    f.Visible = false;
                }
            }
        }

        public static Basisformular GetForm(Formulare Formular)
        {
            foreach (Basisformular f in forms)
            {
                if (f.FormularID == Formular)
                {
                    return f;
                }                
            }

            return null;
        }
    }


public partial class Basisformular : Form
    {
        public Basisformular ()
        {
            InitializeComponent();
        }
 
        public Formulare FormularID;
        public Formulare FormularVorheriges = formulare.NULL;
      ...
}

So ich hoffe das wird jezt nicht zu viel.(Ist nur nen Auszug) Jedenfalls, falls du dazu eine Idee hast, wie ich die Visual Studio Designerunterstützung da miteinbauen kann ohne das Ganze auf verschiedene Projekte aufteilen zu müssen, wäre das super. Es geht ja eigentlich nur um die Designer Fehlermeldung. Oder vllt hast du ein Beispiel, wie man das Pattern besser umsetzen sollte.

08.01.2016 - 19:23 Uhr

Naja also Basisformular und Editorformular befinden sich alle im selben Projekt und Namespace. Das Projekt ist als Exe ausführbar. Ich habe dann noch den Namespace dazugeschrieben, aber bringt nichts.


public partial class BasisFormular : Form
{ ... }
 
//Andere Datei
public partial class EditorFormular : DeviceApplication1.BasisFormular
{ ... }

Anscheindend also doch nur durch seperates Projekt möglich? wäre aber blöd, da im BasisFormular weitere Methoden aufgerufen werden, die ist sonst auch noch alle auslagern muss bzw wieder in ein eigenes Shared Projekt einbauen muss, damit dann alle drei Projekte zusammen arbeiten 😦

08.01.2016 - 18:53 Uhr

Hallo Leute

In einem Windows C# Programm habe ich ein Formular, dass ich als Basis für weitere Formulare verwenden will. Im Prinzip wie die Verwendung einer abstrakten Klasse.

Bsp:


public partial class BasisFormular : Form
{ ... }

//Andere Datei
public partial class AnmeldeFormular : BasisFormular
{ ... }

//Andere Datei
public partial class EditorFormular : BasisFormular
{ ... }

//Andere Datei
public partial class AbmeldeFormular : BasisFormular
{ ... }

Das Programm läuft, aber wenn ich z.B. das EditorFormular im Visual Studio Formulardesigner öffnen und bearbeite will, bekomme ich die Fehlermeldung:


Der Designer konnte für diese Datei nicht angezeigt werden, da keine der enthaltenen Klassen definiert werden kann. Der Designer hat folgende Klassen in der Datei überprüft: EditorFormular -- Die DeviceApplication1.BasisFormular-Basisklasse konnte nicht geladen werden. Stellen Sie sicher, dass auf die Assembly verwiesen wurde und alle Projekte erstellt wurden. 

Wie kann ich das lösen?

Bisher habe ich bei der MS MSDN Seite nur etwas zu visueller Vererbung gefunden. Aber ich habe nicht vor jetzt nur wegen dem Basisformular ein komplett eigenes Projekt anzulegen und darauf zu verweisen.
gibts da noch ne andere Lösung?

Das gleiche Problem habe ich auch mit der Variante bei UserControls.

Grüße Cornflake

16.12.2015 - 18:34 Uhr

@Abt: Danke hat jetzt geklappt. Allerdings muss anscheinend mit


...
ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fs, msZip, buffer);
...

zuerst die bestehende Datei in den Speicher geladen werden, um die Änderung an die richtige Stelle zuschreiben.

Wegen meinen üblen Streams, würde ich mich über eine konstruktive Kritik in Form eines Korrekturvorschlages oder Vorgehensrichtlinie freuen.

Grüße Cornflake

16.12.2015 - 10:41 Uhr

Hallo Leute

Inzwischen kann ich einen String über Memorystream als Datei in eine Zipdatei speichern. Ich verwende .NET 3.5 mit SharpZipLib.

Leider muss ich feststellen, dass jedesmal die anderen Dateien/Ordner nach dem Hinzufügen einer weiteren Datei verschwunden sind. Anbei der Testcode. Anscheinend wird jedesmal die gesamte Zipdatei überschrieben, wenn ich

FileStream fso = File.OpenWrite(ZipDateipfad);

ausführe.


private void button1_Click(object sender, EventArgs e)
{
            SetText("textziptest.zip", "a.txt", "Test A");
            SetText("textziptest.zip", "b.txt", "Test B");
            SetText("textziptest.zip", "c.txt", "Test C");
}

        /// <summary>
        /// Schreibt String in eine Datei
        /// </summary>
        /// <param name="ZipDateipfad">Zipdatei</param>
        /// <param name="Dateiname">Datei in Zipdatei</param>
        /// <param name="Text">Text in Datei</param>
        public bool SetText(string ZipDateipfad, string Dateiname, string Text)
        {
            Dateiname = ZipEntry.CleanName(Dateiname);
            bool result = false;



            //Bei Bedarf Zipdatei anlegen
            if (File.Exists(ZipDateipfad) == false)
            {
                if (!Directory.Exists(new FileInfo(ZipDateipfad).DirectoryName))
                    Directory.CreateDirectory(new FileInfo(ZipDateipfad).DirectoryName);

                ZipFile zfc = ZipFile.Create(ZipDateipfad);
                zfc.BeginUpdate();
                zfc.CommitUpdate();
                zfc.Close();                
            }



            //Alte Datei in Zipdatei löschen
            ZipFile zf = new ZipFile(ZipDateipfad);                        
            if (zf == null) return result;

            ZipEntry ze = zf.GetEntry(Dateiname);
            if (ze != null)
            {
                zf.BeginUpdate();
                zf.Delete(ze);
                zf.CommitUpdate();
            }            
            zf.Close();

            

            //String als neue Datei in Zipdatei schreiben
            FileStream fso = File.OpenWrite(ZipDateipfad);
            using (ZipOutputStream zos = new ZipOutputStream(fso))
            {
                zos.SetLevel(8);

                ze = new ZipEntry(Dateiname);
                ze.DateTime = DateTime.Now;
                ze.Size = GetStreamFromString(Text).Length;

                zos.PutNextEntry(ze);

                using (Stream si = GetStreamFromString(Text))
                {
                    StreamUtils.Copy(si, zos, new byte[4096]);
                }
                zos.CloseEntry();

                zos.IsStreamOwner = false;

                zos.Finish();
                zos.Flush();
                zos.Close();
            }
            fso.Close();



            result = true;
            return result;
        }


        public Stream GetStreamFromString(string s)
        {
            MemoryStream stream = new MemoryStream();
            StreamWriter writer = new StreamWriter(stream);
            writer.Write(s);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }

Habt ihr einen Tipp wie ich das anders lösen kann. In der SharpZipLib Doku habe ich noch kein Beispiel gefunden, bei dem mehrere Dateien(Memorystreams) in einzelnen Schritten zu einer bestehenden Zipdatei hinzugefügt werden.

Ich habe auch noch keine andere C# Zip Bibliothek gefunden, bei der man Memorystreams als Datei in eine Zpdatei lesen/schreiben kann.

Grüße Cornflake

15.12.2015 - 14:07 Uhr

@Abt Dank für deine Infos. 👍
CleanName hatte ich noch nicht gelesen und das BeginUpdate() wohl auch nicht 🤔. Hatte als Doku bisher mehr die Samples verwendet.

Durch das immer vorherige Löschen einer schon vorhandenen gezippten Datei, habe ich jetzt auch keine Fehlermeldung mehr 🙂

15.12.2015 - 11:55 Uhr

@Abt
Kann sein, dass ich die nicht ganz sauber verwende.

Die zos Variable wird beim Verlassen der using Klammern automatisch geschlossen und disposed. Dennoch habe ich ein Close() dazugeschrieben, damit der Stream definitiv vorher geschlossen wird, was man evtl weglassen kann. Aber wichtig die Streams sind bis zum Ende der Benutzung offen.

Über die GetStreamFromString() Methode hole ich mir einmal die Länge und zum anderen wird der Stream zum Schreiben der Datei in die Zipdatei verwendet. Ist an dieser Methode etwas falsch? Ich wüsste sonst nicht wie ich aus einem String einen Stream machen kann.

Was würdest du da anders machen, bzw. was mache ich falsch?

Inzwischen habe ich mir überlegt, vor jedem erneuten Schreiben der Datei eine evtl. in der Zipdatei vorhandene Datei vorher zu löschen. Leider will mich SharpZipLib kein Delete durchführen lassen und wirft an anderer Stelle eine Fehlermeldung.

Hier mein zusätzlicher Code vor dem Schreiben der Datei in die Zipdatei.


...
 private void button1_Click(object sender, EventArgs e)
        {  
            //Evtl. vorhandenen Eintrag vorher löschen
            if (File.Exists(Filepath))
            {
                ZipFile fz = new ZipFile(Filepath);
                ZipEntry zen = fz.GetEntry("Testordner\\test.txt".Replace("\\", "/"));                
                if (zen != null) fz.Delete(zen);   //Hier kommt der Fehler InvalidOperationException("BeginUpdate has not been called")
                fz.Close();
            }

            FileStream fso = File.OpenWrite(Filepath);
...

15.12.2015 - 10:57 Uhr

Hallo
Danke für deine Antwort. Vllt. nutze ich die Zip Datei ja auch falsch.
Allerdings wenn ich das mit einem Zip Programm mache (7zip) gibts keine Probleme.

Ich habe vor in der Zipdatei diese Testdatei, bei Änderungen zu überschreiben.
Der Gedanke dahinter. in der endgültigen Zipdatei liegen mehrere Dateien und Ordner und bestimmte Dateien möchte ich auslesen und auch wieder geändert zurückschreiben ohne jedesmal alles zu löschen.

Grüße Cornflake

14.12.2015 - 21:03 Uhr

Hallo Leute

Habe mal geschaut ob es zu meinem Problem hier im Forum eine Lösung gibt. Leider gibts nur einen passenden Thread, der aber nie richtig beantwortet wurde.
SharpZipLib prooduziert sporadisch defekte Archive

Hab mich letzten riesig gefreut, dass SharpZipLib funzt. Nur auf einmal gabs Fehler, die ich erst durch Löschen meiner Zip Datei wieder beheben konnte. Daher hab ich mir ein kleines Testprogramm geschrieben, mit zwei Textboxen und Buttons. in der ersten Textbox wird der Text als Textdatei im Zip gespeichert und in der Zweiten wieder aus der Zip Datei die Textdatei in die Textbox gelesen.

Ich habe herausgefunden, wenn ich mehr als 4096 Byte ( bzw. größer als Buffer) in die Zipdatei meine Textdatei wiederholt reinschreibe und danach den Inhalt der Textdatei verkleinere, erneut speicher und teilweise das Programm neu starte, es sporadisch zu einem Fehler in der Zipdatei kommt.

Die Zipdatei lässt sich dann noch in 7Zip öffnen und die Textdatei ohne Fehler im Texteditor öffnen, aber der Windows Explorer kann mit der Zipdatei nichts mehr anfangen und bei "7zip Archiv überprüfen", wird festgestellt, dass:> Fehlermeldung:

Warnings: There are some data after the end of the payload data

Update:
Ziemlich sicher kann ich den Fehler repoduzieren wenn ich z.B. 8000 Byte einfüge speichere, dann auslesen, dann 5000 Byte wegnehme, speichere. Das Auslesen bringt dann einen > Fehlermeldung:

Wrong Central Directory signature. Fehler.

Ich vermute daher irgendwas stimmt beim Aufräumen nicht. Anbei der ungeänderte Testcode in C#


namespace ZipFile_test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        string Filepath = "test.zip";
         
        /// <summary>
        /// Packen
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {  
            FileStream fso = File.OpenWrite(Filepath);

            using (ZipOutputStream zos = new ZipOutputStream(fso))
            {
                zos.SetLevel(8); 

                ZipEntry ze = new ZipEntry(("Testordner\\test.txt").Replace("\\", "/"));
                ze.DateTime = DateTime.Now; 
                ze.Size = GetStreamFromString(textBox1.Text).Length;

                zos.PutNextEntry(ze);

                using (Stream si = GetStreamFromString(textBox1.Text))
                { StreamUtils.Copy(si, zos, new byte[4096]); }

                bool t = zos.IsFinished; 
                  
                zos.Finish();
                zos.Flush();
                zos.Close();
            }

            fso.Close(); 

            button1.Text = "ZIP = " + textBox1.Text.Length;
        }


        public Stream GetStreamFromString(string s)
        {
            MemoryStream stream = new MemoryStream();
            StreamWriter writer = new StreamWriter(stream);
            writer.Write(s);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }

        /// <summary>
        /// Entpacken
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {

            if (!System.IO.File.Exists(Filepath))   return ;

            FileStream fsi = File.OpenRead(Filepath);

            ZipFile zipFile = new ZipFile(fsi);

            ZipEntry ze = zipFile.GetEntry("Testordner\\test.txt".Replace("\\", "/"));

            Stream zs = zipFile.GetInputStream(ze);
            StreamReader sr = new StreamReader(zs);

            string konfig = sr.ReadToEnd();

            sr.Close();
            zs.Close();  
            fsi.Close();
              
            textBox2.Text = konfig;
             
            button2.Text = "UnZIP = " + textBox2.Text.Length;
        } 
    }
}

Vllt könnt ihr den Fehler ja nachvollziehen.
Benötigt wird eine leere Windows Forms Anwendung in der 2x Textbox und 2x Button enthalten ist und die SharpZipLib enthalten sein muss.

Vielen Dank
Cornflake

10.12.2015 - 22:06 Uhr

Hi Leute

Falls nicht schon bekannt hier eine legale Möglichkeit viele Fach-EBooks Online zu bekommen.

Falls es in eurer Nähe, wie bei mir in Nürnberg und Erlangen eine Universität gibt, beherbergt diese dort bestimmt auch eine Universitätsbibliothek.

Der Zugang zu der Bibliothek ist für alle (auch Nichtstudenten) frei. Ihr müsst euch dort nur anmelden.

Dann habt ihr neben den Büchern vor Ort auch Zugang über das Internet.

Einige Verlage wie z.B. Springer und Hansa bieten über den Unibibliothekszugang viele ihrer Bücher für den privaten Gebrauch mit Wasserzeichen zum kostenlosen Download an. (Wie ihr auf die Verlagsseite mit Unizugang kommt ist vom jeweiligen Unibiliothesnetzwerk abhängig.)

Ihr könnt daher einiges an Geld und Aufwand sparen, wenn ihr euch so einen kostenlosen Unibiliothekszugang besorgt.

Sollte eure Bibliothek das nicht anbieten, schaut zur nächsten. Evtl. hat sogar eure Stadtbiliothek so einen Ebook Verlagszugang.

Grüße Cornflake

10.12.2015 - 20:05 Uhr

@MrSparkle: Stimmt hast recht. Static kann über "this" nicht gehen, da es sich nicht auf eine Instanz bezieht. Bin heute anscheinend schon überarbeitet.

@Abt: Im Prinzip haste Recht, aber 20 Properties, Methoden, etc. zu posten, finde ich stiftet vllt. mehr Verwirrung als Nutzen.
Aber beim Nächsten mal versuche ich vom Vorgehen her, erst den Originalcode reinzukopieren und dann nur unnötige Zeilen zu entfernen und abschließend vllt. Variablennamen zu vereinfachen. Dann sollten abstrakt, static, public, etc. Dinge nicht mehr fehlen.
Alternativ vorher ein Miniprojekt mit dem zu postenden Code aufsetzen und den Inhalt dann ins Forum kopieren.

👍 Vielen Dank Leute für eure Mühe und schnellen Antworten.

10.12.2015 - 19:16 Uhr

Leute sorry 8o
Habe gerade meinen Fehler entdeckt.
Das ganze funktioniert ohne casten. Der Grund, warum das Property "Hallo" gefehlt hat, ist dass es "public static" war. Anscheinend darf kein "static" drinnen stehen. 🙁

Daraus würde sich jetzt nur die Frage ergeben, wenn in einer Klasse public Properties mit und ohne static vorhanden sind, wie kann ich json.net sagen, dass die static auch mitgenommen werden sollen?

@MrSparkle: Werde wegen static mit serialisieren in der json.net Doku nachschauen und mich melden, wenn ich nichts entdecke.

10.12.2015 - 19:01 Uhr

@abt: Stimmt habe ich oben vergessen einzutragen. Aber ich glaube nachdem ich die Instanz vom Kind übergebe ist es egal ob die Basisklasse abstrakt ist oder nicht.

@MrSparkle: Wenn ich es richtig sehe müsste ich hier:


public void speichern()
  {
    tool.SerialisiereObjekt((Kind)this);  //Kind
  }

casten, damit beim Serialisieren mit Json.net nicht:


{
  "Welt": "geht"
}

sondern


{
  "Welt": "geht",   //wenn das fehlen würde, würde es mich nicht stören
  "Hallo": "geht nicht"
}

als Ergebnis steht.

10.12.2015 - 18:19 Uhr

Hallo MrSparkle

Danke für deine Antwort.
Wenn ich den ContractResolver richtig verstehe, kann ich damit aus einem Objekt z.B. alle Properties mit einem bestimmten Buchstaben beginnend auflösen. Allerdings ist mir nicht klar, wie ich damit auf die Kindklasse casten soll.

Zum Thema [Tutorial] Konfigurationsmodell im .NET Framework gabs glaube ich schon viele Disskussionen zum MS Konfigurationsmodell. Solange es für mich keine Möglichkeit gibt den Speicherort und Name der XML Datei selber festlegen zu können und ich somit auch z.B. nicht unterschiedliche Konfigurationsdateistände für mein Programm laden kann, ist das MS Konfigurationsmodell für mich nicht praktikabel. Egal ob jetzt wieder Argumente kommen, dass nur dieses Modell die Konfigurationsdatei an einem sinnvollen Ort speichert.

Grüße Cornflake

10.12.2015 - 17:51 Uhr

Naja eigentlich will ich das so ähnlich machen wie bei der Settingsklasse (Settings.settings). Da kannst du Einstellungen festlegen und dann diese auch speichern.

Die Kindklasse beinhaltet die Konfiguration des aktuellen Programmes und die Basisklasse liefert die Möglichkeit die Konfiguration abspeichern zu können. Daher will ich das auch nicht unbedingt trennen. Die Frage stellt sich mir daher eher, wie kann ich in der Basisklasse sagen, das "this" auf die Kindklasse gecastet an die Serialisierungsmethode übergeben wird.
Diese Methode kommt dann eigentlich von Json.net (JsonConvert.SerializeObject(this)).

10.12.2015 - 17:32 Uhr

Hallo

In meiner Basisklasse soll eine Methode zum serialisieren der davon abgeleiteten Kindklasse angeboten werden. Nur leider werden nur die public Werte der Basisklasse gespeichert. Hat dazu jemand eine Lösung?

Hier der Beispielaufbau zur Verdeutlichung:



public class  Basis
{
  public string Welt {get;set;}

  public void speichern()
  {
    tool.SerialisiereObjekt(this);
  }

}

public class Kind : Basis
{
  public string Hallo {get;set;}
}


public class main
{
  //...
  Kind k = new Kind();
  k.Hallo = "geht nicht";
  k.Welt = "geht";
  k.speichern();
  //...
}


In "this" steckt eine Kind Instanz, nur leider wird beim serialisieren nur die der "Welt" Wert serialisiert und nicht der "Hallo" Wert.

Wie kann ich das lösen?

Grüße Cornflake

09.12.2015 - 20:37 Uhr

Hi

Thx für eure Antworten.
Habe leider nicht mehr die Seite gefunden, bei der dieser Geschwindigkeitsvergleich vorgenommen wurde. Bin mir aber sicher das dort stand, dass per sqlite3.exe der Import wesentlich schneller als der Import per C# sein soll.
Vllt ist das inzwischen auch anders.

07.12.2015 - 14:20 Uhr

@FZelle ok schade.

Naja wenn ich eine csv Tabelle importieren möchte, dann gibts den Importaufruf von sqlite.exe per Commandzeile.
sqlite> .mode csv
sqlite> .import C:/work/somedata.csv tab1

Diese Art des Imports ist bei sehr langen CSV Dateien wesentlich schneller, als bei anderen rein C# basierenden Aufrufen. Dazu wird aber die sqlite.exe benötigt.

Jedenfalls bei meinem aktuellen Projekt brauch ich den nicht, daher werde ich die Methode auskommentieren.

07.12.2015 - 12:43 Uhr

Hallo Leute
Danke für eure Antworten. Im Prinzip werde ich wohl das dann manuell anpassen.
Ich habe nur aktuell den Fall, dass ich eine SQLite Import Methode geschrieben hatte, die als Hilfmethode bei einigen Projekten zum Einsatz kommt, aber eben nicht bei allen. Jedenfalls benötigt dieser schnelle Import die SQLite.exe und die wird daher bei dem Hilfmethodenklasse mitgegeben und beim kompilieren auch in den Ausgabeordner kopiert. Nur für mein jetziges Projekt brauche ich diese z.B. nicht.

Werde daher vllt. nur diesen Part ändern, dass die SQLite.exe nicht immer mit in den Ausgabeordner kopiert wird, wenn nicht vorhanden.

Am besten wäre es wohl die Hilfsmethodenklasse so zu strukturieren, dass bestimmte Teile deaktiviert werden können. Habe nur keine Ahnung wie ich das am besten mache. Sollte ich eine Hilfmethoden Solution anlegen und da zu jedem Thema (Datenbanken, Controls, Dateihandling,...) ein eigenes Projekt anlegen, dann kommen zum Schluss x Dll Dateien raus und am liebsten wäre es mir eigentlich, wenn ich nur eine Dll habe (auch wenn intern bei der Programmierung gerne viele Klassen zur Strukturierung eingesetzt werden). Noch besser wäre es z.B., wenn nur die wirklich benötigten Hilfsmethoden mit in der exe Datei stehen, da ich die separat eigentlich nicht benötige.
Dass hätte für mich den Vorteil, dass ich zum Weitergeben des Programms nur die eine Exe benötige (und .NET Framework vorausgesetzt). Denn die Benutzer interessiert es nicht, wie toll ich Klassenbibliotheken verwende. Denen wäre einfach nur eine Datei kopieren am liebsten.

@FZelle: Ist das von Xamarin ein separates Tool, dass dies zusammenstellt?

Grüße Cornflake

04.12.2015 - 16:52 Uhr

Hallo Leute

Bei meinen Projekten haben sich mit der Zeit nützliche immer wieder verwendete Methoden angesammelt. Diese liegen bei mir in einem DLL Hilfsmethoden-Projekt, dass ich bei anderen Projekten einbinde.
Natürlich brauche ich bei den Projekten nicht jede Hilfsmethode aus meinem DLL Hilfsmethoden-Projekt.

Daher die Frage: ?(
Kann beim Kompilieren nicht benötigter Code aus eingebundenen DLLs entfernt werden?
Oder kann ich anderweitig im Vorfeld per Script etc. aus einem Projekt nur die benötigten Methoden mit Zwischenverweisen extrahieren?

Grüße Cornflake

04.12.2015 - 16:45 Uhr

Hi
Leider kenn ich die SW-/Hardware von dir nicht. Aber der Debugvorgang, bremst meines Erachtens die Programmverarbeitung wenn auch nur minimal aus. Vllt haste sogar noch an bestimmten Stellen im Code Haltepunkte gesetzt, die weitere Timeouts (in dem Fall benötigte) verursachen.
Oder du hast programmiermäßig irgendwelche Memoryleaks (Variablen die verloren gehen) und im normalen Programmaablauf eher wieder aufgeräumt werden, als im Debuggermodus.
Nur so als Anregungen zur Fehlersuche.

Grüße Cornflake

03.12.2015 - 16:21 Uhr

Hallo Palladin
Das mit dem negativen Suchen ist nur, da ich den RegEx nicht direkt in C# schreibe, sondern in einem Programm, dass RegEx unterstützt.
Ich kann nur "Suchen nach" und "Ersetzen durch" machen.
Daher wenn ich alles löschen will ausser "54" muss ich alles mit Regex finden, außer "54".

Habe es jetzt hinbekommen. Danke für deine Antwort.
Das mit dem www.regex101.com kannte ich noch nicht. geniales Teil 😃 THX

Grüße Cornflake

03.12.2015 - 15:22 Uhr

Hallo Leute

Weiß jemand eine Lösung zu folgender Situation:
x = beliebiger Text

VorgabeTabelle


xxxx44xxxxx
xxxx54xxxxx
xxxx45xxxxx
xxxx46xxxxx
xxxx55xxxxx
xxxx54xxxxx
xxxx56xxxxx
xxxx46xxxxx

Jetzt will ich nur die Zeilen behalten, die ab stelle 5 ein "54" enthalten. Die restlichen Zeilen sollen weg.

Wisst ihr wie man das per RegEx lösen kann?

Mein aktueller Entwurf wäre:


^.{4}(?!54).+\r\n

Das findet er aber nicht, da wie ich rausgefunden habe,
der Assert (look behind, look ahead) mit (?...) nicht zwischen zwei Begriffen stehen kann, sondern nur am Anfang oder Ende.

Gibt es denn bei RegEx eine Lösung für eine "negativ" Suche ?(

Grüße Cornflake

01.12.2015 - 15:55 Uhr

Hallo Leute

Ich glaube ich habe eine Lösung.


//MySpalten ist ein String[] Array mit den Spaltennamen.
DataRow[]  myDataRows = new DataView(MyDataTable).ToTable("", true, MySpalten).Select();              

Grüße Cornflake

01.12.2015 - 15:18 Uhr

verwendetes Datenbanksystem: DataTable

Hallo Leute

Von einer DataTable möchte ich in eine DataView oder DataRow[] Liste nicht nur Zeilen filtern, sondern auch nur bestimmte Spalten übergeben.
Kennt Ihr einen Standardweg dazu?
Ich hatte gehofft das sowas geht, wie:


DataRow[]  myDataRows = MyDataTable.select("Spalte1, Spalte2, Spalte5");

in myDataRows, sollen dann auch nur diese drei Spalten stehen und nicht noch die x anderen.

Grüße Cornflake

25.11.2015 - 13:36 Uhr

Habe eine Hilfsmethode (siehe unten) gefunden. Damit klappt das fast "generell" für alle LINQ Abfragen.

Es funktioniert, wenn ich ein
LINQ mit ... select new { <Spaltenangaben> };
schreibe, da es hier kein CopyToDataTable() gibt.

Allerdings bei einem
LINQ mit ... select <Tabelle>;
geht das nicht, da PropertyInfo nicht den Inhalt, sondern die DataTable Struktur zurück gibt und da scheint es mehrmals eine "Item" Spalte zu geben.
Dafür gibts an dieser Stelle wieder ein CopyToDataTable() und ich komme im Prinzip hin.
Jetzt würde ich gerne, die Methode um eine Erkennung für CopyToDataTable() erweitern, damit wenn diese Möglichkeit vorhanden ist, dieser Weg genommen wird. Leider weiß ich nicht wie ich das hier einbauen kann. Siehe Code Kommentar.


public static DataTable ConvertToDataTable<T>(IEnumerable<T> varlist)
        {
            //!! Hier weiß ich nicht wie auf auf CopyToDataTable prüfen kann.
            //if (varlist.Contains(CopyToDataTable))
            //    return varlist.CopyToDataTable(); 

            DataTable dtReturn = new DataTable();

            // column names 
            System.Reflection.PropertyInfo[] oProps = null;

            if (varlist == null) return dtReturn;

            foreach (T rec in varlist)
            {
                // Use reflection to get property names, to create table, Only first time, others will follow 
                if (oProps == null)
                {
                    oProps = ((Type)rec.GetType()).GetProperties();
                    foreach (System.Reflection.PropertyInfo pi in oProps)
                    {
                        Type colType = pi.PropertyType;

                        if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                        {
                            colType = colType.GetGenericArguments()[0];
                        }

                        dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
                    }
                }

                DataRow dr = dtReturn.NewRow();

                foreach (System.Reflection.PropertyInfo pi in oProps)
                {
                    dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
                    (rec, null);
                }

                dtReturn.Rows.Add(dr);
            }
            return dtReturn;
        }

25.11.2015 - 11:57 Uhr

👍 Danke Palin.
🙂 Das war eine super Erklärung. Jetzt wird es mir so langsam klarer.

🤔 Allerdings hänge ich noch an den Punkten:

  • Laden der Daten über ein Repository oder Service. Wie funktioniert das?
  • Instanzierung mit einem Inversion of Control Container. Wie funktioniert das?
  • Was meinst du mit DI?

Das ganze soll übrigens für lokale Anwendungen (VS2008 C# 3.5) gelten.

Wegen meiner Angabe am Anfang, da hatte ich eigentlich mein Beitrag mit dem Ablauf (Model.cs, Controller.cs und View.cs) und Instanzierung gemeint.

Wenn ich dich richtig verstanden habe, dann würde das jetzt wie folgt aussehen:

Ablauf MVC Version 2:1.Repository/Service (Zuständig für das Laden und Speichern von Dateien)

  • Repository.cs = Methoden zum Laden und Speichern von z.B. CSV Dateien erstellen
  • IRepository.cs = Interface für (Methoden) erstellen.

1.Model (Zuständig für die Datenhaltung in DataTables, bietet Methoden und Events an)

  • AModel.cs = Abstrakte Klasse für Model erstellen.
  • Model.cs = Model von abstrakter AModel Klasse erstellen, das Implementierung des IRepository nutzt (Repository Methoden).
  • IModel.cs = Interface für Model erstellen (Methoden und Events)

1.View (Zuständig für Datenanzeige, Oberflächendesign, optisches Programmverhalten)

  • AView.cs = Abstrakte Klasse für View erstellen.
  • View.cs = View von abstrakter AView Klasse erstellen, die Implementierung des IModel nutzt (Model Events)
  • IView.cs = Interface für View erstellen (Methoden und Events)

1.Controller (Zuständig für Datenanpassungen, Programmlogik, Regeln)

  • AController.cs = Abstrakte Klasse für Controller erstellen.
  • Controller.cs = Controller von abstrakter AController Klasse erstellen, der Implementierung des IModel nutzt (Model Methoden und Events) und IView nutzt (View Methoden und Events)

1.Instanzierung (Instanzierung der Teile: Repository, Model, View und Controller)

  • ioc.cs = Winforms Anwendung, die die anderen Klassenbibliotheken/Projekte instanziert

  • Repository instanzieren

  • Model instanzieren (Bekommt Instanz des Repository mit übergeben)

  • View instanzieren

  • Controller instanzieren (Bekommt Instanzen des Model und View mit übergeben)

⚠ Ich weiß laut MS Naming Konvention gibts kein A vor Namen von abstrakten Klassen. Finde ich hier aber lesbarer.

?(
Wäre dieser Ablauf korrekter?
Was müsste für MVVM eingefügt werden?

24.11.2015 - 19:26 Uhr

Ok jetzt bin ich verwirrt.

Kann mir jemand für MVC erklären was wo wie geschrieben wird, ähnlich wie ich das am Anfang gemacht habe.

Also wer (welche Klasse) bekommt eine Referenz auf welche Instanz übergeben und wer stellt ein Interface zur Verfügung und wer nutzt welches Interface?
Vllt. dann auch gleich im Vergleich zum MVVM.

Vielen Dank für eure Mühe.
Grüße Cornflake

23.11.2015 - 19:24 Uhr

Das wäre zu diskutieren.
Es handelt sich ja um eine Windows-Forms Anwendung.
Laut [Artikel] Drei-Schichten-Architektur arbeitet die Präsentation Schicht hauptsächlich mit der Controller Schicht. Auf die Model Schicht wird nur in Ausnahmefällen zugegriffen.
Wegen den Präfixen; ja die müssten Großbuchstaben sein. Also IModel.cs statt iModel.cs.
Den Namen Controller.cs habe ich nur Beispielhaft genommen. In Wirklichkeit können dass ja auch mehrere sein.

Würdest du bei einer lokalen Anwendung in der Model Schicht, trotzdem mit Services arbeiten? Ich glaube, das wäre zu viel des Guten. Was wäre dazu die Alternative?

23.11.2015 - 15:59 Uhr

Hallo Leute

Zu dem MVC Thema habe ich den Aufbau festgelegt und dazu noch eine Frage, ob dieser Gedanke so passt.
@Christian: Den MVC Beitrag habe ich gesehen. Das Diagram finde ich im Prinzip gut. Wegen den Interfaces usw. habe ich mir jetzt folgende Details überlegt.

Ablauf:1.Model (Zuständig für Dateien-laden -speichern, Datenbanken und DataTables)

  • aModel.cs = Abstrakte Klasse für Model erstellen.
  • Model.cs = Model von abstrakter aModel Klasse erstellen.
  • iModel.cs = Interface für Model erstellen (Methoden, Events, Properties)

1.Controller (Zuständig für Datenanpassungen, Programmlogik, Regeln)

  • aController.cs = Abstrakte Klasse für Controller erstellen.
  • Controller.cs = Controller von abstrakter aController Klasse und Implementierung des iModel (nutzt Model Methoden, Properties und Events)
  • iController.cs = Interface für Controller erstellen (Methoden, Events, Properties)

1.View (Zuständig für Datenanzeige, Oberflächendesign, optisches Programmverhalten)

  • aView.cs = Abstrakte Klasse für View erstellen.
  • View.cs = View von abstrakter aView Klasse und Implementierung des iController (nutzt Controller Methoden, Properties und Events)
  1. Instanzierung
  • Model instanzieren
  • Controller instanzieren (Bekommt Instanz des Model mit übergeben)
  • View instanzieren (Bekommt Instanz des Controllers mit übergeben)

Wenn die View Daten anzeigen will, ruft sie Methoden des Controllers auf. Dieser holt per Methoden DataTables aus dem Model und passt diese per Logik für die View an. Danach meldet er per Event der View die angepasste DataTable.

Sind die einzelnen Schichten des MVC in seperaten Projekten aufgeteilt, würde ich dann ein zusätzliches Shared Klassenbibliotheks Projekt anlegen, in dem die Interface Klassen abgelegt werden und die Instanzierung statt findet.

Das wäre doch eine universell nutzbare Struktur, Vorgehen für die meisten Projekte.
Habe ihr dazu noch Verbesserungen?

Grüße Cornflake

16.11.2015 - 14:09 Uhr

Hi
Habe den Eintrag gesehen, aber verstehe ihn nicht wirklich.
Wenn ich das richtig sehe, bezieht sich der auf ein Dictionary Objekt, dass über IEnumerable um eine DataRow Rückgabe erweitert wird. Da ich aber schon DataTables als Vorlage habe, weiß ich nicht, wie ich das von Dictionary übertragen kann. Wenn ich das richtig sehe würde es aufgrund des "select new" im LINQ da auch nicht anwendbar sein.

16.11.2015 - 13:30 Uhr

Hallo Christian
Prinzipiell haste ja recht, dass die 3 Schichten alles einfacher machen und ich habe meine Solution auch so aufgebaut, aber dennoch hilft das hier mir nicht weiter.

Mein Button in der Präsischicht ruft die Logikschicht auf. diese soll aus der Datenschicht die DataTables holen, damit in der Logikschicht diese entsprechend aufbereitet werden (join, sort, filter) um die Ergebnis-Datatable der Präsischicht über ein Ereignisrückmeldung zurückzuliefern. Die Präsischicht abboniert das Ereignis, JoinDatatableIstFertig.
Wenn ein Haken gesetzt wird, würde ich jetzt der Logikschicht melden, dass in Zeile ein Haken gesetzt wurde oder nicht und diese soll dass dann in der Datenschicht durchführen.

Leider weiß ich noch nicht genau, wie ich das umsetze. Jedoch habe ich wegen joinen der Datatables jetzt rausgefunden, dass Linq das können soll.

Dabei steht wieder ein Problem im Raum.
Wenn ich z.B.


var result = from tw in TabWegpunkte.AsEnumerable()
                              join tg in TabGeprüft.AsEnumerable()
                              on tw.Field<string>("WegpunktNr") equals tg.Field<string>("WegpunktNr")
                              select new
                              {
                                  Geprüft = tg.Field<string>("Geprüft"),
                                  WegpunktNr = tw.Field<string>("WegpunktNr"),
                                  Spielername = tw.Field<string>("Spielername"),
                                  Zeitpunkt = tw.Field<string>("Zeitpunkt"),
                                  Bemerkung = tw.Field<string>("Bemerkung")
                              };

                //Group by habe ich noch nicht hinbekommen, aber selbst bei join geht CopyToDataTable() nicht
              
  DataTable TabErgebnis = result.CopyToDataTable(); //Geht nicht, da anscheinend keine DataRow

Laut Microsofts eigener Seite müsste es nach dem "Example" 2 unter "Creating a Custom CopyToDataTable<T> Method" funktionieren, aber es klappt nicht.
MSDN Creating a DataTable From a Query (LINQ to DataSet) .Net 3.5

Wie komme ich hier weiter? Oder hat jemand von euch noch einen ganz anderen Ansatz das ursprüngliche Problem zu lösen?

Viele Grüße
Cornflake

13.11.2015 - 16:07 Uhr

verwendetes Datenbanksystem: DataSet, VS2008

Hallo Leute

In einem DataGridView will ich Inhalte aus zwei gejointen Tabellen darstellen. Die Inhalte kommen aus zwei CSV Dateien. In der einen CSV Datei kommen bei wiederholtem Einlesen immer mehr Datensätze dazu. In der anderen CSV Datei werden geprüfte Datensätze als abgehakt zurück geschrieben. Das einlesen klappt. aber das richtig darstellen, bzw. zurückschreiben klappt noch nicht, bzw. da stehe ich auf dem Schlauch.

Stellt euch vor, jemand schickt euch in regelmäßigen Abständen (alle 5 Minuten) eine anwachsende CSV-Datei in der Wegpunkte und Spielteilnehmer stehen. Diese Angaben prüft ihr und hakt sie ab, wenn ihr einen Wegpunkt als abgeschlossen anseht. Das ganze soll in einer DataGridView angezeigt werden.


# Tabellen:
TabWegpunkte  (WegpunktNr, Spielername, Zeitpunkt, Bemerkungen)
TabGeprüft       (WegpunktNr, Geprüft)

# DataRelation:
TabWegpunkt.WegpunktNr (n) zu (1) TabGeprüft.WegpunktNr

# DataGridView Anzeige entspricht SQL:
select
   TabGeprüft.Geprüft , 
   TabWegpunkte.WegpunktNr, 
   group_concat(TabWegpunkte.Spielername) as 'Spielername', 
   group_concat(TabWegpunkte.Zeitpunkt) as 'Zeitpunkt', 
   group_concat(TabWegpunkte.Bemerkungen) as 'Bemerkung'
from
   TabWegpunkt
join 
  TabGeprüft
   on TabWegpunkt.WegpunktNr = TabGeprüft.WegpunktNr
group by
   TabWegpunkt.WegpunktNr

# Hinweis: group_concat soll alle Spielernamen, etc. in der 
Gruppe des Wegpunktes zusammenfassen und sie in einer Zelle darstellen.

Das Problem:
Wenn jetzt in der Spalte mit der Checkbox ein Wegpunkt abgehakt wird, soll dass in die Tabelle TabGeprüft zurück geschrieben und in der CSV Datei gespeichert werden. Daher diese Tabelle erweitert sich damit um neue Einträge und bestehende Einträge werden geändert.
Nur wie bekomme ich dass hin? Zum Einen zwei Tabellen joinen als select und zum Anderen nur in einer inserten bzw. updaten?

Aktuell joinen ich per foreach die beiden Tabellen und stelle Sie als Datasource dem DataGridView zur Verfügung. Nur wenn jetzt ein Häckchen gesetzt wird, steht das zwar in der Jointabelle, aber ich muss das ja irgendwie in der CSV Datei zurückschreiben? X(

Ich habe schon überlegt mit einem TableAdapter zu arbeiten. nur der verbindet sich mit Datenbanken und nicht mit DataTables. Und per Code kann ich den auch nicht aufbauen.

Habt ihr eine Idee ?(

Viele Grüße Cornflake

12.11.2015 - 16:34 Uhr

Hallo Leute

Danke für euere Feedbacks. 🙂
BL und DAL scheinen dann allgemein verwendete Begriffe zu sein.

Für die zukünftige Entwicklung, finde ich die Unterscheidung zwischen WebApp, ConsoleApp und DesktopApp gut.

Wegen Microservices bin ich gerade am schauen, dass scheint ein ziemlich neues Thema zu sein, dass sich gerade verbreitet. Gibts dazu für .net schon gute Bücher? Wenn ich das richtig sehe, würde das Programm an sich so eine Art SOA darstellen, nur ohne Webservices oder SOAP. Muss ich mich dann in MessageQueues einlesen?

Wegen der Trennung der Interfaces vom Rest. Da werde ich dann ähnlich von Abt vorgeschlagen ein seperates Projekt verwenden, bzw in den SHARED Bereich verlagern.

Vielen Dank für eure Antworten.

11.11.2015 - 18:07 Uhr

Hallo Leute

Mein Ziel ist es meine Programme besser wartbarer nach einem wiederverwendbaren Konzept zu entwerfen.

Bisher lege ich eine Solution mit Projektname an. In dieser mein Windows Form Projekt und dann verschiedene Klassen. Die Program.cs instanziert die Form1.cs und ist im Prinzip meine "View". Innerhalb dieser werden die anderen Klassen für Geschäftslogik und DataSet mit dem "Controller" und "Model" Part instanziert. Wiederkehrende Hilfsfunktionen habe ich in ein seperates Projekt als Klassenbibliothek ausgelagert.

Dieses Vorgehen finde ich langfristig für größer werdende Projekte nicht nutzbar.
Daher habe ich in letzter Zeit versucht meine Softwarearchitektur zu verbessern und im Internet nach Alternativen gesucht.

Eine Struktur die mir bisher gefällt ist unter: http://www.codingfreaks.de/2014/08/25/eine-solution-bauen zu finden.

Also habe ich jetzt in der Solution je ein Projekt für Shared, Ui (View), Logic (Controller) und Data (Model) angelegt.

Zudem habe ich abweichend ein "Projektname" Projekt als Windows-Forms Projekt angelegt, da ich sonst das Ganze nicht starten kann. In diesem Hauptprojekt instanziere ich die anderen Projekte.

Da ich wiederkehrend Programme habe, die ähnlich aufgebaut sind, habe ich folgende aktuelle Beispielsituation hergenommen.


UI (Ein WinForm Projekt mit DataGridView einem Button zum Aktualisieren und 
      einem Label mit Anzahl Zeilen des GDV. Im DGV werden zwei Spalten mit Werten angezeigt, die sich ändern lassen können.)

DATA (Ein Projekt mit DataSet und enthaltener Tabelle. Im DS werden die 
      angezeigten Werte gespeichert und lassen sich aus einer CSV-Datei laden.)

LOGIC (Ein Projekt mit der Business Logik. Wenn der Button geklickt wird, soll 
      die Tabelle im DS von der CSV gefüllt werden. Änderungen an der Anzeige 
      sollen an das DS weitergeleitet und in der CSV gespeichert werden.)

MAIN (Ein Projekt, dass die anderen Projekte verbindet, instanziert und mit 
      static Main(){} den Einsprungpunkt in die gesamte Solution bietet und Interfaces definiert )

SHARED (Beinhaltet Dateien, die in alles Projekten verwendet werden, 
      wie z.B. eine gemeinsame assemblyinfo.)

Nun zum Problem:
Damit MAIN die Projekte UI, DATA und LOGIC instanzieren kann, benötige ich Verweise auf diese. Soweit so gut. UI soll die IF von MAIN erfüllen und daher einbinden. Dazu muss UI auf MAIN verweisen. Und da knallt es, wegen einem Zirkelbezug zwischen den Verweisen. Ich bin jetzt am überlegen, die IF von MAIN nach SHARED zu verlegen, aber fürchte es wird diesbezüglich noch mehr Schwierigkeiten geben.

Die Frage:
Hat jemand von euch in der Richtung Erfahrung und wie würdet Ihr das lösen?
Gibt es bessere Benennungen? Hab hier im Forum etwas von DAL und BL gelesen, weiß aber nicht ober das so allgemeingültig ist.

Noch als Hinweis ich habe nur Visual Studio 2008 zur Verfügung und kann daher kein Entity Framework nutzen!

Grüße Cornflake