Gerne, und sorry an haarrrgh für die "feindliche Übernahme" des Threads.
Die CommandBus Sache ist natürlich nicht auf meinem Mist gewachsen, aber ich habe sie auf meinen Abenteuern im Bereich CQRS aufgegabelt und finde es eine super Idee, auch unabhängig von CQRS, d.h. als eleganter Ersatz für den typischen Servicelayer.
Mich würde allerdings schon interessieren, was das für eine Anwendung ist, die solche wahnwitzig großen Mengen an Objekten im Speicher hält.
Hi,
Ich habe mal ein etwas komplexeres Beispiel zusammengebastelt, siehe Anhang. Der erwähnte Reflection-lastige Teil befindet sich in Messaging\Registration des CommandBusApp.Core Projekts, insbesondere in MessageHandlerDiscovery. Siehe dazu auch die readme.txt.
Ansonsten zeigt der Code noch einige Möglichkeiten, wie man Systeme erweiterbar halten kann (Open-Closed Principle) sowie die Anwendung eines DI-Containers (hier: Ninject). Es ist übrigens zu erwähnen, dass das Core-Projekt nichts von einem DI-Container weiß.
Achtung: das ist kein Produktionscode, sondern einfach aus der Hüfte geschossen.
Grüße,
Andre
int.MaxValue ist 2147483647, also 2,14 Milliarden.
Es geht ja nicht um Flusskontrolle. Aber wenn FooLoader.Load null zurückgibt, was sagt es dir über die Ursache, warum das Laden fehlgeschlagen ist? Zugriffsrechte? Fehlendes Verzeichnis? Kosmische Strahlung?
Das bedeutet ja nicht, dass man nicht mal kurz vor dem Aufruf von FooLoader.Load checken kann, ob die Datei besteht etc.
Noch einen Kommentar hierzu:
internal class FooSaver { internal bool Save(Foo foo, string targetPath) { // tatsächlich speichern und return true wenn fertig ohne fehler } } internal class FooLoader { internal Foo Load(string sourcePath) { // tatsächliches laden, null wenn nicht möglich } }
Beide Methoden sollten meiner Meinung nach eine Exception werfen, wenn etwas schiefgeht, nicht null oder false. Sonst muss die aufrufende Methode die Rückgabewerte kontrollieren usw. Das ist mit Exceptions deutlich übersichtlicher.
Ich habe mir mittlerweile angeeignet, dass Argumente und Rückgabewerte standardmäßig nie null sein können. Die wenigen Ausnahmen, welche idealerweise auf private Methoden beschränkt sein sollten, müssen entsprechend eindeutig dokumentiert sein.
Grüße,
Andre
An sich finde ich die Verwendung von Streams bei so etwas eine gute Sache, eben weil sie so abstrakt sind. Eine Überladung mit Pfaden kann man immer noch anbieten. Wenn du das nicht möchtest - wie wärs mit so etwas:
interface IStreamProvider {
Stream OpenRead(string name);
Stream OpenWrite(string name);
void Close(Stream stream);
}
class FileStreamProvider : IStreamProvider {
public Stream OpenRead(string name){ return File.OpenRead(name); }
public Stream OpenWrite(string name){ return File.OpenRead(name); }
public void Close(Stream stream){ stream.Close(); }
}
class MemoryStreamProvider : IStreamProvider {
private MemoryStream stream;
public MemoryStreamProvider(MemoryStream stream){
this.stream = stream;
}
public MemoryStreamProvider():this(new MemoryStream()){
}
public MemoryStream Stream{get {return stream;}}
public Stream OpenRead(string name){ return stream; }
public Stream OpenWrite(string name){ return stream; }
public void Close(Stream stream) { /* nichts schließen */ }
}
class FooManager {
private IStreamProvider streamProvider;
public FooManager(IStreamProvider streamProvider){
this.streamProvider = streamProvider;
}
public void Save(Foo foo, string path){
var stream = streamProvider.OpenWrite(path);
try {
new FooSaver().Save(foo, stream);
} finally {
streamProvider.Close(stream);
}
}
public Foo Load(string path){
var stream = streamProvider.OpenRead(path);
try {
return new FooSaver().Load(stream);
} finally {
streamProvider.Close(stream);
}
}
}
Dann hast du nach außen hin noch immer Pfadangaben, kannst aber für die Tests einen MemoryStreamPovider verwenden, um zu überprüfen, was geschrieben wird.
Grüße,
Andre
Hi,
Welches Load/Save willst du denn testen? Das vom Manager oder das von FooLoader/FooSaver.
Allgemein wäre es evtl. ganz nützlich, wenn du statt eines Dateinamens einen Stream verwendest, dann kanst du leicht auch besagten MemoryStream übergeben.
Nur dann habe ich den Fall nicht abgedeckt das die Datei wegen fehlenden Rechten nicht gespeichert werden konnte, der Benutzer wählt das Zielverzeichnis aus, und wenn er unter Vista/Seven "Programme" aus wählt muss ich das ja auch entsprechend handeln.
Auf jeden fall kann ich das, so denk ich, in zwei Teile Splitten.
Das Problem wird doch eher sein, dass die Methode, die die Datei schreibt, eine solche Exception gar nicht abhandeln kann sondern sie einfach nach oben durchreicht.
Grüße,
Andre
Vor allem ist das erste etwas sinnbefreit, da der Formatstring gar keinen Platzhalter mehr enthält ({0}), trotzdem aber ein Wert dafür übergeben wird. Dann kann man auch direkt ein normales String-Literal verwenden.
was passiert in der Send-Methode (einer konkreten Implementierung der Schnittstelle)? Wird das auch aufgelöst oder geht das dirket?
Prinzipiell passiert in Send Folgendes:
Bei 2 kann wenn benötigt ein DI-Container verwendet werden. Man könnte theoretisch auch die Handler einmal zu Beginn der Anwendung erstellen, aber das ist unpraktisch, da man dann keine kurzlebigen Objekte injizieren kann, z.B. eine Unit of Work.
In der Praxis mache ich es so: es gibt einen einfachen Dispatcher, bei dem IMessageHandler<T> registriert werden können, wobei T der Typ einer Nachricht (oder im Speziellen eines Commands) ist. Zudem gibt es eine Dispatch(object message) Methode. Der Dispatcher schaut sich den dynamischen Typ von message an, führt den nötigen downcast aus und ruft alle IMessageHandler<T>s für diesen Typ auf.
Das sieht in etwa so aus:
using System;
using System.Collections.Generic;
using System.Threading;
public interface IDispatcher {
void Register<TMessage>(IMessageHandler<TMessage> handler) where TMessage : class;
void Dispatch(object message);
}
public class Dispatcher : IDispatcher {
private delegate void Handler(object message);
private readonly ReaderWriterLockSlim syncLock = new ReaderWriterLockSlim();
private readonly Dictionary<Type, List<Handler>> handlers = new Dictionary<Type, List<Handler>>();
public void Register<TMessage>(IMessageHandler<TMessage> handler) where TMessage : class {
syncLock.EnterWriteLock();
try {
var list = GetOrCreateHandlerList<TMessage>();
list.Add(message => handler.Handle((TMessage)message));
}
finally {
syncLock.ExitWriteLock();
}
}
public void Dispatch(object message) {
syncLock.EnterReadLock();
try {
List<Handler> list;
if (handlers.TryGetValue(message.GetType(), out list)) {
foreach (var action in list) {
action(message);
}
}
}
finally {
syncLock.ExitReadLock();
}
}
private List<Handler> GetOrCreateHandlerList<TMessage>() {
List<Handler> list;
if (!handlers.TryGetValue(typeof(TMessage), out list)) {
list = new List<Handler>();
handlers[typeof(TMessage)] = list;
}
return list;
}
}
Der Commandbus sieht dann z.B. so aus:
using System.Transactions;
public interface ICommandBus {
void Send(object command);
}
public class InMemoryCommandBus : ICommandBus {
private readonly IDispatcher dispatcher;
private readonly IUnitOfWorkProvider unitOfWorkProvider;
public InMemoryCommandBus(IDispatcher dispatcher, IUnitOfWorkProvider unitOfWorkProvider) {
this.dispatcher = dispatcher;
this.unitOfWorkProvider = unitOfWorkProvider;
}
public void Send(object command) {
using (var scope = new TransactionScope())
using (var uow = unitOfWorkProvider.Begin()) {
dispatcher.Dispatch(command);
uow.Accept();
scope.Complete();
}
}
}
(IUnitOfWorkProvider sei hier ein Objekt, das neue UnitOfWorks öffnen kan).
Send sorgt hier für einen Contxt aus Transaktion und Unit Of Work und leitet das Command ansonsten direkt an den dispatcher weiter. Das bedeutet übrigens, dass, falls es untypischerweise mehrere Commandhandler für das gleiche Command gäbe, alle Handler in der selben Transaktion ausgeführt werden. Man muss selbst entscheiden, ob das die korrekte Semantik ist.
Jetzt fehlt noch ein Bindeglied: die CommandHandler müssen im Dispatcher registriert werden. Dazu verwende ich gerne ein wenig Reflection-Voodoo: alle Klassen zu finden, die einen oder mehrere IMessageHandler<T> implementieren ist leicht. Es wäre jetzt möglich, einfach die Handler zu instantiieren und beim Dispatcher zu registrieren. Es ist aber sinnvoller, die Handler bei jedem Dispatch neu zu erstellen, da man wie gesagt dabei dann kurzlebige Objekte (etwa eine Unit Of Work) injizieren kann. Also habe ich Wrapper-Handler, die das für mich übernehmen, und zwar einmal einen für IDisposable Handler und einen für Nicht-IDisposable Handler:
using System;
public abstract class HandlerInvoker<TMessage> : IMessageHandler<TMessage>
where TMessage : class {
private readonly Func<IMessageHandler<TMessage>> factory;
protected HandlerInvoker(Func<IMessageHandler<TMessage>> factory) {
this.factory = factory;
}
protected IMessageHandler<TMessage> InstantiateHandler() {
return factory();
}
public abstract void Handle(TMessage message);
}
public class DisposableHandlerInvoker<TMessage> : HandlerInvoker<TMessage>
where TMessage : class {
public DisposableHandlerInvoker(Func<IMessageHandler<TMessage>> factory)
: base(factory) {
}
public override void Handle(TMessage message) {
var handler = base.InstantiateHandler();
try {
handler.Handle(message);
}
finally {
((IDisposable)handler).Dispose();
}
}
}
public class NonDisposableHandlerInvoker<TMessage> : HandlerInvoker<TMessage>
where TMessage : class {
public NonDisposableHandlerInvoker(Func<IMessageHandler<TMessage>> factory)
: base(factory) {
}
public override void Handle(TMessage message) {
InstantiateHandler().Handle(message);
}
}
Besagtes Reflection Voodoo macht dann folgendes:
Ich spare mir hier die zwar interessante, aber etwas längliche Implementierung dieses Prozesses, es sei denn, jemand interessiert es.
Man muss nun nur noch den CommandBus und den Dispatcher beim DI-Container registrieren und dann einmal die Handler beim Dispatcher registrieren.
Grüße,
Andre
P.S.: auch wenn ich hier von einem CommandBus spreche, geht es hier ganz allgemein um einen (synchronen) Nachrichtenbus.
Hi,
Wer sich an der Werbung stört, kann diese doch recht problemlos mit den passenden technischen Mittelchen ausblenden.
Nur am Rande zum Thema Bankkonte: ich rate dringend davon ab, eine Bankverbindung auf die Seite zu setzen - ich habe etliche Male eingezogene Beträge zurückholen und mich mit den geschädigten Anbietern herumschlagen müssen, weil irgendwelche Spaßvögel meinten, die Daten missbrauchen zu müssen.
Macht also lieber einen Paypal-Button.
Grüße,
Andre
a) Da der Auftrag sich nicht selber speichern darf
Naja, "nicht darf" ist immer so dogmatisch. Es geht nur darum, dass sich der Auftrag nicht automatisch in das Repository eintragen sollte, da nicht jeder Auftrag unbedingt "verewigt" werden muss. Und wenn du eine gesonderte "Save" Methode anbietest, bist du ja schon irgendwo wieder bei Active Record.
Wir haben weder MVC noch eine nachrichtenorientierte Architektur, sondern eine verteilte Anwendung ähnlich
> , da paßt ein "AuftragsService" ziemlich gut rein.
Ja, so ein Service-Layer ist wohl sinnvoll. Man muss nur immer aufpassen, dass man nicht zu viel Funktionalität in den SL statt in die Domain-Objekte packt, sonst gibt es das sogenannte Anämische Domain Modell. Meine Faustregel: der Service-Layer enthält nur die Logik, die die Kompetenzen eines einzelnen Objekts übersteigt. Er sucht sich die benötigten Objekte und Services zusammen und delegiert dann nur noch zwischen ihnen. Ich vermeide es übrigens auch Repositories in Domain Objekte zu injizieren.
b) Ich habe die Factory weggelassen...denn wenn es ok ist, Domainobjekte auch direkt per "new" zu erzeugen, dann brauche ich die Factory eigentlich auch nicht.
Genau richtig!
this.artikelFactory.Create würde aber nichts anders machen als this.artikelRepo.Get nochmal zu kapseln, und das erscheint mir hier unnötig.
Oder übersehe ich irgendwas?
Naja, eine Factory würde normalerweise kein Repository enthalten. Factory und Repository kümmern ich um verschiedene Zeitpunkte im Leben eines Objektes: die Factory erzeugt ein Objekt, das Repository enthält alle "lebenden" Objekte bis zu ihren "Tod" (zumindest gaukelt es das dem Benutzer vor - in Wirklichkeit sind viele der Objekte in einer DB auf Eis gelegt).
d) Du hast geschrieben daß Du Domainobjekte zwar mit "new" oder per Factory erzeugst, aber gleichzeitig trotzdem noch per Interface abstrahierst.
Ich finde es immer problematisch, solche Dinge zu pauschalisieren. Man sollte nicht gedankenlos wie ein wahnsinniger abstrahieren, nur weil es geht. Andererseits heißt eine der wichtigsten OO-Grundsätze: "Program to an interface, not to an implementation" Siehe hier.
Gleichzeitig erhöht die Einführung von Interfaces die Komplexität des Systems. Man muss also abwägen, wo der größte Vorteil liegt.
Demzufolge benutze ich im AuftragService und im IArtikelRepository einen IArtikel, aber das konkrete ArtikelRepository gibt nicht das Interface, sondern direkt den Artikel zurück. So programmiere ich schon überall gegen Interfaces, aber im Repository benutze ich die konkrete Klasse weil intern eh immer ein Artikel geladen wird...von einem Artikel wird es auch immer nur die eine Ausprägung geben (und nicht wie z.B. in dem Ninject-Beispiel ISword --> Gladius UND Katana).
Im Repository solltest du logischerweise gegen die Typen programmieren, den du von NHibernate zurückbekommst. Normalerweise also die konkreten Typen, Interfaces wenn du sie als Proxies verwendest.
e) Analog Deinem eigenen Vorgehen:
...hat der Container in meinem Beispiel also nur die eine Aufgabe, dem AuftragService die konkreten Repositories zu injizieren, und sonst gar nichts.
Exakt.
Nach manchen Artikeln die ich gelesen habe hatte ich den Eindruck als sei der Container DAS zentrale Element der Applikation, über das sämtliche benutzten Objekte erzeugt werden, aber das war dann wohl auch ein bißchen übertrieben.
Wahrscheinlich erklärt das die "Phobie" mancher DI-Gegner
Wenn der AuftragsService noch mehr Methoden hat brauchen die vielleicht noch andere Repositories, das würde noch ein paar mehr Parameter bedeuten.
Gleichzeitig werden ArtikelRepository und KundeRepository in den anderen Methoden vielleicht gar nicht gebraucht.
Wie Peter bereits sagt, ist das ein Zeichen dafür, dass das SRP missachtet wurde.
Mittlerweile bin ich von diesem starren Servicelayer ("KundenService") weggegangen und arbeite mit Commands und Commandhandlern:
public class ErstelleAuftragCommand {
public string KundenNr{get;set;}
public Position[] Positionen {get;set;}
public class Position {
public string ArtikelNr{get;set;}
public int Anzahl{get;set;}
}
}
public class ErstelleAuftragCommndHandler {
private IKundenRepository kundenRepo;
private IArtikelRepository artikelRepo;
private IAuftragRepository auftragRepo;
public void Handle(ErstelleAuftragCommand cmd){
var kunde = kundenRepo.Get(cmd.KundenNr);
var positionen = cmd.Positionen.Select(pos=>{
var artikel = artikelRepo.Get(position.ArtikelNr);
return new AuftragsPosition(artikel, pos.Anzahl);
});
var auftrag = new Auftrag(kunde, positionen);
auftragRepo.Save(auftrag);
}
}
... irgendwo in der UI oder im Controller o.ä.
class EineUIKlasse {
ICommandBus bus;
public void BenutzerMöchteAuftragErstellen(){
bus.Send(new ErstelleAuftragCommand (...));
...
}
Ich mag diesen Ansatz: keine "Riesenservices" mehr, die CommandHandlers sind kleine Klassen, sie erfüllen einen einzelnen Zweck und jedes Command ist eine Super Boundary (Transaktion, Unit Of Work).
Grüße,
Andre
Danke für deine Antwort. Ich werde also wohl die Invarianten verwenden. Der IL rewriter scheint bei automatischen Properties übrigens relativ clever zu sein.
Aus
internal class Stream
{
...
public Type Type
{
get;set;
}
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(Type != null);
}
}
Macht er (sinngemäß):
internal class Stream
{
..
public Type Type
{
get {
Contracts.Ensure(Contracts.Result<Type>() != null);
return type;
}
set {
Contracts.Require(value != null);
type = value;
}
}
...
}
Das heißt, anstatt alle Invarianten am Ende des getters und setters zu überprüfen, wird aus den Invarianten passende Pre- und Postconditions für die Property erzeugt, da bei den automatischen Properties eh nur ein Backingfield betroffen ist.
Grüße,
Andre
Hi,
Ich spiele gerade ein bisschen mit Code Contracts herum und habe ein kleines Problem mit der statischen Analyse. Gegeben folgende Klasse:
internal class Stream
{
private Type type;
public Stream(Type type)
{
Contract.Requires(type != null);
this.type = type;
}
public Type Type
{
get
{
Contract.Ensures(Contract.Result<Type>() != null);
return type;
}
set
{
Contract.Requires(value != null);
type = value;
}
}
}
Meinem Verständnis nach kann type nie null werden. Trotzdem beschwert sich die statische Codeanalyse bei "return type":
CodeContracts: ensures unproven: Contract.Result<Type>() != null
Ich kann das ganze natürlich "reparieren", indem ich eine entsprechende Invariante hinzufüge:
internal class Stream
{
...
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(type != null);
}
}
Damit kann die Postcondition dann statisch verifiziert werden.
Kann mich jemand erhellen, warum der Analyzer das ohne Invariante nicht verifizieren kann? Ist das ein grundsätzliches Problem des Analyzers oder by-design, oder übersehe ich etwas im Code?
Grüße,
Andre
Du hast in deinem Beispiel _Article _abstrahiert. Aus _Article _wird dann ein IArticle. Welchen Vorteil hat das? Keinen! Ich sehe zumindest kenen Vorteil. _Article _bleibt in meinen Augen ein Model und daran ändert sich auch nichts.
Es gibt viele Gründe, warum man die Modellklassen abstrahieren möchte. Zum Beispiel, wenn man bestimmte Patterns anwenden möchte.
Zwei Beispiele:
NHibernate bietet Lazy Loading an, indem das Framework zur Laufzeit Proxyklassen für die einzelnen Modell-Klassen erzeugt, die erst dann auf die DB zugreifen, wenn es wirklich nötig ist. Das geht aber nur, wenn man entweder alle Methoden virtuell macht oder indem man stattdessen mit interfaces arbeitet. Letzteres umschifft einige lästige Probleme, wenn man mit Vererbungshierarchien arbeitet.
Ein anderes Beispiel wäre das Decorator Pattern.
Du siehst, dass man manchmal auch dann eventuell zu interfaces abstrahieren möchte, auch wenn es von der eigentlichen Geschäftslogik (Artikel) nur eine Implementierung gibt.
Model-Klassen verwendet man in der kompletten Anwendung, ohne diese mit DI/IoC zu injizieren. Es gibt auch keinen Grund, Model-Klassen zu injizieren. Genau so ist es auch mit der Kunden-Klasse.
Wenn du meinen Beitrag noch einmal in Ruhe liest, wirst du folgende Stelle finden:
Es ist allerdings in diesem konkreten Beispiel natürlich fraglich, ob ich überhaupt soweit gehen möchte und Artikel über einen DI-Container resolven möchte, oder anders: sollte ein Artikel (eine Domain Entity) ggf. benötigte Services überhaupt injiziert bekommen - DDD Puristen sagen nein (
> ), zumindest dann, wenn die Abhängigkeiten aus einer anderen logischen Schicht stammen (also z.B. aus der Infrastruktur).In der Praxis ist es bei mir tatsächlich so, dass ich Domain-Objekte (Artikel, Kunde, Auftrag) in den meisten Fällen gar nicht über einen DI-Container erzeugen lasse, manchmal aber über eine Fabrik, da sie die physische Erzeugung (new XYZ) und die ggf. komplexe Initialisierung kapselt.
Grüße,
Andre
Zwei Sachen fallen mir auf:
Mit extra Methode meinst du eine Factory-Methode, oder wie kann ich mir das vorstellen?
Nein, im Grunde eine Initialisierungsmethode.
Ich lehne mich hier einmal an das running example von Ninject an. Angenommen, ich habe ein interface ISword mit verschiedenen Implementierungen (Katana, Gladius), und ich weiß, dass jedes Schwert einen Namen besitzt, der für jede Instanz anders sein kann/soll. Das ist über den DI-Container nicht ohne weiteres zu konfigurieren.
interface ISword{}
class Katana :ISword {
public Katana(string name){...}
}
class Gladius : ISword {
public Gladius(string name){...}
}
Wenn ich das Schwert nun über den DI-Container erzeugen will, muss ich irgendwie den Parameter mitgeben. Beispiel Ninject:
var container = new StandardKernel();
container.Bind<ISword>().To<Gladius>();
...
var sword = container.Get<ISword>(new ConstructorArgument("name", "Excalibur"));
Das ganze ist jetzt wackelig, wenn eine andere Implementierung z.B. den Parameter nicht name sonder title nennt.
Wenn man aber weiß, dass der Name Teil eines jeden Schwertes ist, so wie du es sagst, dann schlage ich stattdessen so etwas for:
interface ISword {
void Initialize(string name);
}
class Gladius : ISword {
public Gladius(){
// irgendwas, aber kein Argument
}
}
class Katana : ISword {
public Katana() {
// irgendwas, aber kein Argument
}
}
Die Erstellung eines Schwertes erfolgt nun in 2 Schritten:
// 1. Instanziierung
var sword = container.Get<ISword>();
// 2. Initialisierung
sword.Initialize("Excalibur");
Das kann jetzt zur Laufzeit eigentlich nicht mehr überraschend fehlschlagen. Um zu garantieren, dass Initialize auch aufgerufen wird, würde ich hier eine Factory empfehlen:
public class SwordFactory {
Func<ISword> createSword;
public SwordFactory(Func<ISword> createSword){
this.createSword = createSword;
}
public ISword Create(string name){
var sword = createSword();
sword.Initialiez(name);
return sword;
}
}
(NB: ich injiziere hier ein Func<ISword> statt z.B. einen IKernel, um eine Abhängigkeit zwischen Factory und DI-Framework zu vermeiden, etwa wie hier für Autofac beschrieben).
Die Factory kann man dann leicht selbst wieder vom DI-Container erstellen lassen und etwa in einen Service verwenden:
class SomeService {
private SwordFactory factory;
public SomeService(SwordFactory factory){
this.factory = factory;
}
public void Foo(){
var sword = factory.Create("Excalibur");
sword.Attack();
}
}
Grüße,
Andre
Strings? Beim Resolve-Aufruf?
Zumindest bei einigen DI-Container (u.a. bei Ninject über ConstructorArgument und Castle Windsor über ein Dictionary) werden explizite Parameter als Schlüssel-Wert Paare (also Argumentname, Wert) übergeben.
Bei LightCore scheint die Benennung expliziter Argumente optional zu sein (steht zumindest so unter Advanced Features). Das führt aber zu anderen Problemen, z.B. wenn sich die Reihenfolge der Parameter ändert.
Natürlich können da Runtime-Fehler auftreten, aber das geht nicht anders.
Und auch ist es klar, das man damit ein bisschen Unabhängigkeit auf den konkreten Typen verliert. Allerdings muss das nicht tragisch sein, wenn man das Wissen hat, das alle erbenden Klassen, die selben Argumente erwarten.
Deshalb rate ich davon ab, Parameter auf diese Art zu übergeben. Wenn man weiß, dass jede Implementation einer Abstraktion diese extra Parameter benötigt, würde ich immer zu einer extra Methode raten, mit der diese Daten explizit typ- und refaktorisierungssicher übergeben werden.
Wenn das Datumsfeld optional ist, mach doch ein Nullable davon, also DateTime?. Dann geht datum.HasValue etc.
Hallo,
Und bei Loggern etc. habe ich dann auch schon oft Beispiele gesehen wo direkt beim Programmstart per Container.Resolve<ILogger> EINE Logger-Instanz erzeugt wird, und die wird dann während der ganzen Programmlaufzeit benutzt.
Ein interessantes Beispiel. Idealerweise "erzeugt" man gar keine Logger auf diese Weise - der Container erzeugt die Instanz(en) selber, wenn sie als Abhängigkeit benötigt werden. Bei den typischen Loggingframeworks (NLog, log4net) erzeugt man übrigens nicht nur eine Instanz, sondern viele verschiedene, typischerweise mit dem (Namen vom) Typ, der den Logger benötigt, ausgestattet, um dann bei der Ausgabe unterscheiden zu können, von welchem Logger die Ausgabe stammt. Das heißt, das gerade das Auflösen einer Logger Instanz gar nicht so trivial ist: der neu erzeugten Instanz muss der Name oder der Typ des abhängigen Objektes mitgegeben werden. Glücklicherweise hat ein DI-Container Zugriff auf diese Information.
Und so halte ich es persönlich auch im Allgemeinen: ein Objekt, dass mit dem DI-Container erzeugt wird, sollte bei der Erzeugung nur die Informationen benötigen, die der Container sich selbst zusammensuchen kann: also andere Abhängigkeiten, vorkonfigurierte Werte und Objekte aus dem Kontext der Objekterzeugung. Auch wenn die meisten DI-Container beim Aufruf von Resolve die manuelle Übergabe von Parametern zulassen, rate ich persönlich davon ab, da meist mit Strings gearbeitet wird, um den Parametern Werte zuzuweisen, was leicht zu Laufzeitfehlern führt und schlecht zu refaktorisieren ist.
Möchte man jedoch transiente Objekte erzeugen, die sowohl zu injizierende Abhängigkeiten haben als auch Werte benötigen, die der Container sich nicht selbst suchen kann, empfehle ich eine Kombination aus Fabrik und DI-Container. Eine Fabrik wird u.a. dann eingesetzt, wenn die Erzeugung einer validen Instanz eines Objektes nicht nur aus dem Aufruf des Konstruktors besteht. Die beschriebene Situation wäre so ein Fall:
interface IArticle {
void Initialize(string number, string name);
}
class Article : IArticle {
public Article(ISomeService serviceViaDI){...}
}
class ArticleFactory {
IContainer container;
public ArticleFactory(IContainer container){
this.container = container;
}
public IArticle Create(string number, string name){
var article = container.Resolve<IArticle>();
article.Initialize(number, name);
return article;
}
}
Die Factory kann man jetzt in die Klassen injizieren, die Instanzen von IArticle erstelle müssen.
Hier zeigt sich, dass ein DI-Container und eine Fabrik lediglich eine gemeinsame Schnittmenge besitzen: ein DI-Container ist nicht unbedingt eine vollständige Fabrik, da ihm wie oben beschrieben ggf. die Kenntnis fehlt, um vollständig initialisierte Objekte zu erstellen. Andererseits ist eine Fabrik keine Obermenge eines DI-Containers, da letzterer auch Lifecycle-Management übernimmt.
Es ist allerdings in diesem konkreten Beispiel natürlich fraglich, ob ich überhaupt soweit gehen möchte und Artikel über einen DI-Container resolven möchte, oder anders: sollte ein Artikel (eine Domain Entity) ggf. benötigte Services überhaupt injiziert bekommen - DDD Puristen sagen nein (inject services into domain entities), zumindest dann, wenn die Abhängigkeiten aus einer anderen logischen Schicht stammen (also z.B. aus der Infrastruktur).
In der Praxis ist es bei mir tatsächlich so, dass ich Domain-Objekte (Artikel, Kunde, Auftrag) in den meisten Fällen gar nicht über einen DI-Container erzeugen lasse, manchmal aber über eine Fabrik, da sie die physische Erzeugung (new XYZ) und die ggf. komplexe Initialisierung kapselt.
Aber vielleicht kommen wir mal konkreter zu deinen Fragen:
- Wohin überhaupt mit der Methode die den neuen Auftrag erzeugt?
a) In die Auftragsklasse selber? (sprich, es wäre der Konstruktor)
...
b) In eine separate Klasse, "AuftragsManager" o.ä.?
…
Ich weiß nicht, ob ich die Frage richtig verstanden habe, aber dein Gefühl ist mMn richtig: geh zunächst vom einfachsten Fall aus und nimm für solche Domain-Objekte new. Sollte die Erstellung eines Auftrages komplex sein, kann man sie in eine Fabrik auslagern.
var kunde = KundeAusDatenbank(„Lieschen Müller“);
var artikel = ArtikelAusDatenbank(„Banane“);
var auftrag = new Auftrag(kunde);
// Lieschen kauft 5 Bananen.
auftrag.PositionHinzufügen(artikel, 5);
- An welcher Stelle lade ich Artikel & Kunde aus der Datenbank? Ich mache es auf jeden Fall via Repository (NHibernate), aber wo genau?
Das lässt sich nicht pauschal beantworten und hat mit DI eigentlich wenig zu tun.
Beispiele:
In einer einfachen MVC Anwendung kannst du das z.B. im Controller machen. Etwa in ASP.NET MVC:
class OrderController : Controller {
// alle injiziert:
IOrderRepository orders;
IArticleRepository articles;
ICustomerRepository customers;
IUnitOfWorkProvider unitOfWorkProvider;
public ActionResult PlaceOrder(string customerNr, string articleNr, int quantity){
ICustomer customer = customers.Find(customerNr);
IArticle article = articles.Find(articleNr);
IOrder order = new Order(customer);
order.AddLine(article, quantity);
orders.Add(order);
unitOfWorkProvider.Current.Commit();
}
}
Um das ganze vom Controller zu lösen ist es aber ggf. sinnvoll, das in eine eigene Klasse zu schieben, also etwa in einen Servicelayer. Das erhöht Wiederverwendbarkeit und sorgt für kleinere, spezialisiertere Klassen (hilft dem Single Responsibility Principle).
In einer nachrichtenorientierten Architektur wird das im Nachrichtenhandler gemacht:
public OrderController : Controller {
IBus bus; // injiziert
public ActionResult PlaceOrder(string customerNr, string articleNr, int quantity){
bus.Send(new PlaceOrderCommand(customerNr, articleNr, quantity);
}
}
..
class PlaceOrderCommandHandler : CommandHandler<PlaceOrderCommand> {
// alle injiziert:
IOrderRepository orders;
IArticleRepository articles;
ICustomerRepository customers;
IUnitOfWorkProvider unitOfWorkProvider;
public void Handle(PlaceOrderCommand cmd){
...
}
}
Du siehst, es gibt kein zwingendes Patentrezept.
- Woher genau kriege ich die Repositories?
a) Erzeuge ich die einmalig beim Starten des Programms und halte sie im Speicher (genau wie ich das oben schon mit dem Logger geschrieben hatte: Container.Resolve<IKundeRepository>)?
Dann sind u.U. ziemlich viele Repositories ständig im Speicher, obwohl ich sie vielleicht nur ganz selten brauche.b) Lasse ich sie mir automatisch vom Container erzeugen, in dem Moment wo ich sie brauche?
Dann werden oft benötigte Repositories immer und immer wieder neu erzeugt.
Ich würde die Repositories i.d.R. An die benötigten Stellen injizieren lassen (siehe Beispiele oben). Der entsprechende Gültigkeitsbereich sollte im Container konfiguriert sein (z.B. pro HttpContext eine Instanz eines Typs). Der Container wird dafür sorgen, dass die Instanzen zur richtigen Zeit erzeugt und zerstört werden. Über das "immer und immer wieder neu" erzeugen würde ich mir zunächst keine Gedanken machen - in "normalen" Anwendungen ist das überhaupt kein Problem.
- Ich muß den frisch erzeugten Auftrag ja auch noch in der Datenbank speichern, ebenfalls per Repository.
Siehe die Beispiele oben. Der Auftrag sollte sich nicht selbst in das Repository speichern (außer natürlich, du verwendest das Active Record Pattern, was ich deinem Text nicht entnehmen konnte).
Um es zusammen zu fassen: nicht überall in der Anwendung, wo Objekte erzeugt werden, sollte generell der DI-Container zum Einsatz kommen. DI-Container sind super, um Infrastruktur und Services an die passende Stelle zu bekommen. Domain Entities und ähnliche transiente Objekte würde ich entweder einfach über new erstellen oder, wenn höhere Abstraktion angestrebt ist oder die Erzeugung komplex ist, über eine Factory.
Grüße,
Andre
Zunächst einmal gilt ja folgende Überlegung: wenn MoveNext() false zurückgibt, kann die gesamte Enumeration abgebrochen werden, also:
public static IEnumerable<T> SelectDistinctConsecutive<T>(this IEnumerable<T> items)
{
using (IEnumerator<T> enumerator = items.GetEnumerator())
{
bool furtherValues = enumerator.MoveNext();
while (furtherValues)
{
T lastValue = enumerator.Current;
yield return lastValue;
do
{
furtherValues = enumerator.MoveNext();
// neu:
if(!furtherValues)
{
yield break;
}
} while (enumerator.Current.Equals(lastValue));
}
}
}
Jetzt besteht das Problem, dass das erste while(furtherValues) ab dem zweiten Durchlauf nicht mehr false sein kann, da sonst die Enumeration bereits beendet worden wäre. Also probieren wir:
public static IEnumerable<T> SelectDistinctConsecutive<T>(this IEnumerable<T> items)
{
using (IEnumerator<T> enumerator = items.GetEnumerator())
{
bool furtherValues = enumerator.MoveNext();
while (true)
{
T lastValue = enumerator.Current;
yield return lastValue;
do
{
furtherValues = enumerator.MoveNext();
if(!furtherValues)
{
yield break;
}
} while (enumerator.Current.Equals(lastValue));
}
}
}
Das kann allerdings nicht mit leeren Enumerationen umgehen, also prüfen wir, dass zumindest ein Element verfügbar ist:
public static IEnumerable<T> SelectDistinctConsecutive<T>(this IEnumerable<T> items)
{
using (IEnumerator<T> enumerator = items.GetEnumerator())
{
bool furtherValues = enumerator.MoveNext();
if (furtherValues)
{
while (true)
{
T lastValue = enumerator.Current;
yield return lastValue;
do
{
furtherValues = enumerator.MoveNext();
if (!furtherValues)
{
yield break;
}
}
while (enumerator.Current.Equals(lastValue));
}
}
}
}
Jetzt läuft es wieder, aber die Verwendung der furtherValues Variable hat kaum noch Sinn, also wird sie geinlined:
public static IEnumerable<T> SelectDistinctConsecutive<T>(this IEnumerable<T> items)
{
using (IEnumerator<T> enumerator = items.GetEnumerator())
{
if (enumerator.MoveNext())
{
while (true)
{
T lastValue = enumerator.Current;
yield return lastValue;
do
{
if (!enumerator.MoveNext())
{
yield break;
}
}
while (enumerator.Current.Equals(lastValue));
}
}
}
}
Das ganze ist jetzt leider durch die doppelte Schleife stark geschachtelt, weswegen ich wieder auf meinen Vorschlag mit der einfachen Schleife verweise. Die Idee dahinter: entscheide pro Element, ob es zurückgegeben wird. Die Kondition für das erste Element ist der erfolgreiche Aufruf von MoveNext, die Kondition für alle weiteren ist die Ungleichheit zum Vorgänger. Prüfe nach jedem Element, ob es weitere gibt, ansonsten beende die Enumeration.
Hi,
IIRC exportiert das aber eben nur den Codestyle. So Sachen wie templates exportiert die Funktion nicht. Templates kannst du über den Template-Explorer exportieren.
Du kannst natürlich auch einfach alles sichern/übertragen, indem du AppData\Roaming\JetBrains\ReSharper... auf den Zielrechner kopierst.
Es gibt auch ein "Resharper Settings Manager" Plugin (http://rsm.codeplex.com/), das habe ich aber noch nicht getestet.
Grüße,
Andre
Hi,
Von einer anderen Anwendung? EnumChildWindows
MSDN: EnumChildWindows Function
Pinvoke: EnumChildWindows
Siehe auch [nochmal hilfe benötigt] bestimmtes fenster auf die maximalgröße resizen?
Grüße,
Andre
es werden zwei verschiedene Bedingungen geprüft. Einmal nur furtherValues und einmal furtherValues und die Gleichheit zweier aufeinanderfolgender Elemente.
Beide Prüfungen werden direkt nacheinander ausgeführt, falls furtherValues false wird oder sobald das aktuelle Element ungleich des vorherigen ist. In beiden Prüfungen wird der Wahrheitsgehalt von furtherValues überprüft, auch wenn sich dieser zwischen den beiden Prüfungen nicht ändern kann. Ich sage ja nicht, dass das dramatisch ist und ein System zur Laufzeit zu Fall bringt, es ist nur nicht optimal.
Hallo,
herbivores letzte Version ist tatsächlich ziemlich kurz. Einige Details sind aber nicht optimal: z.B. wird furtherValues bei jedem Schleifendurchlauf 2x hintereinander geprüft, obwohl sich der Wert dazwischen nicht ändern kann: einmal im letzten while(...) und direkt danach (falls die Werte ungleich sind) im ersten while(furtherValues). Auch finde ich die geschachtelte Schleife etwas kompliziert zu verstehen.
Die erste Version von Uwe81 finde ich sehr leicht zu verstehen, wobei das if auch den while-Block umschließen könnte, da die Bedingung im while nie wahr wird, wenn das erste MoveNext() false zurückgibt.
Man kann natürlich auch die Schachtelung reduzieren und dadurch die verschiedenen Fälle (leere Enumeration, erstes Element, Rest) auch noch explizit in der Struktur wiedergeben:
public static IEnumerable<T> DistinctConsecutive<T>(this IEnumerable<T> items)
{
using (var enumerator = items.GetEnumerator())
{
// leere Enumeration abhandeln
if (!enumerator.MoveNext())
{
yield break;
}
// erstes Element abhandeln
var previous = enumerator.Current;
yield return previous;
// Rest
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (!current.Equals(previous))
{
yield return current;
previous = current;
}
}
}
}
Nicht, dass das notgedrungen besser ist als die Version von Uwe81, es wird nur einiges expliziter gemacht.
Und hier noch eine Version, die NICHT das erste Element speziell behandelt (zumindest nicht explizit):
public static IEnumerable<T> DistinctConsecutive<T>(this IEnumerable<T> items)
{
using (var enumerator = items.GetEnumerator())
{
var returnCurrent = enumerator.MoveNext();
var lastReturnedValue = default(T);
while(true)
{
if (returnCurrent)
{
lastReturnedValue = enumerator.Current;
yield return lastReturnedValue;
}
if (!enumerator.MoveNext())
{
yield break;
}
returnCurrent = !lastReturnedValue.Equals(enumerator.Current);
}
}
}
Einziger Wermutstropfen hier: MoveNext wird für leere Enumerationen 2x hintereinander aufgerufen, beide mit false als Ergebnis. Das ist zwar erlaubt, aber nicht 100% elegant.
Aber wie gesagt, die erste Version von Uwe81 finde ich gut.
Grüße,
Andre
edit: aus do{}while(true) while(true){} gemacht.
Kann das sein, dass du da.Fill (= Daten laden) machen möchtest statt da.Update (= Daten in DB zurückschreiben)?
[edit]Ah, sorry, du möchtest aktualisieren schreibst du. Hast du denn ein InsertCommand angegeben? Lädst du vorher die Daten in das Dataset, damit es das Schema kennt?[/edit]
Es gibt keinen Namespace System.Data.SQLite.Linq, ganz einfach. Nur weil die Assembly so heißt, muss es keinen gleichnamigen Namespace geben. Schau dir mein Beispiel ein, du benötigst lediglich den System.Data.Linq Namespace für die Linq-Abfragen.
Ich habe es gerade mal getestet, läuft einwandfrei. Dabei habe ich nur das hier befolgt: http://kubasik.net/blog/2009/02/09/using-linq-to-sqlite-providers-updated/
Also, Provider installieren, Datenverbindung anlegen, Model erzeugen und wie gewohnt verwenden:
namespace ConsoleApplication3
{
using System;
using System.Linq;
internal class Program
{
private static void Main(string[] args)
{
using (var context = new SQLiteTestConnection())
{
var users = from user in context.Users
where user.Name == "Andre"
select user;
foreach (var user in users)
{
Console.Out.WriteLine("users.ID = {0}", user.ID);
}
}
}
}
}
Hat von euch einer ne Idee oder am besten sogar eine kleine(detailllierte) Anleitung?
Hallo und willkommen,
Wenn du uns jetzt noch eine detaillierte Fehlerbeschreibung mitteilst, könnten wir dir vielleicht helfen. Sortiere noch einmal deine Gedanken und lies dir folgendes durch: [Hinweis] Wie poste ich richtig?
Grüße,
Andre
Hallo,
Ich verwende Extension Methods z.B. manchmal, um triviale Überladungen von interface-Methoden an einen zentralen Ort zu implementieren.
Beispiel:
interface IWidgetRenderContext {
void RenderWidgetZone(string name);
}
// extension:
static class WidgetRenderContextExtensions{
public static void RenderWidgetZone(this IWidgetRenderContext context, CommonZone zone){
context.RenderWidgetZone(zone.ToString());
}
}
So muss nicht jede Implementierung von IWidgetRenderContext die Überladung implementieren (DRY lässt grüssen), eine Basisklasse ist auch nicht nötig. Natürlich mache ich das nicht pauschal und nur wie gesagt bei trivialen Fällen, in denen jede Implementierung gleich aussähe. Glücklicherweise lässt sich diese Extension-Method später bei Bedarf relativ leicht refaktorisieren zu einer "echten" Methode des Interface, falls das mal nötig und/oder sinnvoll werden sollte.
Grüße,
Andre
Hallo,
Du kannst mit C# bzw. .NET im Allgemeinen und der WebClient Klasse im Speziellen leicht Webseiten herunterladen und dann mit verschiedenen Mitteln, wie z.B. Regulären Ausdrücken untersuchen, etwa nach Links zu anderen Seiten der selben Site (google macht - abgesehen von einer ggf. zur Verfügung gestellten Sitemap - nicht viel anderes).
Eine "Auflistung von Unterverzeichnissen" ist so eine Sache. Inwieweit du etwas zu sehen bekommst, das einem Dateisystem ähnelt, ist Sache des Webservers. In vielen Fällen entspricht die Hierarchie der URL nicht einer Hierarchie eines Dateisystems (d.h. http://www.amazon.de/gp/product/1430228865 verweist vermutlich nicht auf das Verzeichnis gp/product/1430228865), weshalb der Begriff "Unterverzeichnis" für dich als Client vermutlich irrelevant ist.
Abgesehen von den technischen Punkten solltest du auch an rechtliche Aspekte denken. Zuerst einmal ist es ggf. problematisch, andere Server automatisiert anzusprechen (Trespass to Chattels). Zum anderen muss geklärt werden, ob die durch das Crawlen gewonnenen Daten überhaupt irgendwie verwendet werden dürfen (vermutlich nicht).
Ohne einen Anwalt im Nacken und ohne Rücksprache der Betreiber der Seiten würde ich sagen: lass es.
Grüße,
Andre
Hallo,
Das Problem mit TargetSite kann ich gerade nicht so recht reproduzieren. Du solltest allerdings bedenken, dass Methoden auch geinlined werden können und daher sowohl StackTrace als auch TargetSite ggf. scheinbar falsche Informationen enthalten.
Grüße,
Andre
Hallo,
Ein Anonymer Delegat bzw ein "normaler" Lambdaausdruck (also keine Expression) wird tatsächlich nicht interpretiert, sondern sind lediglich syntaktischer Zucker für normalen Code, den man auch selber schreiben könnte.
Welcher Code genau generiert wird, hängt davon ab, ob der Delegat auf Kontextvariablen zugreift oder nicht (also ob der Delegat eine Closure besitzt).
Func<string> foo = () => "Hallo Welt";
foo verwendet keine Kontextvariablen, der Compiler kann das also umsetzen zu etwas wie:
public static string fooImplementierung(){
return "Hallo Welt";
}
...
Func<string> foo = fooImplementierung;
In "echt" sieht fooImplementierung z.B. so aus (Reflector sei Dank):
[CompilerGenerated]
private static string <Main>b__0()
{
return "Hallo, Welt";
}
Wenn man allerdings Kontextvariablen verwendet erzwingt man die Erstellung einer Closureklasse, die den Kontext speichert:
public static Func<int> Foo(int x)
{
return () => x;
}
Hier wird eine Closure-Klasse erstellt, die den Wert von x bei der Erzeugung des Lambdaausdrucks speichert:
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public int x;
// Methods
public int <Foo>b__0()
{
return this.x;
}
}
Foo sieht dann etwa so aus:
public static Func<int> Foo(int x)
{
var closure = new <>c__DisplayClass1();
closure.x = x;
return closure.<Foo>b__0;
}
Grüße,
Andre
Hi,
Die Methode bekommst du auf jeden Fall schon mal über Exception.TargetSite.
Grüße,
Andre
Bist du dir da sicher?
Ja.
class Program
{
public static void Main(string[] args)
{
Print(2);
Print(3);
}
private static Func<int> func;
public static void Print(int i)
{
if (func == null)
{
func = () => i;
}
Console.WriteLine(string.Format("i: {0}, func(): {1}", i, func()));
}
}
i: 2, func(): 2
i: 3, func(): 2
Hm, da sind aber schon ein paar schräge Sachen drin. Hier einige Beispiele:
Der Code enthält diverse Syntaxfehler. Hast du ihn mal bei dir kompiliert?
foreach (string key in m_Properties.Keys)
if (key == name)
bExists = true;
m_Properties ist eine Hashtable, warum nicht einfach
bExists = m_Properties.ContainsKey(name);
Überhaupt machst du Dinge, die dir die Hashtable bereits abnimmt.
if(!m_Properties.Remove(name))
{
throw new Exception("Property " + name + " does not exist!");
}
GetProperty wird zu:
string result;
if(!m_Properties.TryGetValue(name, out result))
{
throw new Exception("Property " + name + " does not exist!");
}
return result;
SetPropertyValue ist ein einfaches
m_Properties[name] = value;
Und so weiter...
3. Es ist eine ganz schlechte Idee, Exceptions zu verschlucken.
Warum muss ich eine Property erst hinzufügen, bevor ich ihren Wert setzen kann?
Du hast sehr viel Duplikation in deinem Code, das ist nicht empfehlenswert.
Ich bin ein Freak: ich möchte gerne Properites namens "<>foobar" speichern. Deine Klasse streikt hierbei und hilft mir auch nicht, den Fehler zu erkennen.
Weg mit den ganzen sinnlosen Kommentaren!
// Creates xml document // <-- vollkommen überflüssig
XmlDocument xmlDoc = new XmlDocument();
Dein Link bezieht sich auf das Entity Framework bzw. Linq to SQL. Das EF benötigt Expressions, um die verwendeten Properties etc. identifizieren zu können. Dieser Reflection-lastige Prozess kann mit CompiledQuery einmal ausgeführt werden. Das wiederholte ausführen der Query ist dann logischerweise schneller.
Expression.Compile hingegen erstellt zur Laufzeit Code, der dem Ausdruck entspricht, der durch die Expression repräsentiert wird. Entsprechend sollte der Unterschied zwischen einem anonymen delegaten und der kompilierten Expression minimal sein: mal wird der Code offline erzeugt (quasi von csc.exe), mal zur Laufzeit.
Ich habe einen kleinen Benchmark gebastelt, der dein Szenario in etwa nachstellt:
using System;
using System.Diagnostics;
using System.Linq.Expressions;
internal class Program
{
public static void Main(string[] args)
{
KompilierteExpression(true);
AnonymerDelegat(true);
KompilierteExpression(true);
AnonymerDelegat(true);
}
private static void KompilierteExpression(bool valueToReturn)
{
Expression<Func<Version, bool>> expr = v => valueToReturn;
Func<Version, bool> predicate = expr.Compile();
RunCases(predicate, "Kompilierte expression:");
}
private static void AnonymerDelegat(bool valueToReturn)
{
Func<Version, bool> predicate = v => valueToReturn;
RunCases(predicate, "Anonymer delegat:");
}
private static void RunCases(Func<Version, bool> delegateFromExpression, string name)
{
Console.WriteLine(name);
for (var i = 0; i < 10; ++i)
{
ExecuteTestCase(delegateFromExpression);
}
}
private static void ExecuteTestCase(Func<Version, bool> predicate)
{
const int numberOfPredicateCalls = 100000000;
var version = new Version(1, 2, 3, 4);
var sw = new Stopwatch();
var result = true;
sw.Start();
for (var i = 0; i < numberOfPredicateCalls; ++i)
{
result = predicate(version) && result;
}
sw.Stop();
Console.WriteLine("Zeit in ms: {0}", sw.ElapsedMilliseconds);
Console.WriteLine(result);
}
}
Auf meinem Rechner ist die Version mit einem anonymen Delegaten etwa 10% schneller als die kompilierte Expression.
Hallo,
Du kannst das entweder clientseitig oder serverseitig lösen.
Beispiel für clientseitig:
Sende den kurze Text und den kompletten Text zum Browser und zeige zunächst nur (mittels CSS und Javascript) die kurze Version an. Bei einem Click auf das Pluszeichen tauschst du dann den kurzen mit dem langen Text aus. Das geht z.B. mit jQuery und toggle() sehr leicht. Statt einen langen und einen kurzen Text zu senden kannst du ggf. das Kürzen auch mittels Javascript vornehmen.
Beispiel für Serverseitig:
Sende zunächst nur den kurzen Text. Mach aus dem Pluszeichen ein Linkbutton. Bei einem Klick auf den Button tauschst du (serverseitig) den kurze Text durch einen langen Text aus.
Beispiel für eine Kombination aus Client und Server (AJAX):
Sende zunächst nur den kurzen Text. Bei einem Click auf das Pluszeichen setzt du einen AJAX-Request an den Server ab, der dir den langen Text sendet. Tausche dann den kurzen Text durch den langen Text aus.
Grüße,
Andre
Hallo,
Mal so ganz allgemein: wofür benötigst du überhaupt die Expression? Warum nicht einfach einen delegaten erstellen:
filterMyClassBySubset = myObj => myObj.SubsetID == subsetID;
Das dürfte exakt gleich schnell sein (minus dem Overhead von .Compile()).
Desweiteren ist es nicht sinnvoll an dieser Stelle den delegaten zu cachen, da du auch den Parameter mit cachest. Rufst du SelectBySubset erst mit z.B. 2, dann mit 3 auf, erhälst du beim zweiten Mal die gleichen Ergebnisse wie beim ersten Mal.
Intern erstellt der Compiler (entweder der C# Compiler oder Expression.Compile) übrigens in etwa(!) folgenden Code:
// der Compiler erstellt eine Klasse, die die Contextvariablen speichert:
class Closure{
int subsetId;
public Closure(int subsetId) {
this.subsetId =subssetId;
}
// Methode, die dem Code des Lambdaausdrucks entspricht:
public bool Execute(MyClass myObj){
return myObj.SubsetID == subsetID;
}
}
// aus
// filterMyClassBySubset = myObj => myObj.SubsetID == subsetID;
// wird dann:
filterMyClassBySubset = new Func<MyClass, bool>( new Closure(subsetID).Execute);
Was willst du eigentlich genau erreichen?
Grüße,
Andre
Hallo,
Ich glaube, es geht ihm darum, HTML in der Anwendung bearbeiten zu können, etwa wie mit dem (F)CKEditor für Javascript.
Grüße,
Andre
Hi,
Das Zauberwort heißt AJAX (http://de.wikipedia.org/wiki/Ajax_%28Programmierung%29). Für AJAX unter ASP.NET gibt es dazu Anlaufstellen z.B. unter
http://www.asp.net/ajax
http://msdn.microsoft.com/de-de/library/bb386522.aspx
http://msdn.microsoft.com/de-de/library/ee341002.aspx
Ich persönlich setze für AJAX normalerweise jQuery oder Mootools ein. Die ASP.NET Ajax Controls (etwa das UpdatePanel) sind zwar relativ leicht zu bedienen, aber alles andere als schlank.
Grüße,
Andre
Ich benutze ganz gern CamStudio. Die Screencasts auf aloker.blip.tv habe ich damit z.B. gemacht. Ich bin mit der Bildqualität eigentlich sehr zufrieden.
Das Problem ist tatsächlich, dass verschiedene Konzepte in einen Topf geworfen werden.
Um es also mal auf einen Nenner zu bringen:
Inversion Of Control bedeutet ganz allgemein, dass der Kontrollfluss in der Beziehung zweier oder mehrerer Objekte umgedreht wird. Wikipedia gibt einige gute Beispiele, z.B. das Observer Pattern: beim Observer-Pattern bestimmt nicht das observierte Objekt, welche anderen Objekte über Änderungen informiert werden. Stattdessen melden sich die Observer beim observierten Objekt an und werden dann vom observierten Objekt informiert. Da events sehr änlich arbeiten sind sie ebenfalls bereits ein Mittel der Kontrollflussumkehr.
Dependency Injection ist eine Art von IoC: statt einem Objekt zu gestatten seine Abhängigkeiten selbständig zu suchen werden ihm die Abhängigkeiten von außen mitgeteilt. Das hat NICHTS mit einem Container zu tun:
public class TwitterAccount{
string userName;
string password;
ITwitterApi api;
public TwitterAccount(ITwitterApi api){
this.api = api;
}
...
public void SendTweet(string message){
api.SendTweet(userName, password, message);
}
}
Hier wird die Abhängigkeit (eine ITwitterApi) an den Konstruktor übergeben -> Dependency Injection!
IoC bzw DI Container sind lediglich "schlaue" Fabriken, die Objekte erstellen und dabei gleichzeitig die Abhängigkeiten der erstellten Objekte erkennen, auflösen und injizieren können.
Ob DI und DI Container "Standard" sind, kann ich nicht beurteilen. Die Java-Welt ist tendenziell weiter, was die Verwendung von Patterns angeht, vermutlich ist es hier nicht anders. Das Problem ist natürlich, dass viele Firmen Brownfield-Anwendungen warten müssen, da schmeißt man nicht unbedingt das komplette Design um (zumindest nicht ohne eine solide Basis an automatisierten Tests). Wenn ich so in diversen Maillistings lese, habe ich aber subjektiv den Eindruck, dass sich im Bereich Testbarkeit, Unittests, DI, automatisierte Builds usw. deutlich etwas tut.
Aber letztendlich ist es fast egal, ob es "Standard" ist. Die Vorzüge von DI sind offensichtlich, wer etwas anderes behauptet, hat das Problem und die Lösung nicht verstanden. Ihnen sei das hier zur Lektüre gereicht.
DI Container wiederum nehmen sehr viel Arbeit ab, die man so oder so erledigen muss, und führen zu mMn einfacherem, wartbarerem Code, da man die Objekterstellung fast vollständig ausblenden kann. Man muss sie nicht einsetzen. Ich habe allerdings noch kein Projekt erlebt, bei dem der Einsatz eines DI Containers mehr geschadet als genutzt hat. Es ist ja auch nicht so, dass der Einsatz eines solchen Containers andere Entscheidungen beschneiden würde.
Alles andere, XML Konfiguration, austauschbare Implementierungen, usw. sind nette Dreingaben, die man nutzen kann, aber nicht muss. Und wie gesagt kann man problemlos konkrete Klassen über einen DI Container erstellen lassen, man wird nicht zur Interface-itis gezwungen (wobei man natürlich je nach Situation schon die gebührende Abstraktion verwenden soll, um das System offen zu halten (Open-Closed-Principle) - aber das lässt sich auch noch später hinein-refaktorisieren).
Grüße,
Andre
Wie man in der Dokumentation sieht, ist Convert.DBNull vom Typ Object. In der Dokumentation zu ?: steht, dass die beiden Ergebnistypen entweder gleich sein müssen, oder einer in den anderen implizit castbar sein muss. Das ist bei string und object der Fall (string lässt sich implizit zu object casten), nicht aber bei DBNull und string.
In meinem Beispiel würde also auch folgendes reichen:
txtPR4.Text == string.Empty ? (object)DBNull.Value : txtPR4.Text
oder
txtPR4.Text == string.Empty ? DBNull.Value : (object)txtPR4.Text
Ob die CPU-Frequenz sich ändert, sollte für QueryPerformanceFrequency irrelevant sein, denn:
The frequency cannot change while the system is running.
Quelle: QueryPerformanceFrequency Function
Das Problem sind nicht die variablen CPU-Frequenzen sondern wie schon erwähnt v.a. die Anwesenheit mehrerer Kerne.
Hier noch einige Tipps zum Thema Timing: Game Timing and Multicore Processors
Grüße,
Andre
Probier mal
txtPR4.Text == string.Empty ? (object)DBNull.Value : (object)txtPR4.Text
Das sollte gehen, da Value ein object erwartet.
Grüße,
Andre
Hi und willkommen im Forum,
Siehe:
[FAQ] Warum blockiert mein GUI?
[FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke)
Und:
[Hinweis] Wie poste ich richtig? 1.1
Grüße,
Andre
Nein. Angenommen, ich Instanziere GenericClass<BClass>, dann stünde in der Add Methode:
public BClass Add()
{
BClass x = new AClass();
return x;
}
Und das kann nicht gehen!
Grüße,
Andre
P.S.:
Um es anschaulich zu machen:
class Apfel : Obst{}
class Birne : Obst{}
Birne b = new Apfel(); // Fehler!