Laden...

Forenbeiträge von MaXeM Ingesamt 198 Beiträge

04.05.2010 - 11:14 Uhr

Hallo,

ich wollte mich etwas mit automatisiertem Scheduling und der gleichen beschäftigen. Da mir momentan nichts sinnvolleres eingefallen ist, ist mein Projekt folgendes:

Ein "intelligenter" Raidplaner für OGame.
Bei OGame kann man andere Spieler(Planeten) Raiden (angreifen um Resourcen zu klauen), Planeten können sowohl mit Schiffen als auch fixer Verteidigung verteidigt werden. Mit sog. Spionageberichten erfährt man was genau auf diesem Planeten vorhanden ist.

Der Ablauf soll folgendermaßen sein: Man spioniert beliebig viele Planeten aus, fügt die Spionageberichte in das Programm ein und gibt dazu noch seine verfügbare Flotte an. Das Programm soll dann versuchen den optimalen Raidplan zu erstellen.

Folgende Begrenzungen existieren: *Maximal X Angriffe können in einem Plan enthalten sein. Die Zahl ist bekannt und wird vor dem Start angegeben. *Es steht eine begrenzte Flotte (verschiedene Schiffsarten, verschiedene Anzahl / Schiffsart) zur verfügung, ebenfalls bekannt. *Schiffe die in einem Plan bereits eingesetzt werden, können im selben Plan nicht erneut eingesetzt werden. *Planeten bei denen ein Angriff nicht über einem bestimmten Gewinn liegt bzw. in einem Verlust resultiert sollen ignoriert werden. *Die Simulation eines Angriffes auf einen Planeten dauert zwischen 10ms und 500ms, im Schnitt ca. 200ms. *Es stehen maximal 25 Minuten pro Planerstellung zur verfügung.

Mein Ansatz wären hier jetzt zwei genetische Algorithmen:
Einmal einen der versucht die optimale Fleetzusammenstellung pro Planet herauszufinden (aus den zu dem Zeitpunkt noch verfügbaren Schiffen) und dann noch einmal einen der den gesamten Plan optimiert.

Gibt es für so etwas einfachere / schnellere / bessere Alternativen? Es geht mir nicht um den optimalen Raidplan sondern nur etwas annähernd optimales.

08.03.2010 - 03:08 Uhr

Hallo,

in letzter Zeit habe ich recht viel mit WPF und dem MVVM-Pattern zu tun, was mich daran stört ist die Menge an Boilerplatecode wie z. B. die INotifyPropertyChanged Geschichte.

Um das zu minimieren habe ich ein bisschen mit Mono.Cecil rumgespielt und mir eine kleine Konsolenanwendung geschrieben. Diese Anwendung schreibt die WPF Assembly so um, dass jeder public Setter INotifyPropertyChanged benutzt und für jede public void Methode ein ICommand Field und Property angelegt wird, das Field wird dann im Konstruktor initialisiert. (Die ICommand Implementierung ist eine Art RelayCommand / DelegateCommand).

Also kurzes Code Beispiel:


// Was man vorher selbst schreiben musste
public class AViewModel: AbstractViewModel
{
    private string _name;
    private readonly ICommand _doSomethingCommand;

    public AViewModel()
    {
        _doSomethingCommand = new Command(DoSomething, CanDoSomething);
    }

    public ICommand DoSomethingCommand
    {
        get { return _doSomethingCommand; }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
            {
                return;
            }

            _name = value;

            FirePropertyChanged("Name");
        }
    }

    public void DoSomething()
    {
        
    }

    public bool CanDoSomething()
    {
        return true;
    }
}


// Was man jetzt schreiben muss

public class AViewModel: AbstractViewModel
{
    public string Name { get; set; }

    public void DoSomething()
    {
        
    }

    public bool CanDoSomething()
    {
        return true;
    }
}

Das funktioniert auch alles so weit. Das einzige Problem ist: ich kann keine Breakpoints mehr setzen. Die PDB und Dll/Exe sind scheinbar nicht mehr synchron bzw. in der umgeschriebenden PDB fehlt etwas. Die Pdb der Beispielanwendung hat vorher 18kb und nachher nur noch 12.

Muss man mehr machen als:


var assembly = AssemblyFactory.GetAssembly(Path);
assembly.MainModule.LoadSymbols();

// Änderungen

assembly.MainModule.SaveSymbols();
AssemblyFactory.SaveAssembly(assembly, Path);

Bisher habe ich herausgefunden, dass es so bei einigen funktioniert bei anderen aber nicht.

Hätte hier noch jemand einen Tipp was ich probieren könnte oder evtl. eine Alternative zu Mono.Cecil (wenn möglich nicht PostSharp)?

Wäre so etwas z. B. mit LinFu möglich (da es ja auf Cecil basiert) und funktioniert dort das Debuggen?

06.03.2010 - 00:42 Uhr

Du musst auf beide Spalten zusammen einen unique constraint legen, nicht auf jede spalte einzeln.

27.02.2010 - 01:12 Uhr

Wenn ich dich richtig verstanden habe wäre das Stichwort Model Driven UI

26.02.2010 - 16:59 Uhr

Afaik ist im .Net / SqlServer Bereich folgendes vermehrt anzutreffen:

Entity - Tabellen (Englisch, PascalCase, Plural)
Customers
Orders

M:N Tabellen
Einen "vernünftigen" neuen Begriff für diese Relation erstellen oder aber aus den teilnehmenden Tabellen zusammengesetzt:
CustomersAddresses
UsersRoles
oder
CustomersToAddresses
UsersToRoles
oder
CustomerAddresses
UserRoles
oder
Customers_Addresses
Users_Roles

Spalten (PascalCase, Singular)
FirstName
LastName

**Primary Key Spalte **
Id
oder
CustomerId (Tabellenname + Id)

Foreign Key Spalte
CustomerId
FKCustomerId
Customer

29.06.2009 - 00:12 Uhr

Das ist SQL eigentlich standardmäßig.

08.06.2009 - 17:49 Uhr

Das ist in jedem Fall ja sehr Ähnlich zu dem was ich gemacht habe, bei mir verwendet z. B. der MessageBus den "CommandHandler" intern, genau wie das restliche Programm wenn es um asynchrone Ausführung geht.

Bei mir war der Haupgrund für den CommandHandler das einfachere Unittesting der asynchronen Geschichte, bei den Unittests schiebt man einfach einen CommandHandler ein der alles synchron durchläuft -> Kein Problem mit Threading während der Unittests.

08.06.2009 - 16:08 Uhr

Aber was ist z.b. mit nicht gespeicherten Daten?
Abfragen an den Benutzer ob noch gespeichert werden soll, o.ä.
sollten schon ggf mit Abbrechen beantwortet werden können,
und dann hat wohlmöglich jemand zu früh den Service gekillt.

Naja man muss ja nicht direkt eine ShutDownMessage losschicken sondern einfach ein Request und wenn man ein Response bekommt heißt es, das irgend etwas noch Zeit benötigt, kommt kein Response innerhalb einer bestimmten Zeit kann man halt schließen.

Insgesammt ist bei Messages und ServiceBus sachen die asynchrone Arbeit
meist viel schwieriger und Fehleranfällig, als die sequentielle.
Du musst dann ja alles mögliche ThreadSave implementieren, was sonst meist
nicht nötig wäre.

Ja das stimmt definitiv und ist auch einer der Gründe warum ich die Frage gestellt habe, ich kann momentan noch nicht abschätzen wie groß der Mehraufwand wirklich ist.

Incl. Contextswitch um aus Nebenthreads direkt auf den UI-Thread zu kommen.

Habe ich bei meiner kleiner Testanwendung halbwegs drin (wenn auch Quick & Dirty aber es funktioniert).

Bei mir ist die Signatur der Subscibefunktion momentan:

SubscribeTo<T>(Action<T> handler, bool receiveInMainThread, Func<T, bool> restriction) where T : IMessage

IMessage ist ein leeres Markerinterface und es gibt natürlich entsprechende Überladungen, nur mit dem Handler oder Handler + Restriction oder nur Handler und MainThread.

Ein Aufruf der alles benutzt würde halt so aussehen:


_bus.SubscribeTo<LoginResonse>(LoginResponseHandler, true, x => x.Failed);

08.06.2009 - 15:32 Uhr

... da ich mir auch mal ein paar Gedanken über so eine Architektur gemacht habe, habe diese Idee dann aber relativ schnell verworfen.

Genau das ist der Grund für meine Frage, das Interesse, bisher habe ich es noch nicht gemacht (ledigleich ein kleines ProofOfConcept Progrämmchen), finde es aber sehr interessant, dass man dann überhaupt keinerlei Referenzen mehr auf irgendwelche Service/Repository Interfaces benötigt, lediglich auf die Messages und den MessageBus.

Du bist halt danach extrem flexibel was zur Folge hat das du wahrscheinlich wesentlich mehr Code wiederverwenden kannst. Also schon eher eine Überlegung Wert das nicht auf die eine Clientanwendung zu beschränken sondern es von anfang an als Framework zu entwickeln auf das man mehrere Anwendungen aufsetzen kann.

Natürlich wäre das sehr gut, allerdings gibt es dabei ein Problem, wie und wo die MessageQueue speichern und wie an andere Rechner versenden, was bei Fehler machen etc.

In einer Clientanwendung gibt es all diese Probleme nicht, eine Message kann nicht einfach verloren gehen da es alles nur in einer Queue<IMessage> o. Ä. vorhanden ist und einfach losgeschickt wird.

Das mit dem grundsätzlich Asynchron sollte man aber nochmal überdenken,
denn es gibt schon messages ( ShutDown z.b. ) die sollten synchron refolgen.

Natürlich bedarf grundsätzlich asynchron zu Arbeiten einer großen Änderung der Art wie man programmiert, allerdings lässt sich damit das Meiste erledigen.

Mir fällt z.B. nichts ein, was so fatal daran wäre, wenn die ShutDown-Message von Service A ein paar ms vor Service B empfangen wird.

08.06.2009 - 15:06 Uhr

ja, diese Annahme ist falsch.

Oh das wusste ich nicht, war vorher wohl auch noch nie zu einem Problem geworden, danke 😃

Ich glaube ich weiß jetzt, wo das Problem liegt. Deklariere innerhalb der Schleife eine Variable currAction, weise bei jedem Durchlauf item.Value an currAction zu und verwenden .Click += (x, y) => currAction();.

Danke, damit funktioniert es.

07.06.2009 - 21:44 Uhr

ein Dictionary ist eine unsortierte/unsortierbare Datenstruktur. Der Presenter kann also gar nicht tun, was du behauptest. Du könntest SortedDictionary verwenden.

Ist es also falsch, wenn ich davon ausgehe, dass wenn ich per foreach-Schleife ein Dictionary durchgehe, dann die KeyValuePairs in der Reihenfolge durchgehe wie ich sie hinzugefügt habe? (Weil das in der richtigen Reihenfolge hinzufügen meinte ich in diesem Fall mit sortieren)

Die anonyme Function wird erst ausgeführt, wenn der Click erfolgt. Zu dem Zeitpunkt bezeichnet _actions.Count - 1 aber in jedem Fall die letzte je angefügte Action

Das heißt also, es wird nicht die Funktion "an das Event übergeben" sondern bei Ausführung an der zuweisenden Stelle "geguckt"?

Edit:

Wenn im Dictionary die korrekten Aktionen eingetragen sind und wenn man von dem Problem mit der Sortierung absieht, sollte es wie schon gesagt korrekt funktionieren.

Das ist aber leider nicht der Fall, es wird immer die letzte Aktion aufgerufen, auch wenn ich .Click += (x, y) => item.Value(); mache

07.06.2009 - 18:40 Uhr

Hallo,

ich hätte mal eine Frage, was ihr von Messageing in Clientanwendungen haltet, ohne WebServices etc.

Man erstellt also eine Art MessageBus welches Publish / Subscribe / Unsubscribe Funktionen hat und verschickt dann einfach Messages asynchron in der Anwendung umher.

Vorteile wären:*Sehr lose Koppelung *Logging sehr einfach - auch nachträglich - implementierbar *Grundsätzlich Asynchron *Umstieg auf Services um ein vielfaches einfacher

Nachteile:*Single Point of Failure (MessageBus) *Komplexer / Aufwendiger *Programmfluss schwerer nachzuvollziehen

07.06.2009 - 18:23 Uhr

Das ganze kommt wieder in deine vorhandenen 3 Schichten rein.

Als Beispiel rufst du dann in der Form, wo du die Konfigurationseinstellungen anzeigst / bearbeiten lässt, etwas auf wie: IAppConfig.GetCurrentConfig(), diese Funktion befindet sich in der mittleren Schicht und ruft wiederum eine Funktion in der Datenschicht auf um die Konfiguration aus einem Speicher zu bekommen, diese "Rohkonfiguration" wird dann aufbereitet und zurück gegeben.

Wenn der Benutzer nun Änderungen gemacht hat und diese speichern will, ruft er in der mittleren Schicht eine entsprechende Funktion auf welche die neue Konfiguration validiert und - sofern sie gültig ist - speichert.
Danach wird ggf. ein Neustart initiiert bzw. die neue Konfiguration direkt On The Fly angewendet.

Gerade bei kleineren Projekten - und vor allem Anfang - kann und wird es dir passieren, dass deine Mittlere Schicht sehr "dünn" ist, also dass die Datenschicht dir schon mehr oder weniger Objekte übergibt welche einfach nur durchgereicht werden.

Ein solches verhalten solltest du schnellstmöglich korrigieren, weil eine solche Proxyschicht keinerlei Wert hat.

07.06.2009 - 17:55 Uhr

Erstmal danke für die Antwort 😃

Es kann also sein, dass "Sitzung|Anmelden, LustigeAnmeldeFunktion" vor "Sitzung, null" verarbeitet wird, was dann zu Problemen führt.

In dem Dictionary wären ja nicht nur diese 2 Einträge sondern sehr viele mehr (im Prinzip das gesamte Menü) und sehr viele davon führen eine Aktion aus.
Das Dictionary kommt vom Presenter welcher das Dictionary so sortiert, dass die Parents jeweils vor den Childs enthalten sind.

Was genau verursacht hier das Problem? (Das Menü wird korrekt gebaut, nur wie gesagt, alle im Menü enthaltenen Punkte rufen die letzte übergebene Funktion auf (soweit ich das gesehen habe, sind nur die Menüpunkte betroffen wo item.Value != null war, also dort wo korrekterweise eine Funktion an das Click-Event gebunden wurde).

Wenn ich für jeden Eintrag eine Instanz einer Klasse verwende wo mehr oder weniger genau diese Funktion im Konstruktor enthalten ist, dann funktioniert es.

07.06.2009 - 04:09 Uhr

Hallo,

ich habe momentan ein kleineres Problem, zunächst einmal der Codeausschnitt:


public void UpdateMenu(Dictionary<string, Action> items)
{
    foreach (var item in items)
    {
        var splitted = item.Key.Split('|');

        var currentName = splitted[splitted.Length - 1];
        var mItem = new ToolStripMenuItem(currentName);

        _menuItems.Add(item.Key, mItem);

        if (item.Value != null)
        {
            _actions.Add(item.Value);
            mItem.Click += (x, y) => _actions[_actions.Count - 1]();
        }

        if(splitted.Length == 1)
        {
            _uxMenu.Items.Add(mItem);
        }
        else
        {
            var pathToParent = string.Empty;

            for (int i = 0; i < splitted.Length-1; i++)
            {
                if (i > 0)
                {
                    pathToParent += "|";
                }
                pathToParent += splitted[i];
            }

            _menuItems[pathToParent].DropDownItems.Add(mItem);
        }
    }
}

Ich baue in dieser Funktion ein Menü, in dem übergebenen Dictionary befinden sich Beispielsweise so etwas:
Sitzung, null
Sitzung|Anmelden, LustigeAnmeldeFunktion

Das Problem ist nun, alle MenuItems rufen beim Click die letzte übergebene Action auf, darum habe ich auch testweise eine IList<Action> verwendet (vorher war es mItem.Click += (x, y) => item.Value();).

In der ActionList sind die korrekten Actions abgelegt, seltsamerweise sehe ich jedoch per Reflection, wenn ich das MenuItem bis Component hinuntergehe, dass bei den Events nur die letzte übergeben Action drin steht.

Meine Fragen nun:

  1. Warum ist das so?
  2. Was kann ich dagegen tun? :>

Edit:
Achja, wenns geht, ohne eine eigene Klasse zu erstellen (das ist jetzt mein Workaround)

29.05.2009 - 16:24 Uhr

die datenbank heißt nur Test?

dann mach mal
<add key="hibernate.connection.connection_string" value="Server=localhost\SQLEXPRESS;Database=Test;User ID=testuser;Password=huhuhuhu" />

Testweise könntest du auch mal kurz auf WindowsAuthentification stellen und den hier nutzen:
Data Source=localhost\SQLEXPRESS;Initial Catalog=Test;Integrated Security=SSPI;

Welche Version von NHibernate benutzt du?

29.05.2009 - 15:08 Uhr

warum möchtest du das machen?

guck dir folgendes an:
delegates, anonyme methoden, Action (Action, Action<T>, Action<T1, T2......>)

24.05.2009 - 12:02 Uhr

wenn du einer DataTable per Rows.Add(row) eine Row hinzufügst, ist der RowState auf Added, du musst also nach deinem geadde einfach AcceptChanges aufrufen.

Prinzipiell musst du es aber gar nicht so machen wie du es dort machst, zieh einfach eine BindingSource auf die Form, setze dessen DataSource auf deine DataTable und setze die DGV DataSource auf die BindingSource, dann hast du direkt Sort schon eingebaut wenn jmd auf den ColumnHeader klickt.

10.05.2009 - 22:11 Uhr

Hallo,

ich möchte grad ein ganz einfaches Setupprogramm erstellen, sprich paar Dateien ins vom Benutzer angegeben Verzeichnis kopieren + Startmenüeinträge etc.

Funktioniert auch, wäre da nicht das kleine Problem, dass vorweg - oder im Nachhinein - ein Treiber installiert werden muss welcher auch den MS Installer verwendet -> Wenn ich es aus meinem Installer heraus ausführe kommt logischerweise eine Meldung von wegen zwei MSInstaller Instanzen bitte beenden Sie die erste Installation etc.

Nach etwas rumgegoogle hab ich ein SourceForge-Projekt gefunden, jedoch muss dort der Pfad zur Treiberinstallationsdatei im Code angegeben werden, was irgendwie recht Sinnfrei ist wenn dieser erst vom Benutzer eingestellt wird.

Gibt es irgendwie eine einfache Möglichkeit ohne etwas externes zu basteln wo ich einfach diese zwei Installer nacheinander laufen lassen kann?

07.05.2009 - 11:13 Uhr

Zu 1:
Ich würde die Service.GetIrgendwas() Tests definitiv gegen eine richtige Datenbank laufen lassen, denn im Prinzip möchtest du ja die Querylogik testen (sei es nun SQL, XML oder was auch immer).
Wenn du später dann Tests machst welche auf diese Services zugreifen und du nicht die Daten selbst testen möchtest sondern was mit diesen geschieht, würde ich einfach die Services mocken und mit vordefinierten Daten arbeiten.

Zu 2:
Hier gehen die Meinungen relativ weit auseinander, einige meinen das man alles was Public zur verfügung gestellt wird testen sollte, egal wie simpel es ist, anderen reicht es das zu testen, was etwas komplexer ist.
Ist eine Geschmacks und Ermessenssache.

Zu 3:
Du solltest auch die Validate Methode einzeln prüfen, aus einem ganze einfachen Grund, wenn diese Tests fehlschlagen, weil die Validierungslogik nicht stimmt, wird auch der Executetest fehlschlagen, selbst wenn die Executelogik ansich richtig ist.

06.05.2009 - 15:58 Uhr

Eigentlich müssten auch diese beiden Möglichkeiten funktionieren:

1.


Drop table Statistiken.dbo.Deinetabelle
Drop Database Statistiken
Create Database Statistiken
Create table Statistiken.dbo.Deinetabelle


use Statistiken;
Drop table Deinetabelle;
use master;
Drop Database Statistiken;
Create Database Statistiken;
use Statistiken;
Create table Deinetabelle;

22.04.2009 - 14:49 Uhr

du hast also grob gesagt:



public class class1
{
    public class1(string para)
    {
        //...
    }
}

public class class2 : class1
{
    public class2(string para)
        : base(para) {}
}

public class class3 : class2
{
    public class3(string para)
        : base(para) {}
}

also auch wirklich bei jedem : base(para) gemacht?

13.04.2009 - 01:31 Uhr

select test.Beschreibung, M1.Name, M2.Name
from test
inner join Mensch M1 on test.BenutzerId = M1.MenschId
inner join Mensch M2 on test.ErschafferId= M2.MenschId

11.04.2009 - 01:24 Uhr

Beim Datenbankzugriff gehen die Meinungen recht stark auseinander, DIE Lösung gibt es dabei nicht ;>

Rainbird hat vor relativ kurzer Zeit eine Umfrage gemacht, Adapter und Reader waren da afaik gleichauf z. B.

Um Datenbankunabhängig zu sein guck dir mal die Klassen DbProviderFactory, DbProviderFactories (static, erstellt die Factory anhand des Providernamens (System.Data.SqlClient z. B.)), DbConnection, DbCommand etc. an, müsste System.Data Namespace sein.

Das ganze könnte dann so ablaufen, dass du in der App Config die ConnectionString eigenschaft verwendest und dort sowohl Provider als auch Connectionstring angibst, mit dieser könntest du dann mit einer statischen Helfermethode die entsprechende Factory/direkt Connection etc. zurückgeben lassen.

Implementiert könnte das z. B. so ausschauen:


public delegate T DbDataReaderHandler<T>(DbDataReader reader);

    public static class DAO
    {
        private static readonly Dictionary<string, DbProviderFactory> _factories =
            new Dictionary<string, DbProviderFactory>();

        public static T ExecuteReader<T>(string connectionName, string command, DbDataReaderHandler<T> handler,
                                         params Parameter[] parameters)
        {
            using (var con = CreateConnection(connectionName))
            {
                using (var cmd = CreateCommand(con, command, parameters))
                {
                    con.Open();

                    using (var reader = cmd.ExecuteReader())
                    {
                        return handler(reader);
                    }
                }
            }
        }

        public static void ExecuteNonQuery(string connectionName, string command, params Parameter[] parameters)
        {
            using (var con = CreateConnection(connectionName))
            {
                using (var cmd = CreateCommand(con, command, parameters))
                {
                    con.Open();

                    cmd.ExecuteNonQuery();
                }
            }
        }

        public static T ExecuteScalar<T>(string connectionName, string command,
                                         params Parameter[] parameters)
        {
            using (var con = CreateConnection(connectionName))
            {
                using (var cmd = CreateCommand(con, command, parameters))
                {
                    con.Open();

                    var value = cmd.ExecuteScalar();

                    if (value == DBNull.Value || value == null)
                        return default(T);

                    return (T) value;
                }
            }
        }

        public static DataTable ExecuteAdapter(string connectionName, string command,
                                               params Parameter[] parameters)
        {
            var result = new DataTable();

            using (var con = CreateConnection(connectionName))
            {
                using (var cmd = CreateCommand(con, command, parameters))
                {
                    con.Open();

                    using (var adapter = CreateAdapter(connectionName, cmd))
                    {
                        adapter.Fill(result);
                    }
                }
            }

            return result;
        }

        private static DbDataAdapter CreateAdapter(string connection, DbCommand cmd)
        {
            var result = _factories[connection].CreateDataAdapter();
            result.SelectCommand = cmd;
            return result;
        }

        private static DbCommand CreateCommand(DbConnection con, string command,
                                               IEnumerable<Parameter> parameters)
        {
            var cmd = con.CreateCommand();

            cmd.CommandText = command;

            foreach (var parameter in parameters)
            {
                var para = cmd.CreateParameter();
                para.Value = parameter.Value;
                para.ParameterName = parameter.Name;
                cmd.Parameters.Add(para);
            }

            return cmd;
        }

        private static DbConnection CreateConnection(string connectionName)
        {
            var setting = ConfigurationManager.ConnectionStrings[connectionName];
            var provider = setting.ProviderName;

            if(!_factories.ContainsKey(provider))
                _factories[provider] = DbProviderFactories.GetFactory(provider);

            var result = _factories[provider].CreateConnection();
            result.ConnectionString = setting.ConnectionString;

            return result;
        }
    }

09.04.2009 - 21:01 Uhr

um das ein wenig zu präzisieren: es ist schlimmer sehr viele assemblys im speicher zu haben als einige wenige aber dafür umso größere.

Hast du dafür evtl. ein paar Zahlen ab wann sich das wie bemerkbar macht?

05.04.2009 - 03:03 Uhr

Regex?

04.04.2009 - 21:29 Uhr

Was du evtl. machen könntest wäre eine Abstrakte Klasse:


public abstract class Whatever
{
  protected bool _initialized;
  
  public void Run()
  {
     if(!initialized) 
     {
         Init();
     }
     PreRun();
     WhileRunning();
     PostRun();
     CleanUp();
  }

  protected virtual void Init()
  {
      _initialized = true;
  }
  protected virtual void PreRun() {}
  protected virtual void PostRun() {}
  protected virtual void CleanUp() {}
  protected abstract void WhileRunning();
  
}

28.03.2009 - 17:54 Uhr

Wie genau lautet denn die Fehlermeldung?

28.03.2009 - 17:51 Uhr

Du erstellst dir einfach ein Event in deinem UC und abonierst dieses im Container.

Schnelle Variante zum Testen:


public delegate void ABCHandler(char chosenChar);
public class DeinUserControl : UserControl 
{
  public event ABCHandler ChosenCharChanged;
  
  private void DeinLinkLabelClickAbonierTeil(object sender, ...)
  {
     if(ChosenCharChanged != null) 
     {
         ChosenCharChanged(((LinkLabel)sender).Text[0]);
     }
  }
}

Du solltest dir aber eigentlich eine von EventArgs abgeleitete Klasse erstellen und dann public event EventHandler<DeineArgs> ChosenCharChanged benutzen.

28.03.2009 - 17:44 Uhr

cmd.Parameters.AddWithValue("@URL", neu.url); -> afaik musst du dort das @ weglassen. Aber Acces geht afaik eh nach Position.

25.03.2009 - 22:40 Uhr

Nochmal wegen den Lizenzen usw.

Viele Schulen - zumindest Berufsschulen - stellen für Azubis ein MSDN Academic Alliance Konto bereit, dort gibts alle Betriebssysteme(auch Server), VS normal / Prof 08 05 03, Sql Server etc. für privaten Gebrauch / Ausbildungszwecke kostenlos.

22.03.2009 - 13:53 Uhr

Das Zweite ist vom Syntax her ein Karteischer Join, also etwas was du idR. nicht machen möchtest, Sql Server / Oracle etc. macht davon aber intern den Inner Join, Access könnte mit Pech den Karteischen Join machen, ob es dies tut oder ob es weg optimiert wird weis ich nicht.

Dieses geklammere hast du btw. auch nur bei Acces, sonst reicht:

Select * From bla
inner join blub on bla.blub = blub.bla
inner join bli on bla.bli = bli.bla
etc

Bei der Joinmenge: 5 Joins gehen auf jeden Fall noch ganz gut.

21.03.2009 - 01:36 Uhr

Auch ne Möglichkeit wäre:


SELECT    * 
FROM      Foto
WHERE     ( EXISTS ( SELECT NULL
                     FROM FotoStichwort AS fs                     
                     WHERE fs.StichwortID = 1 AND fs.FotoID = Foto.FotoID))           
          AND  
          ( EXISTS ( SELECT NULL
                     FROM FotoStichwort AS fs2 
                     WHERE fs2.StichwortID = 2 AND fs2.FotoID = Foto.FotoID))

20.03.2009 - 22:04 Uhr

mach halt aus WHERE stichwort.inhalt = "Familie" OR stichwort.inhalt = "Portrait"
WHERE stichwort.inhalt = "Familie" AND stichwort.inhalt = "Portrait" ?

18.03.2009 - 22:22 Uhr

dann mach mal


SqlParameter para = new SqlParameter("ArtikelID", ArtikelID);
cmd.Parameters.Add(para);

statt
cmd.Parameters.AddWithValue("@ArtikelID", ArtikelID);

Anmerkung:
Guck dir mal die Naming-Conventions an, Methodennamen werden idR. im PascalCase benannt und Parameter sowie lokale Variablen im camelCase.

Beispiel wie die Methode dann aussehen könnte:


public static DataTable GetPreise(int artikelId)
{
    const string query = 
        "SELECT * "+
        "FROM Artikelpreise "+
        "WHERE ArtikelID = @ArtikelID";

    using(var con = new SqlConnection(Properties.Settings.Default.DBConnectionString))
    {
        using(var cmd = new SqlCommand(query, con))
        {
            cmd.Parameters.Add(new SqlParameter("ArtikelID", artikelId));
            using (var adapter = new SqlDataAdapter(cmd))
            {
                var table = new DataTable();
                con.Open(); // Hier bin ich mir grad nicht ganz sicher ob der Adapter das nicht automatisch macht.
                adapter.Fill(table);
                return table;
            }
        }
    }
}

18.03.2009 - 21:12 Uhr

Gründe die mir gerade einfallen:

  1. Wie schonmal gesagt wurde: cmd.Parameters.Add("@ArtikelID", SqlDbType.Int); sagt nur das du einen Parameter vom Typ Int mit Namen @@ArtikelID(man beachte das @@) hinzufügst jedoch ohne Wert.

  2. Wahrscheinlicher: Wie oben gesagt mit cmd.Parameters.Add("@ArtikelID"....) fügst du einen Parameter für @@ArtikelID hinzu, ändere dies zu cmd.Parameters.Add("ArtikelID"....), das @ musst du nicht bzw. darfst du nicht setzen wenn du einen Parameter hinzufügst, damit sprichst du im Sql lediglich Parameter an.

18.03.2009 - 19:50 Uhr

genau das hätte ich auch vorschlagen ... Irgendwo fehlt dir glaub ich etwas Grundverständnis MaXeM.

Du meinst Emilio111 oder? ^^

18.03.2009 - 19:02 Uhr

Warum musst du dafür wissen wo die Daten herkommen?

ändere doch einfach DataTable getPreise() in DataTable getPreise(int id)

13.03.2009 - 20:58 Uhr

Dieses Problem hat man eigentlich bei allen OR Mappern welche einen Kontext für das ChangeTracking verwenden, bei NHibernate äussert sich dies z. B. dadurch, dass bei einer Anwendungsweiten Session (Pendant zum ObjectContext) das Flushen immer länger dauert, je mehr Entities in der Session sind. Eine Abhilfe schafft hierbei das aufrufen von Evict auf Entities (werden vom ChangeTracking dann ausgeschlossen), inwieweit dies beim EF nötig ist, kann ich nicht sagen.

Das ausschließliche Arbeiten mit detached Entities (RepositoryPattern z. B. -> Session ist nach Abschluss der Methode nicht mehr vorhanden) hat den Nachteil, dass man eigenes ChangeTracking einbauen muss -> Session Per Call.
Der Vorteil ist, der Client muss nicht im geringsten wissen, dass intern NHibernate verwendet wird.

Dann, gerade in ASP.Net beliebt, Session Per View, beim start eines WebRequests wird die Session geöffnet, alles geholt und am ende wird die Session verworfen. In WinForms wird dieser Ansatz auch recht gerne genommen, hat aber einige Nachteile, spätestens in MDI ähnlichen Anwendungen, wenn Form a Entity A aus Session 1 anzeigt (Kundenübersicht z. B.) und Form b Entity A in Session 2 speichern möchte (Detailform) (ein Objekt kann nicht zeitgleich in zwei offenen Sessions vorhanden sein).

12.03.2009 - 19:29 Uhr

Ich würde an deiner Stelle die ganzen Entities also Klassen welche du mit Daten füllst aus dem DI Container rausnehmen und diese konkret erstellen, erleichtert sehr vieles.

Da ich selbst das "Service Antipattern" verwende, kommen bei mir eigentlich nur die Services in den Container rein, die Entities verwende ich - zumindest dort wo sie befüllt werden - konkret, ob du sie als Interface oder auch konkret übergibst ist relativ egal, nur dass du beim Interface übergeben mehr oder weniger eine CreateNew<IEntity>() Funktion irgendwo brauchst.

05.03.2009 - 20:43 Uhr

in dem Zusammenhang kann ich dir http://www.win2008workstation.com/wordpress/ nur empfehlen.

04.03.2009 - 17:05 Uhr

die BindingSource hat eine ResetBinding funktion, dies sollte helfen.

04.03.2009 - 17:04 Uhr

Hallo,

da es mir nicht Konkret um WebServices / WCF etc. geht poste ich es mal hier rein und nicht bei Netzwerk.

Da es bei SOA ja darum geht, wiederverwendbare Komponenten zu bastlen und zur Zeit bei uns nicht allzuviel los ist und ich mich mal mit Services beschäftigen wollte, wollte ich fragen ob folgender Aufbau Sinn machen würde.

Also ganz unten wären Services wie AdressService, dieser behandelt nur Adressen ohne irgendwelche Referenzen zu wem / was diese gehören, dieser ermöglich Abfragen per Id, Straße u. Ä., darauf könnte z. B. ein KundenService aufbauen, dieser erlaubt ebenfalls Abfragen per Id sowie Umsatz, Firma, Vorname etc., auf diesen sowie einigen anderen Services (Artikel, Lager, User, Standort etc.) würde dann letztlich z. B. ein VerkaufService aufsetzen welcher es ermöglicht etwas zu Verkaufen.

Macht eine solche feine unterteilung Sinn? Sprich wäre die Latenz bei einem solchen Aufbau noch tragbar? oder wäre das zu viel Overhead?

Weil z. B. der Adress/KundenService wäre ja in einer Reihe von Anwendungen / Services verwendbar und auf den VerkaufService könnte man ja noch eine Reihe von weiteren Dingen aufbauen (Reklamation etc.), die Wiederverwendbarkeit wäre also definitiv gegeben.

01.03.2009 - 17:58 Uhr

Nein das hat nichts direkt mit Referenzen zu tun. Allerdings macht es Sinn hier einen Foreign Key zu setzen.

Im Select Befehl siehst du, dass ich die Tabelle Keywords in der From Klausel drin hab und die Tabelle Files durch einen Inner Join, per Tabelle.Feld kannst du auf jedes Feld zugreifen, sofern du die Tabelle im Query eingebunden hast. Dies funktioniert auch wenn du einer Tabelle einen Alias gibst.

01.03.2009 - 16:45 Uhr

Beispiel du hast ein Dokument mit dem Schlagwort Test, gibt der User nun Test ein wäre der Befehl z. B.

select * from Files where Id in (select FileId from Keywords where Keyword = 'Test')

Achtung:

  1. Dies nicht verwenden, statt 'test' parameter nehmen!
  2. Ein where Id in wird sehr wahrscheinlich langsamer sein als ein where exists

Edit:
Eigentlich ginge es sogar ohne where id in / exists:

select files.* from keywords
inner join files on keywords.fileId = files.Id
where keywords.Keyword = 'test' <- auch hier im richtigen code parameter nehmen

01.03.2009 - 14:31 Uhr

Eine Tabelle für alle Stichwörter mit z. B. Id, FileId, Keyword

26.02.2009 - 07:06 Uhr

Hallo,

den Umstieg habe ich selbst auch erst vor ein paar Tagen gemacht bzw. ich bin gerade etwas nebenbei am herumprobieren, damals hat mir WebForms absolut nicht zugesagt und so habe ich fast ausschließlich WinForms gemacht.
Vor ein paar Tagen habe ich mir dann mal Asp.net MVC angeguckt und finde es auf jeden Fall sehr viel besser (weit mehr, eigentlich volle Kontrolle über das HTML).
Sehr zu empfehlen ist dabei das MVC Storefront von Rob Conery, sind glaub 26 Teile.

15.02.2009 - 01:38 Uhr

Extras -> Optionen -> Windows-Forms Deisgner -> LayoutMode auf SnapToGrid stellen.

15.02.2009 - 00:52 Uhr

Hallo,

in meinem aktuellen Projekt muss ich mit recht vielen Daten arbeiten (~10k Datensätze aus CSV Dateien einlesen, mit DB vergleichen und entsprechend speichern) und hab ich mich somit für Typed DataSets entschieden.

Da ich im laufe des Projektes öfters Joins brauche und nicht jedes mal zwei Queries machen wollte habe ich etwas rumgesucht jedoch kein Snipped o. Ä. gefunden, also hab ich es selbst gebastelt:


private readonly string _connectionString;
private DbConnection _currentConnection;
private readonly DbProviderFactory _factory;

public DataService(string connectionString)
{
    _factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
    _connectionString = connectionString;
}

private DbConnection Connection
{
    get
    {
        if (_currentConnection == null)
        {
            _currentConnection = _factory.CreateConnection();
            _currentConnection.ConnectionString = _connectionString;
            _currentConnection.Disposed += _currentConnection_Disposed;
            _currentConnection.Open();
        }

        return _currentConnection;
    }
}

private void _currentConnection_Disposed(object sender, EventArgs e)
{
    _currentConnection.Disposed -= _currentConnection_Disposed;
    _currentConnection = null;
}

public void FillJoin(string sqlCommand, IDictionary<string, object> parameters,
                     params DataTable[] tables)
{
    DataTable qryTable = new DataTable();

    using(Connection)
    {
        using(var adapter = _factory.CreateDataAdapter())
        {
            using(var cmd = Connection.CreateCommand())
            {
                cmd.CommandText = sqlCommand;
                if(parameters != null)
                {
                    foreach(var pair in parameters)
                    {
                        var para = cmd.CreateParameter();
                        para.Value = pair.Value;
                        para.ParameterName = pair.Key;
                        cmd.Parameters.Add(para);
                    }
                }
                adapter.SelectCommand = cmd;

                adapter.Fill(qryTable);
            }
        }
    }

    foreach(DataRow row in qryTable.Rows)
    {
        int qryColumn = 0;

        for(int tbl = 0; tbl < tables.Length; tbl++)
        {
            DataRow resultRow = tables[tbl].NewRow();
            for(int column = 0; column < tables[tbl].Columns.Count; column++)
            {
                if(column == 0)
                {
                    if(tables[tbl].Rows.Find(row[qryColumn]) != null)
                    {
                        qryColumn += tables[tbl].Columns.Count;
                        break;
                    }
                }
                resultRow[column] = row[qryColumn];
                qryColumn++;
            }

            if(resultRow.ItemArray[0] != DBNull.Value)
            {
                tables[tbl].Rows.Add(resultRow);
                resultRow.AcceptChanges();
            }
        }
    }
}

Anwendungsbeispiel:


var set = new TestSet();     // Typed  DataSet
const string qry = "SELECT t1.*, t2.* " +
                   "FROM Table1 t1 " +
                   "INNER JOIN Table2 t2 ON t1.T2Id = t2.Id";
_dataService.FillJoin(qry, null, set.Table1, set.Table2);

Diese Lösung geht davon aus, dass in der ersten Spalte einer Tabelle das Id Feld ist, da dies bei mir bisher immer der Fall war hab ich mir keine weiteren gedanken dazu gemacht.

Naja nun die eigentliche Frage:
Gibt es schönere Lösungen für sowas?

12.02.2009 - 00:21 Uhr

Schau dir mal den OPENROWSET SqlBefehl an, könnte das machen was du brauchst.