Laden...

Forenbeiträge von inflames2k Ingesamt 2.298 Beiträge

09.10.2017 - 10:54 Uhr

Lässt sich schwer sagen ohne weitere Informationen über dein Projekt zu haben. Insofern ist nun die Frage, hast du die Datei korrekt in deinem Projekt? Wie sieht deine Projektstruktur aus?

Sind eventuell Fehler in der Klasse oder in der Service-Klasse vorhanden die das korrekte Einlesen behindern?

05.10.2017 - 13:59 Uhr

Hallo,

wie schreibst du denn die Datei?

Außerdem ist das ein Fall den ich eigentlich nicht genauer betrachten würde. In der Regel stürzt ein Rechner ja nicht genau in dem Moment ab, wo du deine Anwendung beendest.

25.09.2017 - 09:51 Uhr

Hallo,

der sauberste Weg wäre, mit Hilfe von C++/CLI einen "Wrapper" um die C#-Bibliothek zu schreiben und im C++ dann auf die neue C++/CLI Assembly zu verweisen.

Using .NET Classes/Modules from Native C++

20.09.2017 - 11:48 Uhr

Da bisher keiner eine Antwort hat, habe ich mal ein Minimalbeispiel für die Anzeige und Filterung erstellt. Im Anhang ist dieses zu finden.

Beschränkt habe ich mich auf Message und Kategorie zum FIltern, da dies die Problemkinder sind. Gefiltert wird über das Kontextmenü des DataGridViews.

// EDIT:
Je mehr Einträge vorhanden sind, desto länger dauert es. Aufgrund meiner "Speicherung" der Meldung und Kategorie im Objekt nachdem diese das erste mal gelesen wurden, betrifft dies zum Glück nur den ersten Filtervorgang.

// EDIT2:
Um noch Zeiten zu liefern, wie viel länger es dauert FormatDescription direkt bei der Initialisierung zu erstellen:

Elapsed Reading without initializing: 00:00:03.2891373
Elapsed Reading with initializing: 00:13:11.0264655

Bei gleicher Datenmenge, versteht sich.

20.09.2017 - 08:07 Uhr

Hallo,

das Panel für den Browser soltlest du dir eigentlich sparen können. Für mich sieht das eher aus, als wäre der Index auf der Oberfläche falsch.

Das später hinzugefügte Control sollte in der Regel nur den Bereich haben, der auch tatsächlich frei ist.

19.09.2017 - 16:54 Uhr

Ich vermute, er bezieht sich auf den unnötigen cast im If.

19.09.2017 - 16:49 Uhr

Was passiert denn stattdessen mit deinem Chromium?

19.09.2017 - 16:15 Uhr

Das einfachste wird es sein, den Button und die Listbox in ein Panel zu packen. Dieses kannst du dann mit DockStyle.Top oben andocken. Danach wird bei DockStyle.Fill der Browser unten angedockt.

18.09.2017 - 21:13 Uhr

Hallo,
das Graphicsobjekt besitzt Eigenschaften für das smoothing und Antialising. Ich denke mit denen solltest du das ganze glatter bekommen.

18.09.2017 - 17:02 Uhr

Hallo,

in einem Programm zur Logauswertung werden die Einträge des Ereignisprotokolls ermittelt und angezeigt. Das funktioniert soweit super.

Verwendet wird der EventLogReader zum Auslesen der Einträge, da die EventLogEntry-Klasse nicht alle benötigten Informationen bereitstellt.

Nun ist allerdings das Problem, dass die Meldung des Ereignisses im EventLogRecord erst zur Abfragezeit ermittelt wird und dies z.T. bis zu 2 Sekunden dauert. Das führt teilweise dazu, dass schon bei nur 30 Einträgen eine Minute erforderlich ist zur Ermittlung der Meldungen.

Gibt es einen effizienteren Weg, die Meldung zu erhalten? Via google treffe ich leider nicht auf vielversprechende Beiträge.

18.09.2017 - 16:11 Uhr

Ohne weitere Informationen zu dem Webservice wird es schwer dir zu helfen. In der Regel ist es die richtige Vorgehensweise, sich den Proxy von Visual Studio generieren zu lassen, so wie du es auch gemacht hast.

Bzgl. deiner Frage: Es wurden Klassen für tlist und tlogin erstellt. Insofern sollte manuelles arbeiten mit Xml nicht notwendig sein.

18.09.2017 - 14:55 Uhr

Stimmt denn die Url zum SOAP Service? Wenn nicht kommt es auch vor, dass einfach eine ASP.NET Fehlerseite aufgerufen wird die dann HTML zurück liefert. Hast du die Service-Adresse mal im Browser aufgerufen?

18.09.2017 - 14:50 Uhr

Hallo,

auch wenn du schon eine Lösung hast, wäre es eventuell eine Option von der ButtonCell und ButtonColumn abzuleiten.

Ich habe da gerade mal etwas rumprobiert. Folgendes kam dabei raus (noch nicht optimal):


    public class DataGridViewButtonCellEx : DataGridViewButtonCell
    {
        private bool _hideButton;

        public bool HideButton
        {
            get { return _hideButton; }
            set { 
                _hideButton = value;                
            }
        }

        protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            if (!this._hideButton)
                base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
            else
            {
                graphics.FillRectangle(Brushes.White, cellBounds);
                using (Brush b = new SolidBrush(Color.Black))
                {
                    Font f = this.Style.Font != null ? this.Style.Font : this.DataGridView.DefaultCellStyle.Font;
                    SizeF size = graphics.MeasureString(formattedValue.ToString(), f);

                    graphics.DrawString(formattedValue.ToString(), f, b, cellBounds.X + (cellBounds.Width - size.Width)/2, cellBounds.Y + (cellBounds.Height - size.Height)/2);
                   
                }
            }   
        } 
    }

    public class DataGridViewButtonColumnEx : DataGridViewButtonColumn
    {        
        public override DataGridViewCell CellTemplate
        {
            get
            {
                if (!(base.CellTemplate is DataGridViewButtonCellEx))
                    base.CellTemplate = new DataGridViewButtonCellEx();

                return base.CellTemplate;
            }
            set
            {
                if (value is DataGridViewButtonCellEx)
                    base.CellTemplate = value;
                else
                    throw new ArgumentException("The cell template has the wrong style!");
            }
        }
    }

Wird für die Zelle HideButton auf true gesetzt, wird nur der Text gezeichnet.

Das Ergebnis sieht dann wie folgt aus:

18.09.2017 - 09:13 Uhr

Verschiebe das base.OnPaint an den Anfang deiner Methode, dann funktioniert auch das zeichnen.

Der Button übermalt sonst dein gezeichnetes einfach wieder.

15.09.2017 - 08:31 Uhr

Nach weiterer Recherche muss ich nun wohl einsehen, dass der Filter selbst wohl nicht die Ursache ist. Wie mir scheint liegt es an meiner Ereignisprotokollauswertung selbst und wie ich meine internen Objekte aufgebaut habe.

Das Problem ist wohl nicht das Filtern sondern am Beispiel EventLogs die EventRecord-Klasse und deren Methode "FormatDescription". - Mal sehen ob ich meinen Programmablauf optimieren kann.

Insofern erkläre ich das Thema erst einmal für beendet.

14.09.2017 - 17:09 Uhr

Hallo Th69,

werde ich mir mal ansehen. Das Problem tritt aber bei allen String-Operationen auf (Contains, Equals, ...).

14.09.2017 - 17:02 Uhr

Mache ich etwas verkehrt? Folgendes ändert nichts am Laufzeitverhalten:


BlockingCollection<EventLogEntryDetails> filteredCollection = new BlockingCollection<EventLogEntryDetails>();
// aufteilen der eingabemenge
IEnumerable<IEnumerable<EventLogEntryDetails>> chunked =  this.Chunk(info.Entries, 5000);
chunked.AsParallel().ForAll((list) =>
{
    BlockingCollection<EventLogEntryDetails> currentEntries = new BlockingCollection<EventLogEntryDetails>();
    list.AsParallel().ForAll(
                        (entry) =>
                        {
                            if (filter.Matches(entry))
                                currentEntries.Add(entry);
                        });

    foreach (EventLogEntryDetails details in currentEntries)
        filteredCollection.Add(details);
});        

14.09.2017 - 16:25 Uhr

Huh,

ich habe das jetzt mal so angepasst, dass jeder Filter sich die auszuführende Operation merkt. Dazu habe ich einen Delegate wie folgt definiert:


delegate bool FilterDelegate(Object value);

Dieser wird beim ersten Aufruf der Methode Match ermittelt und für alle weiteren Aufrufe wiederverwendet. Einzige Änderung die an einem Filter durchgeführt werden kann ist die Aktivierung / Deaktivierung. - Daher ist ein UnPrepare nicht nötig. Ein ändern des Filters führt immer zur Generierung eines neuen Filter-Objektes.

Ein Beispiel der Zuweisung des Delegates für den Filter:


case FilterCondition.Contains:
      filterDelegate = (object value) => value.ToString().Contains(this.ConditionValue);
      break;

Die Match-Methode sieht nun wie folgt aus:


public bool Match(ILogEntry<TEventType> entry)
{
   bool matches = false;

   if (this._filterMethod == null)
         this._filterMethod = this.EvaluateFilterMethod(entry);

  object sourceValue = this.GetSourceValue(entry);
  matches = this._filterMethod(sourceValue);

  return matches;
}

Allerdings ist das filtern dadurch nicht wesentlich schneller gewurden. Der nächste Schritt ist dann wohl tatsächlich das sinnvolle Aufteilen der Datenmenge und Parallele Filtern dieser.

14.09.2017 - 13:37 Uhr

Grundsätzlich stimme ich euch zu.

Allerdings führt ja der Aufruf von filter.Matches(entry) in meinen SQLLogFilter, also in die Methode


 /// <summary>
        /// Method to match the details to the filters
        /// </summary>
        /// <param name="details">the details</param>
        /// <returns>true if the details matches</returns>
        public bool Matches(SQLLogEntry details)
        {
            bool matches = true;

            // match time
            matches = this.Filters["EventTimeFilter"].Matches(details) &&                      
                      this.Filters["EventFilter"].Matches(details);

            return matches;
        }

this.Filters["EventFilter"] enthält einen LogFilter. Der Aufruf von Matches führt zu folgender Methode:


        /// <summary>
        /// Method to check if the event log entry matches a filter
        /// </summary>
        /// <param name="dataItem">the event log entry</param>
        /// <returns>true if the entry matches a filter and should be shown</returns>
        /// <remarks>
        /// Matching will be done in the following order:
        /// 1) Match Source filters
        /// 2) Match eventID filters
        /// 3) Match message filters
        /// </remarks>
        public bool Matches(ILogEntry<TEventType> dataItem)
        {
            // match source filters, ID Filters and message filters
            return this.MatchSourceFilters(dataItem) && this.MatchEventIDFilters(dataItem) && this.MatchMessageFilters(dataItem) && this.MatchCategoryFilters(dataItem);
        }

Alle Methodenaufrufe darin führen später dann zu:


 /// <summary>
        /// Method to match filters
        /// </summary>
        /// <param name="dataItem">the event log entry</param>
        /// <param name="direction">the filter direction</param>
        /// <param name="column">the filter column</param>
        /// <returns>true if the event log entry matches a filter</returns>
        private bool MatchFilters(ILogEntry<TEventType> dataItem, FilterDirection direction, FilterColumnType column)
        {
            bool matches = false;
            IEnumerable<FilterEntry<TEventType>> entries = this.Entries.Where((x) => x.FilterColumn == column && x.FilterDirection == direction && x.Active);

            if (entries.Count() == 0)
                matches = direction == FilterDirection.Include ? true : false;

            foreach (FilterEntry<TEventType> entry in entries)
            {
               // oder verknüpfung
               matches = matches || entry.Match(dataItem);   
            }

            return matches;
        }

Wie man sieht werden also möglicherweise auch mal 10 oder 20 Filter angewendet und nicht nur einer. Wie ich mir hier also für die einzelnen Filter merken soll, was zu tun ist müsste man mir also noch einmal erklären. Im groben und ganzen verstehe ich aber auch nicht, was das bringen soll.

Der ENUM-Wert ändert sich während der Filterung nicht. Ich nehme nicht an, dass das Switch jetzt den großen Einfluss hat, eher das Contains, Equals etc.

Mit PLinq werde ich mir mal ansehen wie ich das sauber hinbekomme.

14.09.2017 - 12:00 Uhr

Hallo,

zur Auswertung von Log-Einträgen (SQL-Logs, Windows Ereignisprotokolle) verwenden wir intern ein Programm, welches die Log-Einträge einliest.

Speziell bei SQL-Logs aber auch im Windows-Ereignisprotokoll kann es vorkommen, dass mit Datenmengen über 30000 Einträgen zu rechnen ist.

Nun haben wir eine Filterfunktion ähnlich der aus dem Process Monitor von Sysinternals erstellt. Damit kann auf einzelne Property-Werte der Log-Einträge gefiltert werden. Nun ist es jedoch so, dass speziell bei String-Werten die Filterung sehr lange dauert. Meine Frage ist nun, ob ihr eine Idee habt wie die Filterung beschleunigt werden kann.

Grundsätzlich wird eine Reihe von Filtern nacheinander angewendet. Ein Filtereintrag sieht wie folgt aus:


public class FilterEntry<TEventType> : INotifyPropertyChanged
    {
        private bool _active;

        public bool Active
        {
            get { return _active; }
            set {
                if (_active == value)
                    return;
                _active = value;
                this.OnPropertyChanged("Active");
            }
        }        

        /// <summary>
        /// Gets or sets the filter direction
        /// </summary>
        public FilterDirection FilterDirection { get; set; }

        /// <summary>
        /// Gets or sets the image index
        /// </summary>
        public Int32 ImageIndex
        {
            get
            {
                switch(this.FilterDirection)
                {
                    case FilterDirection.Exclude:
                        return 1;
                    case FilterDirection.Include:
                        return 0;
                }

                return -1;
            }
        }

        public FilterColumnType FilterColumn { get; set; }

        public string ColumnName
        {
            get
            {
                switch(this.FilterColumn)
                {
                    case FilterColumnType.EventID:
                        return "Ereignis-ID";
                    case FilterColumnType.Message:
                        return "Meldung";
                    case FilterColumnType.Source:
                        return "Quelle";
                    case FilterColumnType.Category:
                        return "Kategorie";
                }

                return string.Empty;
            }
        }

        public FilterCondition FilterCondition { get; set; }

        public string ConditionName
        {
            get
            {
                switch(this.FilterCondition)
                {
                    case Toolkit.FilterCondition.Contains:
                        return "enthält";
                    case Toolkit.FilterCondition.ContainsNot:
                        return "enthält nicht";
                    case Toolkit.FilterCondition.EndsWitch:
                        return "endet mit";
                    case Toolkit.FilterCondition.Equal:
                        return "gleich";
                    case Toolkit.FilterCondition.GreaterThan:
                        return "größer als";
                    case Toolkit.FilterCondition.LessThan:
                        return "kleiner als";
                    case Toolkit.FilterCondition.NotEqual:
                        return "ungleich";
                    case Toolkit.FilterCondition.StartsWith:
                        return "beginnt mit";
                }
                return string.Empty;
            }
        }

        public string ConditionValue { get; set; }

        public bool Match(ILogEntry<TEventType> entry)
        {
            bool matches = false;

            object sourceValue = this.GetSourceValue(entry);
            if (sourceValue is string)
            {
                // match string 
                string value = sourceValue.ToString();
                matches = this.MatchString(value);
            }
            else
            {
                // match number 
                Int32 value = (Int32)sourceValue;
                matches = this.MatchNumber(value);
            }

            return matches;
        }

        private bool MatchString(string value)
        {
            bool matches = false;

            switch(this.FilterCondition)
            {
                case FilterCondition.Contains:
                    matches = value.Contains(this.ConditionValue);
                    break;
                case FilterCondition.ContainsNot:
                    matches = !value.Contains(this.ConditionValue);
                    break;
                case FilterCondition.EndsWitch:
                    matches = value.EndsWith(this.ConditionValue);
                    break;
                case FilterCondition.Equal:
                    matches = value.Equals(this.ConditionValue);
                    break;
                case FilterCondition.GreaterThan:
                    matches = value.CompareTo(this.ConditionValue) > 0;
                    break;
                case FilterCondition.LessThan:
                    matches = value.CompareTo(this.ConditionValue) < 0;
                    break;
                case FilterCondition.NotEqual:
                    matches = !value.Equals(this.ConditionValue);
                    break;
                case FilterCondition.StartsWith:
                    matches = value.StartsWith(this.ConditionValue);
                    break;
            }

            return matches;
        }

        private bool MatchNumber(Int32 value)
        {
            bool matches = false;

            Int32 conditionValue = -1;
            if (Int32.TryParse(this.ConditionValue, out conditionValue))
            {
                switch (this.FilterCondition)
                {
                    case FilterCondition.Contains:
                        matches = value.ToString().Contains(this.ConditionValue);
                        break;
                    case FilterCondition.ContainsNot:
                        matches = !value.ToString().Contains(this.ConditionValue);
                        break;
                    case FilterCondition.Equal:
                        matches = value.Equals(conditionValue);
                        break;
                    case FilterCondition.GreaterThan:
                        matches = value > conditionValue;
                        break;
                    case FilterCondition.LessThan:
                        matches = value < conditionValue;
                        break;
                    case FilterCondition.NotEqual:
                        matches = value != conditionValue;
                        break;
                    case FilterCondition.StartsWith:
                        matches = value.ToString().StartsWith(this.ConditionValue);
                        break;
                    case FilterCondition.EndsWitch:
                        matches = value.ToString().EndsWith(this.ConditionValue);
                        break;
                }
            }

            return matches;
        }

        private object GetSourceValue(ILogEntry<TEventType> entry)
        {
            object sourceValue = string.Empty;
            switch (this.FilterColumn)
            {
                case FilterColumnType.EventID:
                    sourceValue = entry.EventID;
                    break;
                case FilterColumnType.Message:
                    sourceValue = entry.Message;
                    break;
                case FilterColumnType.Source:
                    sourceValue = entry.Source;
                    break;
                case FilterColumnType.Category:
                    sourceValue = entry.Category;
                    break;
            }

            return sourceValue;
        }

Die Filter sind in einer übergeordneten Klasse zusammengefasst, in der diese ausgewertet werden. Diese sieht wie folgt aus:


public class LogFilter<TEventType> : IDataFilter<ILogEntry<TEventType>>
    {
        public LogFilter()
        {
            this.Entries = new BindingList<FilterEntry<TEventType>>();
        }

        private bool _bSorting = false;
        public bool Sorting
        {
            get { return _bSorting; }
        }
       
        public BindingList<FilterEntry<TEventType>> Entries { get; set; }

        /// <summary>
        /// Method to append sorted entries
        /// </summary>
        /// <param name="filterEntries">the filter entries</param>
        private void AppendSortedFilterEntries(List<FilterEntry<TEventType>> filterEntries)
        {
            // append entries 
            foreach (FilterEntry<TEventType> entry in filterEntries)
            {
                this.Entries.Add(entry);
            }
        }

        /// <summary>
        /// Method to check if the event log entry matches a filter
        /// </summary>
        /// <param name="dataItem">the event log entry</param>
        /// <returns>true if the entry matches a filter and should be shown</returns>
        /// <remarks>
        /// Matching will be done in the following order:
        /// 1) Match Source filters
        /// 2) Match eventID filters
        /// 3) Match message filters
        /// </remarks>
        public bool Matches(ILogEntry<TEventType> dataItem)
        {
            // match source filters, ID Filters and message filters
            return this.MatchSourceFilters(dataItem) && this.MatchEventIDFilters(dataItem) && this.MatchMessageFilters(dataItem) && this.MatchCategoryFilters(dataItem);
        }

        /// <summary>
        /// Method to match filters
        /// </summary>
        /// <param name="dataItem">the event log entry</param>
        /// <param name="direction">the filter direction</param>
        /// <param name="column">the filter column</param>
        /// <returns>true if the event log entry matches a filter</returns>
        private bool MatchFilters(ILogEntry<TEventType> dataItem, FilterDirection direction, FilterColumnType column)
        {
            bool matches = false;
            IEnumerable<FilterEntry<TEventType>> entries = this.Entries.Where((x) => x.FilterColumn == column && x.FilterDirection == direction && x.Active);

            if (entries.Count() == 0)
                matches = direction == FilterDirection.Include ? true : false;

            foreach (FilterEntry<TEventType> entry in entries)
            {
               // oder verknüpfung
               matches = matches || entry.Match(dataItem);   
            }

            return matches;
        }

        /// <summary>
        /// Method to match event id filters
        /// </summary>
        /// <param name="dataItem">the event log entry</param>
        /// <returns>true if the entry matches a filter and should be shown</returns>
        private bool MatchEventIDFilters(ILogEntry<TEventType> dataItem)
        {
            // values to show must be in include filter but not in exclude filter
            return this.MatchFilters(dataItem, FilterDirection.Include, FilterColumnType.EventID) && !this.MatchFilters(dataItem, FilterDirection.Exclude, FilterColumnType.EventID);
        }

        /// <summary>
        /// Method to match event id filters
        /// </summary>
        /// <param name="dataItem">the event log entry</param>
        /// <returns>true if the entry matches a filter and should be shown</returns>
        private bool MatchCategoryFilters(ILogEntry<TEventType> dataItem)
        {
            // values to show must be in include filter but not in exclude filter
            return this.MatchFilters(dataItem, FilterDirection.Include, FilterColumnType.Category) && !this.MatchFilters(dataItem, FilterDirection.Exclude, FilterColumnType.Category);
        }

        /// <summary>
        /// Method to match event source filters
        /// </summary>
        /// <param name="dataItem">the event log entry</param>
        /// <returns>true if the entry matches a filter and should be shown</returns>
        private bool MatchSourceFilters(ILogEntry<TEventType> dataItem)
        {
            // values to show must be in include filter but not in exclude filter
            return this.MatchFilters(dataItem, FilterDirection.Include, FilterColumnType.Source) && !this.MatchFilters(dataItem, FilterDirection.Exclude, FilterColumnType.Source);
        }

        /// <summary>
        /// Method to match the message filters
        /// </summary>
        /// <param name="dataItem">the event log entry</param>
        /// <returns>true if the entry matches a filter and should be shown</returns>
        private bool MatchMessageFilters(ILogEntry<TEventType> dataItem)
        {
            // values to show must be in include filter but not in exclude filter
            return this.MatchFilters(dataItem, FilterDirection.Include, FilterColumnType.Message) && !this.MatchFilters(dataItem, FilterDirection.Exclude, FilterColumnType.Message);
        }

        /// <summary>
        /// Method to reset the filters
        /// </summary>
        public void Reset()
        {
            // clear all filter entries
            this.Entries.Clear();
        }
    }

Dem LogFilter wird anschließend noch eine Klasse übergestülpt die noch weitere Filter hält. Diese sieht wie folgt aus:


public class SQLLogFilter
    {
        public SQLLogFilter()
        {
            this.Filters = new Dictionary<string, IDataFilter<ILogEntry<SQLLogEntryType>>>();            
            this.Filters.Add("EventTimeFilter", new EventTimeFilter<SQLLogEntryType>(ApplicationSettings.SQLLogHistoryMonths));
            this.Filters.Add("EventFilter", new LogFilter<SQLLogEntryType>());
        }

        public Dictionary<string, IDataFilter<ILogEntry<SQLLogEntryType>>> Filters { get; set; }

        public IEnumerable<FilterColumn> FilterColumns
        {
            get
            {
                yield return this.CreateFilterColumnData("Quelle", FilterColumnType.Source);
                yield return this.CreateFilterColumnData("Meldung", FilterColumnType.Message);
            }
        }

        private FilterColumn CreateFilterColumnData(string displayName, FilterColumnType columnType)
        {
            FilterColumn column = new FilterColumn();
            column.DisplayName = displayName;
            column.Column = columnType;
            return column;
        }

        /// <summary>
        /// Method to match the details to the filters
        /// </summary>
        /// <param name="details">the details</param>
        /// <returns>true if the details matches</returns>
        public bool Matches(SQLLogEntry details)
        {
            bool matches = true;

            // match time
            matches = this.Filters["EventTimeFilter"].Matches(details) &&                      
                      this.Filters["EventFilter"].Matches(details);

            return matches;
        }

        public void Reset()
        {
            foreach (string filterName in this.Filters.Keys)
            {
                this.Filters[filterName].Reset();
            }
        }
    }

Mein Aufruf zur Filterung ist anschließend:


foreach (SQLLogEntry entry in this.Model.LogEntries)
{
   if (filter.Matches(entry))
   {
      this.FilteredList.Add(entry);
    }
}

Ich weiß, es ist ein bischen viel Code für das Forum. Aber um das ganze Ausmaß zu erkennen aus meiner Sicht notwendig.

Mein Problem ist nun, fange ich an nach einem Textwert zu filtern, dauert dies bei 30k Einträgen sehr lang (auch dann, wenn nur ein FilterEntry festgelegt wurde).

Hat jemand von euch eine Idee, wie ich die Filterung performanter durchführen kann, ohne großartig den aktuellen Aufbau zu verlieren?

04.09.2017 - 13:26 Uhr

Leider wurde ich da auch nicht fündig. Drum dachte ich, frage ich mal nach. Aber mir scheint auch als würde das nicht gehen.

Es ist wohl nicht vorgesehen beliebig viele Registerkarten zu haben und Multiline zu verwenden. 🤔

Da muss ich mir wohl was anderes überlegen. Problem ist ja, dass man dann irgendwann auf den Registerkarten nicht mehr alles sieht wenn zu viele Registerkarten vorhanden sind.

04.09.2017 - 10:17 Uhr

Hallo,

wir haben eine Anwendung, in der viele Registerkarten im TabControl angezeigt werden und zur Laufzeit weitere hinzugefügt werden können. Das TabControl haben wir auf MultiLine stehen, so dass die TabHeader nicht nur in einer Zeile stehen.

Meine Frage ist nun: Kann man die maximale Anzahl Zeilen irgendwie limitieren, so dass nach z.B. 3 Zeilen wieder ein Pfeil nach Rechts / Links erscheint um zu den nächsten TabPages zu wechseln?

01.09.2017 - 16:41 Uhr

Ich habe jetzt das MouseDown noch etwas angepasst. Damit ist es zumindest möglich mehrere Zeilen per Drag&Drop zu übernehmen.


 private void List_MouseDown(Object sender, MouseEventArgs e)
        {
            DataGridView gridView = sender as DataGridView;

            int rowIndexFromMouseDown = gridView.HitTest(e.X, e.Y).RowIndex;

            //if shift key is not pressed
            if (Control.ModifierKeys != Keys.Shift && Control.ModifierKeys != Keys.Control && rowIndexFromMouseDown >= 0)
            {
                //if row under the mouse is not selected
                if (!gridView.Rows[rowIndexFromMouseDown].Selected)
                    gridView.Rows[rowIndexFromMouseDown].Selected = true;               
            }

            this._dragSource = gridView;
        }

Mir fehlt hier irgendwie die Möglichkeit mit der Maus mehrere Zeilen zu selektieren (per MausMove von erster bis n-ter Zeile) und dann bei verlassen des Controls erst das Drag zu beginnen.

01.09.2017 - 16:22 Uhr

Hallo,

ich habe 2 DataGridView's zwischen denen per Drag&Drop die Daten ausgetauscht werden.
Für eine einzelne Zeile funktioniert das wunderbar. Selektion von mehreren Zeilen ist mit dem Ansatz leider nicht möglich.

Hat jemand einen Ansatz, wie ich das Problem lösen könnte?

Meine EventHandler sehen wie folgt aus:


 private void List_MouseDown(Object sender, MouseEventArgs e)
        {
            DataGridView gridView = sender as DataGridView;
            if (gridView != null && e.Button == MouseButtons.Left)
                this._dragSourceGrid = gridView;

        }

        private void List_MouseMove(Object sender, MouseEventArgs e)
        {
            DataGridView gridView = sender as DataGridView;

            if ((e.Button & MouseButtons.Left) == MouseButtons.Left && gridView != null)
            {
                // Proceed with the drag and drop, passing in the list item.                   
                DragDropEffects dropEffect = gridView.DoDragDrop(
                gridView.SelectedRows,
                DragDropEffects.Move);
            }
        }

        private void List_DragOver(Object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Move;
        }

        private void List_DragDrop(Object sender, DragEventArgs e)
        {
            DataGridView gridView = sender as DataGridView;
            if (gridView == null)
                return;

            if (e.Effect == DragDropEffects.Move)
            {
                if (gridView == this.lbAvailableObservers)
                {
                    this.MoveDataLeft();                
                }
                else if (gridView == this.lbSelectedObservers)
                {
                    // data should be from available observers
                    this.MoveDataRight();
                }
            }
        }

        private void List_MouseUp(Object sender, MouseEventArgs e)
        {
            if (this._dragSourceGrid != null)
                this._dragSourceGrid = null;
        }

31.08.2017 - 14:50 Uhr

Nach langem suchen, haben wir die Ursache gefunden. Tatsächlich war für eine Spalte der AutoSizeMode gesetzt. Nachdem dieser vor der Datenzuweisung entfernt wurde, funktioniert es wieder flüssig.

31.08.2017 - 10:31 Uhr

Ja, der Code ist eigentlich trivial:


private void ExpandMainNodes()
{
     this.ExpandNode(this._errorNode);
     this.ExpandNode(this._initNode);
     this.ExpandNode(this._okNode);
}

private void ExpandNode(TreeGridNode node)
{
     if(node.Visible && node.Nodes.Count > 0 && !node.IsExpanded)
          node.Expand();
}

Und zu der Frage: Wie im Eingangspost geschrieben ist das ausklappen, also quasi die Anzeige der Zeilen die Ursache. Habe jetzt zum Test einfach mal alle Einträge als "Root"-Knoten hinzugefügt und den SiteNode-Teil im TreeGridView so angepasst, dass nur ein "Rows.Add(node)" ausgeführt wird.

Das Verhalten bleibt das gleiche. - Das hinzufügen der Zeilen frisst massiv performance. Damit liegt die Ursache dann wohl nicht direkt bei dem TreeGridView sondern mehr bei dem DataGridView selbst, im speziellen beim Hinzufügen der Zeilen.

31.08.2017 - 08:06 Uhr

Hallo,

danke, das SuspendDrawing etc. hatte ich aber auch schon gefunden: Leider bringt das auch nicht den gewünschten Effekt.

30.08.2017 - 15:16 Uhr

Hallo,

in einem Programm verwenden wir das TreeGridView von Mark Rideout. Zu finden hier: Customizing the DataGridView to support expanding/collapsing (ala TreeGridView)

Wir visualisieren mit dem Control sich ständig ändernde Status. Dabei können Einzelne Knoten den Parent wechseln.

Um die Aktualisierung zu beschleunigen, habe ich ein Hashtable verwendet in dem ich mir Informationen zum Datensatz und den zugehörigen TreeGridNode merke. - Auf diese Weise muss ich zur Aktualisierung nicht den ganzen Baum durchsuchen. Das brachte bei der Aktualisierung der Daten schon einen großen Performance Schub.

Problem ist nun aber immer noch die Initialisierung der Daten und Erzeugung der Knoten. Je mehr Zeilen ins Grid wandern, desto höher wird die Dauer bis die Anwendung verwendet werden kann (in meinem Konkreten Fall reden wir von 500 Einträgen, durch die die Anwendung ca. 2 Minuten lang nicht reagiert).

Folgender Code wird zur Generierung / Aktualisierung der Daten verwendet (unnötiges Drumherum habe ich weggelassen):


public override void SetData(List<DataItem> infos)
{
    foreach(DataItem info in infos)
    {
        this.SetData(info);
    }

    // main nodes ausklappen

    base.SetData(infos);
}

public void SetData(DataItem info)
{
    // Übergeordneten Knoten anhand des Status holen
    // Es gibt die Status - unbekannt, OK und Fehler
    // damit nur 3 mögliche parent knoten
    TreeGridNode initNode = this:GetMainNodeByState(info.State);

     TreeGridNode currentNode = null;
     if(this._knownData.Containskey(info.GUID))
         currentNode = (DataItem)this._knownData[info.GUID];

      if(currentNode == null)
      {
         currentNode = initNode.Nodes.Add(info.Name);
         this._knownData.Add(info.GUID, currentNode);  
      }

      // füllen der Daten ... weggelassen
      
      // neuen Parent zuweisen, wenn der Status sich geändert hat
      if(currentNode.Parent != initNode)
      {
           currentNode.Parent.Remove(currentNode);
           initNode.Nodes.Add(mainnode);
      }

      // jedes DataItem hat untergeordnete Items, die werden auf die gleiche weise
      // dem currentNode zugeordnet ... weggelassen, da unnötig für das Beispiel
}

Hat jemand eine Idee, wie ich das Anwendungsverhalten optimieren kann, so dass der Start (also das Erzeugen der 500 Knoten) nicht mehr 2 Minuten in Angriff nimmt bzw, hat jemand überhaupt schon Erfahrung mit dem Control?

Ich konnte Ermitteln, dass das Ausklappen der Knoten die Ursache ist.

Zusätzliche Anmerkung:
Dass das Control nicht mehr supported wird ist klar. Es wird nur schon länger in der Anwendung verwendet als ich im Unternehmen tätig bin. - Bisher gab es aber bei unseren Kunden nie den Fall, dass so viele Daten angezeigt wurden. Es handelte sich zuvor immer um so zwischen 10 und 20 Einträgen + 1-3 Child-Einträgen. 500 Einträge sind dann wohl doch eine ganz andere Hausnummer.

EDIT: Ursache korrigiert.

14.07.2017 - 13:20 Uhr

Somit ist dein Programm zwar mit einreihen fertig aber die eigentlichen Threads arbeiten dann im Hintergrund noch.

T-Virus

Das habe ich jetzt soweit verstanden. - Allerdings erkenne ich ja auf Basis eines Events ob die Abarbeitung fertig ist. Dieses wird in der Methode "DownGradeFinished" ausgeführt. Wie ich oben ergänzt habe, habe ich ja nun auch erkannt das aus irgend einem Grund das Event ausgelöst wird bevor überhaupt die Schleife betreten wurde. Die Stelle habe ich nun auch gefnden (warum auch immer ich geistig umnachtet die Zeile geschrieben habe).

An der Stelle wo ich ExecuteDownGrade aufrufe, wird das Event auch ausgelöst. - Also habe ich wohl den Pool doch korrekt verwendet wie erwartet und einfach nur das Event einmal zu viel ausgelöst.

Insofern kann nun das ganze Thema als gelöst und Entwicklerfehler gewertet werden.

EDIT:

Liegt wie gesagt daran, dass du nicht auf die Abarbeitung wartest.
Leg dir ein Delegate an, starte es mit Begin und merke dir alle aktiven Delegates.
Am ende musst du dann nur noch deine Liste durchlaufen und die End Methode aufrufen.
Dann wird auf die Abarbeitung der Threads gewartet.
Dann ist dein Programm erst fertig, wenn alles Delegates erledigt sind.

Nö, wie gerade ergänzt lag es an einer anderen Stelle. - Die Methode "ExecuteDownGrade" wird übrigens nie parallel mehrfach aufgerufen. - Hintergrund ist einfach das die Anwendung sich nicht aufhängen soll. Mehrere Starts sind nicht vorgesehen und die entsprechende Schaltfläche auch inaktiv bis das Event ausgelöst wird.

14.07.2017 - 12:55 Uhr

Ja, aber mein Event wird doch erst ausgeführt wenn der Code fertig ausgeführt wurde. Wenn dem nicht so ist, muss ich ja die letzten 7 Jahre nur quatsch produziert haben.

Das der Code so noch nicht sauber ist, ist mir klar. - Aber nichts desto trotz, sollte das Ereignis eben doch erst ausgeführt werden wenn der Code und die Schleife fertig ausgeführt wurden. Steht immerhin am Ende.

PS: Auf Threads / die Threadqueue bin ich beschränkt, da es sich um eine .NET 2.0 Anwendung handelt, deren Migration auf ein höheres Framework nicht vorgesehen ist.

EDIT: Bzgl. deiner Aussage der Code wäre so nicht gewollt.

Mein Ziel war es, auf Basis der Einstellungen die in der "DownGradeInfo" stehen alle Datensätze zu ermitteln die es betrifft. Das funktioniert und passiert wie gewollt am Anfag der Ausführung. Habe ich ein DataTable mit Daten erhalten, dann möchte ich dieses Zeilenweise durchgehen und einen neuen Datensatz mit der neuen Bewertung schreiben. Auch das passiert wie von mir gewollt.

Einziges wo ich jetzt prinzipiell gerade ein Problem erkannt habe ist, dass ich bei Fehlschlagen der Datenermittlung die Abarbeitung beenden sollte und nicht wie aktuell am Ende noch einmal das Event auszulösen.

Das erklärt mir alles aber immer noch nicht, warum nach Auslösen des Events - was ja ganz klar am Ende der Abarbeitungskette passiert - die Tabelle in der Datenbank noch gefüllt wird.

EDIT2:
Ok, ich sehe dass ich vermutlich den Threadpool tatsächlich bisher nicht korrekt verwendet habe. Mit entsprechenden Breakpoints habe ich nun nämlich gesehen, dass das Event ausgelöst wird - und erst danach das erste mal in die Methode "DownGradeArticle" gesprungen wird. Das Erklärt damit ja schon erst einmal das absurde verhalten.

14.07.2017 - 11:38 Uhr

verwendetes Datenbanksystem: MSSQL Server (2012)

Hallo,

ein bestehendes Programm wurde durch mich um eine Funktion erweitert, die eine große Menge an Artikeln im Datenbestand umbewerten soll. Ich ermittle die Datensätze aus der Datenbank und schreibe diese anschließend in einer Schleife mit der neuen Bewertung zurück.

Der Ablauf findet in einem Extra-Thread statt, nach dessen Fertigstellung ich eine Meldung ausgebe. Nach Fertigstellung sollten aus meiner Sicht alle Daten bereits in der entsprechenden Datenbanktabelle stehen. - Die Speicherung erfolgt über eine gespeicherte Prozedur der jeder Datensatz einzeln übergeben wird.

Beobachtet habe ich nun das wenn mein Programm bereits fertig ist mit dem Schreiben der Daten, die Tabelle noch nicht alle Datensätze enthält und sich Schrittweise füllt. Ich frage mich nun warum das so ist. Bei großen Datenmengen kann das im Produktiv Einsatz nämlich schon ein paar Probleme hervor rufen.

Mein Code für die Umbewertung sieht wie folgt aus:


private void ExecuteDownGrade(int downGradeID)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(Object state)
    {
        this._downGradeState.UpdateState("Ermittlung der abzuwertenden Artikel...");
        DataTable dtDownGradeArticles = null;

        try
        {
            dtDownGradeArticles = this.TypedController.GetDownGradeArticles(this._downGradeInfo);
        }
        catch
        {
            this.DownGradeFinished(false, true);
        }

        List<string> failedDownGrades = new List<string>();

        if (dtDownGradeArticles != null)
        {
            int currentArticle = 0;
            foreach (DataRow row in dtDownGradeArticles.Rows)
            {
                if (!this._downGradeState.IsCanceled)
                {
                    currentArticle ++;
                    this._downGradeState.UpdateState("Abwertung Artikel{0} von {1}", currentArticle, dtDownGradeArticles.Rows.Count);

                    try
                    {
                        this.TypedController.DownGradeArticle(row, this._downGradeInfo, downGradeID);
                    }
                    catch
                    {
                        // add failed downgrade to list
                        failedDownGrades.Add(row["ArticleID"].ToString());
                    }
                }
            }
        }

        this.DownGradeFinished(false, failedDownGrades.Count > 0);
    }));
}

Die Methode DownGradeArticle macht nichts anderes, als die Daten für den Artikel anzupassen und über die gespeicherte Prozedur an die Datenbank zu übergeben. Dies geschieht direkt ohne einen weiteren Thread. Die Prozedur Commited die Eintragungen auch vor Beendigung dieser.

Insofern erschließt sich mir nicht, wieso der Thread fertig ist aber noch nicht alle Daten in der Tabelle stehen sondern diese sich nach und nach füllt.

Woran liegt das und gibt es eine Möglichkeit das Verhalten anzupassen, dass der Prozeduraufruf auch tatsächlich erst zurück kommt, wenn der Datensatz gespeichert ist? Das ganze dann bitte aber auch ohne Warteschleifen in die Prozedur zu bauen.

Als Randinformation noch:
Die Prozedur ruft intern weitere gespeicherte Prozeduren auf und verteilt bestimmte Daten damit auf unterschiedliche Tabellen. Aber alle Aktionen innerhalb der Prozeduren laufen im gleichen Thread (weiß auch garnicht ob Multithreaded in gespeicherten Prozeduren überhaupt möglich wäre).

23.06.2017 - 10:25 Uhr

Hast du denn schon mal mit dem Debugger geprüft, ob der Code überhaupt angesprochen wird?

22.06.2017 - 08:59 Uhr

Bei deinem Link ist doch auch ein Beispiel dabei, wie du das nur für bestimmte Controller ausführst.

21.06.2017 - 15:17 Uhr

Lege dein Config-Objekt als Member von "Program" an. Beachte aber, dass es statisch sein muss, sonst kannst du nicht darauf zugreifen.

In deiner Start-Methode übergibst du dann das Objekt an dein ApiFindingCall. - Danach hast du auch innerhalb dessen Zugriff auf die Konfigurationswerte.

16.06.2017 - 08:10 Uhr

Das ist immer abhängig von dem jeweiligen Framework. Für .NET 2.0 kompilierte Anwendungen Beispielsweise erfordern auf WIndows 10 eine Nachinstallation des Frameworks, da 4.5 vorinstalliert ist und dieses nicht kompatibel mit 2.0 ist.

08.06.2017 - 11:33 Uhr

Hallo,

ist jetzt nicht das Optimum was man rausholen kann, aber unter DatenbankClient ADO.NET - Providerunabhängig ist meine Datenbankclient Klasse zu finden.

08.06.2017 - 11:24 Uhr

Beschreibung:

Da immer mal wieder Fragen auftauchen bzgl. einem DataLayer der für die verschiedensten Datenbanksysteme funktioniert poste ich hier mal den Quelltext meines DbClients.

Der zu verwendende Datenbank-Provider wird entweder über den Namen des ConnectionStrings aus der Anwendungskonfiguration oder durch Übergabe an den Konstruktor definiert.

Ein einfaches Verwendungsbeispiel wäre:


DbClient client = new DbClient("dbconnection");
int result = (Int32)client.ExecuteScalar(CommandType.Text, "SELECT COUNT(*) FROM myTable", null);

Console.WriteLine("Item Count {0}", result);

Zur Datenermittlung stehen die Funktionalitäten "ExecuteScalar", "ExecuteNonQuery", "ExecuteDataReader" aus der IDbConnection zur Verfügung und zusätzlich eine Methode "ExecuteDataSet" welche die Ergebnismenge mit Hilfe des DataAdapters als DataSet zurück liefert.

Sind Parameter an die Datenbankabfrage / Prozedur zu übergeben, kann mit Hilfe der Methode "CreateParameter" ein Parameter erzeugt werden.


IDataParameter parameter = client.CreateParameter("@MyParam", DbType.String, 255, ParameterDirection.Input, "Dies ist ein Test Parameter");

Der Einfachheithalber kann man die Parameter in einer Liste oder direkt in einem Array von "IDataParameter"-Objekten sammeln und anschließend übergeben.

**Hinweis: **Die Klasse hat derzeit keine Transaktionsunterstützung. Sollte die benötigt werden, können die ja relativ einfach hinzugefügt werden. Nur die Open- und Close Methode sollten dann angepasst werden.

Hier nun die Klasse.


    /// <summary>
    /// class for db access
    /// </summary>
    public class DbClient : IDisposable
    {
        #region Fields

        IDbConnection _connection;
        DbProviderFactory _factory;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the connection string
        /// </summary>
        public string ConnectionString
        {
            get
            {
                return this._connection.ConnectionString;
            }
        }

        /// <summary>
        /// Gets or sets the command timeout
        /// </summary>
        public int CommandTimeout { get; set; }

        #endregion

        #region Construction / Destruction

        /// <summary>
        /// 
        /// </summary>
        /// <param name="connectionStringName">the name of the connection string defined in application configuration</param>
        public DbClient(string connectionStringName)
        {
            ConnectionStringSettings connectionString = null;

            if (string.IsNullOrEmpty(connectionStringName))
                throw new ArgumentException("There must be given a connection string name!");

            if (ConfigurationManager.ConnectionStrings[connectionStringName] != null)
                connectionString = ConfigurationManager.ConnectionStrings[connectionStringName];
            else
                throw new InvalidOperationException(String.Format("The connection string settings for {0} could not be found!", connectionStringName));

            this.InitializeConnection(connectionString.ProviderName, connectionString.ConnectionString);
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dbProvider">the database provider name</param>
        /// <param name="connectionString">the connection string</param>
        public DbClient(string dbProvider, string connectionString)
        {
            this.InitializeConnection(dbProvider, connectionString);
        }
        
        #endregion

        #region Methods

        /// <summary>
        /// Method to initialize the db connection
        /// </summary>
        /// <param name="providerName">the db provider name</param>
        /// <param name="connectionString">the connection string</param>
        /// <param name="connectTimeout">the connect timeout</param>
        private void InitializeConnection(string providerName, string connectionString)
        {
            this._factory = DbProviderFactories.GetFactory(providerName);

            if (this._factory == null)
                throw new InvalidOperationException(String.Format("The factory for data provider {0} could not be found!", providerName));

            this._connection = this._factory.CreateConnection();
            this._connection.ConnectionString = connectionString;
        }

        /// <summary>
        /// Method to open the connection
        /// </summary>
        private void OpenConnection()
        {
            if (this._connection != null && this._connection.State != ConnectionState.Open)
            {
                this._connection.Open();
            }
        }

        /// <summary>
        /// Method to close the connection
        /// </summary>
        private void CloseConnection()
        {
            if (this._connection != null && this._connection.State != ConnectionState.Closed)
            {
                this._connection.Close();
            }
        }

        /// <summary>
        /// Method to dispose the connection
        /// </summary>
        public void Dispose()
        {
            if (this._connection != null)
            {
                this.CloseConnection();
                this._connection.Dispose();
            }
        }

        /// <summary>
        /// Method to call a stored procedure and retrieve the result
        /// </summary>
        /// <param name="procedureName">name of the stored procedure</param>
        /// <param name="parameters">parameters for calling the stored procedure</param>
        /// <returns>the dataset with the execution results</returns>
        public DataSet ExecuteDataSet(string procedureName, IDataParameter[] parameters)
        {
            return this.ExecuteDataSet(CommandType.StoredProcedure, procedureName, parameters);
        }

        /// <summary>
        /// Method to call a stored procedure and retrieve the result
        /// </summary>
        /// <param name="commandType">the command type</param>
        /// <param name="procedureName">the command to execute</param>
        /// <param name="parameters">parameters for calling the stored procedure</param>
        /// <returns>the dataset with the execution results</returns>
        public DataSet ExecuteDataSet(CommandType commandType, string commandText, IDataParameter[] parameters)
        {
            return this.ExecuteDataSet(commandType, commandText, parameters, 60000);
        }

        /// <summary>
        /// Method to call a stored procedure and retrieve the result
        /// </summary>
        /// <param name="commandType">the command type</param>
        /// <param name="procedureName">the command to execute</param>
        /// <param name="parameters">parameters for calling the stored procedure</param>
        /// <returns>the dataset with the execution results</returns>
        public DataSet ExecuteDataSet(CommandType commandType, string commandText, IDataParameter[] parameters, int commandTimeout)
        {
            DataSet dsResult = new DataSet();
            // open the connection
            this.OpenConnection();

            DbCommand command = (DbCommand)this.CreateCommand(commandType, commandText, parameters, commandTimeout);

            DbDataAdapter adapter = this._factory.CreateDataAdapter();
            adapter.SelectCommand = command;
            adapter.Fill(dsResult);

            // close the connection
            this.CloseConnection();
            return dsResult;
        }

        /// <summary>
        /// Method to execute a datareader
        /// </summary>
        /// <param name="procedureName">the procedurename</param>
        /// <param name="parameters">the parameters</param>
        /// <returns>the data reader</returns>
        public IDataReader ExecuteDataReader(string procedureName, IDataParameter[] parameters)
        {
            return this.ExecuteDataReader(CommandType.StoredProcedure, procedureName, parameters);
        }

        /// <summary>
        /// Method to execute a datareader
        /// </summary>
        /// <param name="commandType">the command type</param>
        /// <param name="procedureName">the procedurename</param>
        /// <param name="parameters">the parameters</param>
        /// <returns>the data reader</returns>
        public IDataReader ExecuteDataReader(CommandType commandType, string commandText, IDataParameter[] parameters)
        {
            return this.ExecuteDataReader(commandType, commandText, parameters, 60000);
        }

        /// <summary>
        /// Method to execute a datareader
        /// </summary>
        /// <param name="commandType">the command type</param>
        /// <param name="procedureName">the procedurename</param>
        /// <param name="parameters">the parameters</param>
        /// <param name="commandTimeout">the command timeout</param>
        /// <returns>the data reader</returns>
        public IDataReader ExecuteDataReader(CommandType commandType, string commandText, IDataParameter[] parameters, int commandTimeout)
        {
            this.OpenConnection();

            IDbCommand command = this.CreateCommand(commandType, commandText, parameters, commandTimeout);

            this.CloseConnection();

            return command.ExecuteReader();
        }

        public object ExecuteScalar(string procedureName, IDataParameter[] parameters)
        {
            return this.ExecuteScalar(CommandType.StoredProcedure, procedureName, parameters);
        }

        public object ExecuteScalar(CommandType commandType, string commandText, IDataParameter[] parameters)
        {
            return this.ExecuteScalar(commandType, commandText, parameters, 60000);
        }

        public object ExecuteScalar(CommandType commandType, string commandText, IDataParameter[] parameters, int commandTimeout)
        {
            this.OpenConnection();

            IDbCommand command = this.CreateCommand(commandType, commandText, parameters, commandTimeout);
            Object result = command.ExecuteScalar();

            this.CloseConnection();

            return result;
        }

        public int ExecuteNonQuery(string procedureName, IDataParameter[] parameters)
        {
            return this.ExecuteNonQuery(CommandType.StoredProcedure, procedureName, parameters);
        }

        public int ExecuteNonQuery(CommandType commandType, string commandText, IDataParameter[] parameters)
        {
            return this.ExecuteNonQuery(commandType, commandText, parameters, 60000);
        }

        public int ExecuteNonQuery(CommandType commandType, string commandText, IDataParameter[] parameters, int commandTimeout)
        {
            int result = 0;
            this.OpenConnection();

            IDbCommand command = this.CreateCommand(commandType, commandText, parameters, commandTimeout);
            result = command.ExecuteNonQuery();

            this.CloseConnection();

            return result;
        }

        /// <summary>
        /// Method to create a command
        /// </summary>
        /// <param name="commandType">the command type</param>
        /// <param name="commandText">the command text</param>
        /// <param name="parameters">the parameters</param>
        /// <param name="commandTimeout">the command timeout</param>
        /// <returns>the command</returns>
        private IDbCommand CreateCommand(CommandType commandType, string commandText, IDataParameter[] parameters, int commandTimeout)
        {
            IDbCommand command = this._connection.CreateCommand();
            command.CommandTimeout = commandTimeout;
            command.CommandText = commandText;
            command.CommandType = commandType;

            if(parameters != null)
                foreach (IDataParameter parameter in parameters)
                    command.Parameters.Add(parameter);

            return command;
        }

        /// <summary>
        /// Method to create a parameter
        /// </summary>
        /// <param name="parameterName">the parameter name</param>
        /// <param name="dbType">the database type</param>
        /// <param name="size">size of the parameter</param>
        /// <param name="value">the value of the parameter</param>
        /// <returns>the new parameter</returns>
        public IDataParameter CreateParameter(string parameterName, DbType dbType, int size, object value)
        {
            return this.CreateParameter(parameterName, dbType, size, ParameterDirection.Input, value);
        }

        /// <summary>
        /// Method to create a parameter
        /// </summary>
        /// <param name="parameterName">the parameter name</param>
        /// <param name="dbType">the database type</param>
        /// <param name="size">size of the parameter</param>
        /// <param name="parameterDirection">the parameter direction</param>
        /// <returns></returns>
        public IDataParameter CreateParameter(string parameterName, DbType dbType, int size, ParameterDirection parameterDirection)
        {
            return this.CreateParameter(parameterName, dbType, size, parameterDirection, null);
        }

        /// <summary>
        /// Method to create a parameter
        /// </summary>
        /// <param name="parameterName">the parameter name</param>
        /// <param name="dbType">the database type</param>
        /// <param name="parameterDirection">the parameter direction</param>
        /// <param name="size">size of the parameter</param>
        /// <param name="value">the value of the parameter</param>
        /// <returns>the new parameter</returns>
        public IDataParameter CreateParameter(string parameterName, DbType dbType, int size, ParameterDirection parameterDirection, object value)
        {
            DbParameter parameter = this._factory.CreateParameter();
            parameter.ParameterName = parameterName;
            parameter.Direction = parameterDirection;
            parameter.DbType = dbType;
            parameter.Value = value;
            parameter.Size = size;
            
            return parameter;
        }

        #endregion
    }

Hinweis: Die Klasse an sich ist nicht Threadsicher. In meinen Privaten projekten löse ich das so, dass ich den Client noch einmal mit einer übergeordneten Klasse kapsle die dann nur die notwendigen Methoden bereitstellt.

Ein Beispiel für die Kapselnde Klasse ist:


public class Database
{
      #region Fields   

      private Int32 _commandTimeout = 10;
      private String _connectionStringName;

      #endregion

      #region Construction / Destruction

      public Database(string connectionStringName, Int32 commandTimeout)
      {
            this._connectionStringName = connectionStringName;
            this._commandTimeout = commandTimeout;
      }

      public DataSet GetData(string procedureName, List<IDataParameter> parameters)
      {
            using(DbClient dbClient = new DbClient(connectionStringName))
            {
                dbClient.CommandTimeout = commandTimeout;

                return dbClient.ExecuteDataSet(procedureName, parameter.ToArray());
            }
      }

      #endregion
}

Damit bin ich auf der sicheren Seite und es wird immer eine neue Instanz der DbClient-Klasse erstellt. In Anwendungsszenarien, wo Crossthreaded-Zugriffe ausgeschlossen sind, lasse ich diese zusätzliche Klasse jedoch weg.

Schlagwörter: Datenbankabfrage, unterschiedliche Datenbanken unterstützen , Datenbankzugriffe

07.06.2017 - 14:28 Uhr

Hallo,

wenn deine Zeilen tatsächlich wie oben beschrieben aussehen, wäre es eventuell klüger auf die ganze Zeile zu matchen. - So bekommst du später auch eine einfachere Auswertung hin.

Folgender Ausdruck prüft die ganze Zeile:

^[0-9]{6}\s([\w|\s|.]+)$

Im Grunde prüft der Ausdruck auch nur, ob die Zeile mit 6 Ziffern anfängt, gefolgt von einem Leerzeichen und einem beliebigen Text der eingeklammert ist.

Noch etwas vereinfacht reicht hier denke ich auch schon folgender Regex:
^[0-9]{6}\s(.+?)$

Solltest du mit den Daten weiter arbeiten müssen bietet es sich auch an, Variablen zu verwenden. Folgendes Beispiel zeigt wie, die Informationen kannst du aus den Matches rausziehen.

^(?<number>[0-9]{6})\s((?<kunde>.+?))$

30.05.2017 - 08:33 Uhr

Hallo Fitzel69,

deine Herangehensweise ist nicht korrekt. Wie bereits gesagt wurde handelt es sich beim Button.Click um ein Event. Einen Vergleich darauf kannst du nicht durchführen.

Korrekterweise müsstest du einen EventHandler implementieren (macht Visual Studio bei Doppel-Klick auf den Button automatisch) und darin deine Berechnung ausprogrammieren oder in eine extra Methode die du aus dem EventHandler aufrufst.

29.05.2017 - 15:17 Uhr

Um mal zum Einstiegsthema zurück zu kommen. Wäre es nicht tatsächlich besser, direkt die DbProviderFactories-Klasse zu verwenden? Das sollte eigentlich mit allen korrekt implementierten Datenbankprovidern funktionieren.

EDIT:
Das setzt natürlich eine Anpassung am Aufbau des Codes des TE voraus. - Der Provider sollte dabei übergeben und ausgewertet werden. - Anhand dessen liefert die Klasse ja die korrekte Connection etc.

23.05.2017 - 09:00 Uhr

Du müsstest sowohl bei der Liste als auch bei den einzelnen Items den vollen Namespace angeben.

27.04.2017 - 11:29 Uhr

Hallo,

da man im Internet nicht wirklich fündig wird stelle ich meine Frage hier ein (auch wenn die schon Tausendfach an anderen Stellen gestellt, für mich aber unbefriedigend beantwortet wurde).

Gibt es Tools, die auf Basis von SCPD-Service Definitionen Soap-Clients generieren können? Ich möchte vermeiden, jede SOAP-Nachricht manuell zusammen zu bauen oder wie der Entwickler von FritzTR064 manuell die "Webservice-Proxies" zu erstellen.

Falls jetzt irgendwelche Fragen bzgl. der Fritz!Box aufkommen, nein das Tool suche ich nicht dafür. Ich habe weitere Devices die SOAP per UPNP bereitstellen.

OhNET habe ich mir angeschaut, ist für mich aber auch nicht befriedigend. - Ich hoffe ich muss keinen Client-Generator selbst entwickeln.

28.03.2017 - 10:55 Uhr

Hab ne einfache for Schleife wobei die int Variable 0 ist und die Count 1 aber er springt mir einfach nicht in die Schleife rein.

Hallo, deine Bedingung ist verkehrt herum, dein Count ist mit 1 nie kleiner als x mit 0.


for(int x = 0; x < count; x++)
{
   // 
}

28.03.2017 - 09:40 Uhr

ich habe versucht mich an die Übung im Buch zu halten. Und da wird die Konsole
als Ausgabe Medium angesteuert

Dann wird die Anwendung sicher eine reine Konsolenanwendung sein. - Mein Vorschlag: Erstelle ein neues Projekt als Konsolenanwendung und übernimm deine Funktionalität in diese.

24.03.2017 - 15:04 Uhr

Warum willst du Infos aus einer Forms-Anwendung in der Konsole ausgeben? Setzen wir mal an der Stelle an.

Wozu benötigst du denn die Form, wenn die Ausgaben sowieso in der Konsole erfolgen sollen? Dann erstelle doch gleich eine Konsolenanwendung.

24.03.2017 - 14:24 Uhr

Nö, hast du nicht. Einziges was du getan hast, ist dass du jetzt deine "DataRow" als Item anhängst.

Was spricht für dich denn gegen den folgenden Ansatz?


LB_Dataset.DataSource = ds.Tables["BSP"];
LB_Dataset.DisplayMember = "CurrencyName";

Wenn du mehrere Daten ausgeben willst, nimm das DataGridView oder passe dein SQL-Statement entsprechend an. Beim DataGridView reicht im ersten Moment dann auch einfach folgendes:


DataGridView.DataSource = ds.Tables["BSP"];

23.03.2017 - 13:11 Uhr

Würde das dann nicht in einer zu aufgeteilten Struktur resultieren? Oder ist es irgendwie möglich die Klasse Auto in Hauptprojekt zu lassen und sie dennoch als Parameter der Methode CreateAuto zu verwenden?

Wenn du in einem Unterprojekt eine Methode "CreateAuto" hast, dann gehört Auto entweder in das Unterprojekt oder aber wie angesprochen in ein weiteres Projekt auf das Hauptprojekt und Unterprojekt verweisen.

Normalerweise ist es ja so, das du eine Hirarchie hast die von oben nach unten geht. - Das heißt, Klassen die du in Unterprojekten benötigst, dürfen und können garnicht im Hauptprogramm existieren.

23.03.2017 - 11:26 Uhr

Fällt mir ja direkt eine ganz einfache Lösung ein. - Erst nach X-Sortieren und dann bei Punkten mit gleicher X-Koordinate nach Y-Sortieren. - Ach halt warte, hier bleibt ja das Problem, dass man nicht weiß in welche Richtung.

07.03.2017 - 10:16 Uhr

Ich versuche es mit meinem Wissensstand zu lösen, der leider sehr begrenzt ist 😄.

Und mein Beispiel ist jetzt nicht auf deinem Wissensstand? Sind doch nur 2 Schleifen. Eine die den "Zähler" verwaltet und eine die durch das Array durchgeht und die Boolschen Variablen inversed.

Das entsprechende Einfärben des Labels da noch zu integrieren sollte das geringste Problem sein.

06.03.2017 - 18:32 Uhr

Hallo, für das Umkehren würde ich wie folgt durchgehen:


// start with one so all items will be true
int step = 1;
while(step <= boolArray.Length)
{
   // iterate by current step width and reverse items
   for(int i = step; i < boolArray.Length; i+=step)
   {
        boolArray[i] = !boolArray[i];
        myLabels[i].BackColor = boolArray[i] ? Color.Green : Color.Red;
   }

   // raise step and inverse all items matching
   step++;
}

Ist jetzt ungetestet aber so in der Richtung würde ich das durchgehen.