Dies hier ist schon wieder eine wunderbare Idee aus Jimmy Nilssons Applying Domain-Driven Design and Patterns (das Buch scheint bis zur letzten Seite super Tipps zu liefern
) und zwar von Ingemar Lundberg.
Was ist eigentlich die Aufgabe einer Webseite: irgendwelche Controls mit Text zu füllen. Was dieser Text beinhaltet, dass wird von verschiedenen Funktionen entschieden. (Wie er ausgegeben wird, interessiert nicht.) Die Hauptaufgabe also bei der testgetriebenen Entwicklung von Webforms ist, diese Funktionalitäten zu ermitteln und zu implementieren. So bekommt man eine Webanwendung, bei der die Hauptbereiche getestet sind und nur die eigentliche Html-Ausgabe ungetestet bleibt. Außerdem wird auf dieser Art sichergestellt, dass die View sonst nichts tut.
Nehmen wir ein einfaches Beispiel: das Füllen eines Warenkorbs. Es stehen 3 Produkte zur Auswahl und der Käufer darf in seinen Warenkorb maximal 3 stellen. Um etwas Logik dabei zu haben, wird festgelegt, dass von ein Produkt nur maximal 2-mal gewählt werden darf. Wenn diese Bedingung erfüllt ist, soll das Produkt nicht mehr auswählbar sein. Gleiches gilt, wenn im Korb bereits 3 Produkte sind, kein Produkt darf mehr auswählbar sein.

Das Auswählen eines Produktes passiert z.B. mit einem OnClick-Event auf dem Link. Aus der Sicht der Funktionalität ist das nicht wichtig, hauptsache das Event wird ausgelöst.
Was tut also ein Modell um eine View zu steuern: nachdem es sichergestellt hat, dass alle Controls leer sind, lädt es die Daten mit irgendeiner Repository (nennen wir sie IDeposit), gibt sie der View und veranlasst diese, die Daten zu rendern. Danach muss es die übermittelten Daten identifizieren können und, wenn es OK ist, muss es diese mit einer anderen Repository (die nennen wir IAcquisition) abspeichern. Mit diesem “ist OK” wird sichergestellt, dass die obigen Regeln eingehalten wurden, also dass nicht zu viele Produkte bzw. identische Produkte ausgewählt wurden. Danach muss die View die Daten wieder rendern.
Mit diesen Informationen können wir bereits das Produkt und die 2 Interfaces definieren, die wir hier als Blackbox betrachten:
namespace WebformMVP.Tests
{
//Wegen der Bedingung "nicht mehr als zwei vom selben Typ" muss eine Product-Klasse geben. Sonst würde auch ein string reichen
public class Product
{
public string Name;
public int Type;
public Product(string name, int type)
{
Name = name;
Type = type;
}
}
public interface IDeposit
{
IList<Product> Load();
}
public interface IAcquisition
{
void Add(Product product);
}
}
Jetzt ist endlich Zeit für den ersten Test. Wie ich schon am Anfang geschrieben habe, eine View muss einfach nur Text darstellen. Um die View simulieren zu können, wird sie von einem Interface abgeleitet, genauso wie die Testklasse, unsere Fakeview. Diese bekommt als Felder strings anstelle von Controls, die allerdings korrekt gefüllt werden müssen. Wir tun so als ob, wir abstrahieren die View auf das Minimum:
namespace WebformMVP.Tests
{
public interface IShoppingView
{
void AddSourceItem(string text, bool available);
}
[TestFixture]
public class Tests : IShoppingView
{
string m_sourcePanel;
string m_shoppingCartPanel;
[Test]
public void FillSourcePanel()
{
m_model.Fill();
m_model.Render();
Assert.That(m_sourcePanel, Is.EqualTo("Product 1 available; Product 2 available; Product 3 available; "));
}
}
}
So wird die Anwendung natürlich nicht mal kompiliert
, dazu brauchen wir noch ein paar Schritte.
Dadurch, dass die Testklasse von diesem Interface ableitet, sind wir in der Lage, die Methoden entsprechend überschreiben zu können. Dieser Trick nennt sich Implement Interfaces Explicitly. Gleichzeitig lassen wir die Klasse auch von IDeposit ableiten, um auch dessen Methode zu überschreiben:
namespace WebformMVP.Tests
{
[TestFixture]
public class Tests : IShoppingView, IDeposit
{
string m_sourcePanel;
string m_shoppingCart;
IList<Product> m_sources= new List<Product>{new Product("Product 1", 1), new Product("Product 2", 2), new Product("Product 3", 3)};
void IShoppingView.AddSourceItem(string text, bool available)
{
m_sourcePanel += text + (available ? " available;": string.Empty) + " ";
}
IList<Product> IDeposit.Load()
{
return m_sources;
}
[Test]
public void FillSourcePanel()
{
m_model.Fill();
m_model.Render();
Assert.That(m_sourcePanel, Is.EqualTo("Product 1 available; Product 2 available; Product 3 available; "));
}
}
}
Es funktioniert immer noch nicht, wir brauchen ja noch ein Modell.
namespace WebformMVP.Tests
{
public class ShoppingModel
{
public void Fill()
{
throw new NotImplementedException();
}
public void Render()
{
throw new NotImplementedException();
}
}
[TestFixture]
public class Tests : IShoppingView, IDeposit
{
...
private ShoppingModel m_model;
//Es muss sichergestellt werden, dass beim Laden der View alle Felder leer sind.
[SetUp]
public void Setup()
{
m_sourcePanel = string.Empty;
m_shoppingCart = string.Empty;
m_model= new ShoppingModel();
}
[Test]
public void FillSourcePanel()
{
m_model.Fill();
m_model.Render();
Assert.That(m_sourcePanel, Is.EqualTo("Product 1 available; Product 2 available; Product 3 available; "));
}
}
}
Ok, es kompieliert endlich! Aber wir gehen ja nach TDD vor, der Test ist wie gewünscht rot
. Die 2 Methoden Fill und Render sind noch nicht implementiert.
Was sollen die Methoden tun? Fill() sollte eine lokale Liste mit Hilfe der Deposit-Repository füllen und Render() soll diese Elemente in das SourcePanel-Feld der View schreiben. Also muss unser Modell eine Liste, das IDeposit-Interface und das IShoppingVew als neue Member bekommen. Letzteren werden natürlich injectet (s. Dependency Inversion):
public class ShoppingModel
{
IDeposit m_deposit;
IList<Product> m_products;
[NonSerialized]IShoppingView m_view;
public ShoppingModel(IDeposit deposit)
{
m_deposit = deposit;
}
public void SetView( IShoppingView view )
{
m_view = view;
}
public void Fill()
{
m_products = m_deposit.Load();
}
public void Render()
{
foreach( Product product in m_products )
{
m_view.AddSourceItem( product.Name, true );
}
}
}
[TestFixture]
public class ShoppingCartTests:IShoppingView,IDeposit
{
...
private ShoppingModel m_model;
[SetUp]
public void Setup()
{
m_model = new ShoppingModel(this);
m_model.SetView( this );
m_sourcePanel = string.Empty;
m_cartPanel = string.Empty;
}
[Test]
public void FillSourcePanel()
{
m_model.Fill();
m_model.Render();
Assert.That( m_sourcePanel, Is.EqualTo( "Product 1 available; Product 2 available; Product 3 available; " ) );
}
...
}
Der Test ist grün! Jetzt ist sicher klar wie es weitergeht und ich will den Artikel nicht noch länger machen. Hier sind also die nächsten Tests und die Implementierung dazu:
[TestFixture]
public class ShoppingCartTests:IShoppingView,IDeposit
{
private string m_sourcePanel;
private string m_cartPanel;
private IList<Product> m_products = new List<Product> { new Product( "Product 1", 1 ), new Product( "Product 2", 2 ), new Product( "Product 3", 3 ) };
private ShoppingModel m_model;
[SetUp]
public void Setup()
{
m_model = new ShoppingModel(this, new Cart());
m_model.SetView( this );
m_sourcePanel = string.Empty;
m_cartPanel = string.Empty;
}
...
[Test]
public void AddAnItem()
{
m_model.Fill();
m_model.AddAt( 0 );
m_model.Render();
Assert.That( m_sourcePanel, Is.EqualTo( "Product 1 available; Product 2 available; Product 3 available; " ) );
Assert.That( m_cartPanel, Is.EqualTo( "Product 1 " ) );
}
[Test]
public void AddTwoItemsOfAKind()
{
m_model.Fill();
m_model.AddAt( 0 );
m_model.AddAt( 0 );
m_model.Render();
Assert.That( m_sourcePanel, Is.EqualTo( "Product 1 Product 2 available; Product 3 available; " ) );
Assert.That( m_cartPanel, Is.EqualTo( "Product 1 Product 1 " ) );
}
[Test]
public void AddThreeDifferentItems()
{
m_model.Fill();
m_model.AddAt( 0 );
m_model.AddAt( 2 );
m_model.AddAt( 1 );
m_model.Render();
Assert.That( m_sourcePanel, Is.EqualTo( "Product 1 Product 2 Product 3 " ) );
Assert.That( m_cartPanel, Is.EqualTo( "Product 1 Product 3 Product 2 " ) );
}
...
void IShoppingView.AddCartItem( string text )
{
m_cartPanel += text + " ";
}
}
public class ShoppingModel
{
IDeposit m_deposit;
IList<Product> m_products;
ICart m_cart;
[NonSerialized] IShoppingView m_view;
public ShoppingModel(IDeposit deposit, ICart cart)
{
m_deposit = deposit;
m_cart = cart;
m_view = view;
m_products = new List<Product>();
}
public void SetView( IShoppingView view )
{
m_view = view;
}
public void Fill()
{
m_products = m_deposit.Load();
}
public void Render()
{
foreach( Product product in m_products )
{
m_view.AddSourceItem( product.Name, m_cart.IsOkToAdd(product) );
}
foreach( Product product in m_cart.List )
{
m_view.AddCartItem( product.Name );
}
}
public void AddAt( int index )
{
var product = m_products[index];
m_cart.Add( product );
}
}
public interface ICart
{
bool IsOkToAdd( Product product );
void Add( Product product );
IList<Product> List { get; }
}
public class Cart :ICart{
private IList<Product> m_cartItems = new List<Product>();
public bool IsOkToAdd( Product product )
{
return m_cartItems.Where( a => a.Type == product.Type ).Count() < 2 && m_cartItems.Count < 3;
}
public void Add( Product product )
{
m_cartItems.Add( product );
}
public IList<Product> List
{
get { return m_cartItems; }
}
}
Die einzige größere Änderung zum ersten Test ist das neue ICart-Objekt. Da es hier um mehr als es eine Liste geht (irgendwo muss ja die Logik der maximal 2 gleichen Produkte pro Warenkorb errechnet werden), habe ich dafür das Interface und die Klasse definiert.
Jetzt sind wir fast fertig. Es muss lediglich die abstrahierte Umgebung in eine Webanwendung nachgebaut werden. Das heißt, wir implementieren die Methoden Page_Load(), Pre_Render() und AddAt_Click() und die Methoden des Interface IShoppingView. Um die Kontrolle zu behalten, löschen wir den Designer und schalten den ViewState aus (deswegen mag ich diesen Ingemar so sehr
).
//default.aspx
<%@ Page Language="C#" AutoEventWireup="true" EnableViewState="false" CodeBehind="Default.aspx.cs" Inherits="ShoppingCart.Web._Default" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Shopping Cart</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel runat="server" ID="srcPanel"></asp:Panel>
<asp:Panel runat="server" ID="cartPanel"></asp:Panel>
</div>
</form>
</body>
</html>
//default.aspx.cs
using System;
using System.Collections.Generic;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace ShoppingCart.Web
{
public class _Default : System.Web.UI.Page, IShoppingView
{
protected ShoppingModel model;
protected HtmlTable srcTable, cartTable;
protected Panel srcPanel, cartPanel;
protected void Page_Load( object sender, EventArgs e )
{
if( !IsPostBack )
{
model = new ShoppingModel(new FakeDeposit(),new Cart());
//Speichern, hier in Session aber sonst natürlich mit einer Repository
Session["ShoppingModel"] = model;
model.Fill();
}
else
{
model = (ShoppingModel)Session["ShoppingModel"];
}
model.SetView( this );
ModelRender();
}
protected void Page_PreRender()
{
srcPanel.Controls.Clear();
cartPanel.Controls.Clear();
ModelRender();
}
private void ModelRender()
{
srcTable = new HtmlTable();
srcPanel.Controls.Add( srcTable );
srcTable.Width = "50%";
srcTable.Border = 1;
cartTable = new HtmlTable();
cartPanel.Controls.Add( cartTable );
cartTable.Width = "50%";
cartTable.Border = 1;
cartTable.BgColor = "#cccccc";
model.Render();
}
public void AddSourceItem( string text, bool available )
{
int index = srcTable.Rows.Count;
HtmlTableRow tr = new HtmlTableRow();
HtmlTableCell tc = new HtmlTableCell { InnerText = text };
if( available )
{
LinkButton lb = new LinkButton();
tc.Controls.Add( lb );
lb.ID = index.ToString();
lb.Text = ">>";
lb.Click += AddAt_Click;
}
tr.Cells.Add( tc );
srcTable.Rows.Add( tr );
}
private void AddAt_Click( object sender, EventArgs e )
{
model.AddAt( Convert.ToInt32( ((LinkButton)sender).ID ) );
}
public void AddCartItem( string text )
{
HtmlTableCell tc = new HtmlTableCell { InnerText = text };
HtmlTableRow tr = new HtmlTableRow();
tr.Cells.Add( tc );
cartTable.Rows.Add( tr );
}
}
internal class FakeDeposit :IDeposit
{
public IList<Product> Load()
{
return new List<Product> { new Product( "Product 1", 1 ), new Product( "Product 2", 2 ), new Product( "Product 3", 3 ) };
}
}
}
Fertig. Ich muss eingestehen, als ich das Beispiel aus dem Buch nachprogrammiert habe, war ich wirklich überrascht, wie alles geklappt hat, obwohl ich während des Testens keine Webseite angesprochen habe. Die Wahrheit ist, ich habe noch nie nach dem MVP-Pattern entwickelt, aber eine Webseite so aufzusetzen ist genial! Hoch lebe die Abstraktion!
Ich lade hier das Projekt hoch, vielleicht glaubt es mir jemand nicht
In diesem Tipp geht es um die Verwendung des XmlnsDefinition Attributs.
Zusammenfassung
Um eigene Controls in XAML Dateien verwenden zu können, kann man eine Namespace-Definition der Form xmlns:xyz="clr-namespace:MyNamespace;assembly=MyAssembly" verwenden. Es ist jedoch einfacher eigene Controls mit Hilfe des XmlnsDefinition Attributs zu verwalten.
Beschreibung
Controls, die nicht aus dem Default Namespace http://schemas.microsoft.com/winfx/2006/xaml/presentation stammen, benötigen eine XML Namespace-Definition, um in XAML Code verwendet werden zu können. Eine solche Definition bildet einen .NET CLR Namespace auf einen XML Namespace Präfix ab. In Silverlight 2 musste man dafür im Root-Element der XAML Datei ein Attribut mit folgendem Aufbau angeben:
<UserControl xmlns:xyz="clr-namespace:MyNamespace" ...>
- oder -
<UserControl xmlns:xyz="clr-namespace:MyNamespace;assembly=MyAssembly" ...>
Über den Präfix xyz kann man dann Controls aus dem angegebenen CLR Namespace und der entsprechenden Assembly verwenden. Wächst nun ein Projekt im Laufe der Entwicklung immer weiter an, verteilen sich die selbst geschriebenen Custom- und insbesondere User-Controls meist auf mehrere Namespaces und Assemblies. Dies führt zum einen zu vielen verschiedenen Namespace-Definitionen pro XAML Datei in der sie verwendet werden und zum anderen wird auch das Refactoring immer mühsamer.
Seit Silverlight 3 gibt es nun das aus WPF bekannte Attribut XmlnsDefinition, welches auf Assembly-Ebene das Mapping von XML Namespaces auf CLR Namespaces regelt.
[assembly: XmlnsDefinition("http://mycompany.com/xaml", "MyNamespace.Controls")]
[assembly: XmlnsDefinition("http://mycompany.com/xaml", "MyNamespace.Controls.Xyz")]
Dieses Attribut kann mehrfach vorkommen und steht typischerweise in der Datei AssemblyInfo.cs. Es stellt sicher, dass alle Controls aus allen aufgeführten Namespaces in einer XAML Datei über ein und dieselbe Url angesprochen werden können. Auch wenn die Controls aus verschiedenen Assemblies stammen, die jeweils ihre eigenen XmlnsDefinition Attribute haben, reicht jetzt eine einzige Namespace-Definition:
<UserControl xmlns:abc="http://mycompany.com/xaml" ...>
<abc:MyControl ...>
Die Url selbst kann beliebig gewählt werden und hat keine weitere Bedeutung, außer dass sie eindeutig sein muss.
Über das Attribut XmlnsPrefix kann man optional noch festlegen, welches der Default Namespace-Prefix sein soll, den ein XAML Designer-Tool automatisch vergibt, wenn das erste Control per Drag & Drop zu einem User Control hinzugefügt wird.
[assembly: XmlnsPrefix("http://mycompany.com/xaml", "abc")]
Microsoft verwendet beispielsweise in Silverlight 4 den Präfix toolkit für Controls aus dem Silverilght Toolkit.
Ausnahmen
Theoretisch könnte man so vollständig auf die Schreibweise mit
clr-namespace verzichten, gäbe es nicht eine Ausnahme. Wenn man in einer XAML Datei ein Control verwenden möchte, welches aus der selben Assembly stammt wie die XAML Datei, muss für dieses Control eine
clr-namespace basierte Definition verwendet werden. Oder anders ausgedrückt: Die Url basierte Namespace Definition funktioniert nur für Controls, die aus referenzierten Assemblies stammen. Controls aus der eigenen Assembly werden leider nicht gefunden und ein Fehler angezeigt. Meiner Ansicht nach ist das ein Bug im XAML Parser, da ich keinen Grund sehe, warum dies "by Design" so sein sollte.
In mehr als zwei Jahren Einsatz von Silverlight in ganz unterschiedlichen Projekten habe ich jede Menge Erfahrungen gesammelt und hatte viele große und kleine Probleme zu lösen. Wenn ich davon erzähle, höre ich oft, dass auch andere einen nicht unerheblichen Teil ihrer Zeit mit dem "Herausfinden von Dingen" verbringen. Daraus ist nun die Idee entstanden, die interessantesten Erfahrungen aufzuschreiben und hier regelmäßig als Silverlight Tipp der Woche zu veröffentlichen.
Der Aufbau der Tipps wird so gestaltet, dass es für den Leser möglichst einfach ist zu entscheiden, ob die jeweilige Information für ihn von Interesse ist oder nicht. Daher gibt es jeweils am Anfang eine nur wenige Sätze lange Zusammenfassung, in der die wesentliche Aussage des Tipps beschrieben wird. Danach kommen dann weitere Erläuterungen, sowie ggf. Hintergrundinformationen und Links.
Hier geht's zum ersten Tipp.
Die Sicherheit seiner Kunden und Partner ist für Microsoft ein vordringliches Anliegen. Um diese beim sicheren Betreiben Ihrer IT-Infrastruktur bzw. ihres Computers optimal zu unterstützen, wird Microsoft voraussichtlich am nächsten Montag, den 2. August 2010, gegen 19:00 Uhr Mitteleuropäische Zeit eine außerplanmäßige Sicherheitsaktualisierung (Out of Band Release to address Microsoft Security Advisory 2286198 welches diese Microsoft-Sicherheitsempfehlung (2286198) betrifft) veröffentlichen. Diese wird, soweit mein Kenntnisstand, alle unterstützten Versionen von Microsoft Windows betreffen.
Solche außerplanmäßigen Sicherheitsupdates werden von Microsoft nur aus wichtigen Gründen veröffentlicht und sollten daher umgehend installiert werden. Diese Vorabinformation soll Ihnen helfen, die außerplanmäßigen Sicherheitsaktualisierungen auch in Ihrem Unternehmen bzw. auf Ihrem Privat Computer möglichst rasch zum Einsatz zu bringen... [... mehr Details in diesem ausführlichen Blogeintrag auf Giza-Blog.de]
This post is powered by
www.Giza-Blog.de |
Visit: MSDN Online |
Follow MSDN Online on
Twitter | Follow Kay
Giza on Twitter
Daily News on MSDN:
MSDN
Aktuell
©
Copyright 2006-2010 Kay Giza. All rights reserved.
Legal
Brian Harry hat auf seinem Blog alle Details zu diesem Meilenstein beschrieben, da wir ja auch mit TFS 2010 den Workflow auf WF4 umgestellt haben :-)
Der wichtigste Punkt dabei ist, dass wir das GatedCheck-In Feature in TFS 2010 entsprechend weiterentwickelt haben, so dass nun auch mehrere Checkins gleichzeitig via Gated Checkin eingecheckt werden können. Dies war notwendig, da der Visual Studio EndtoEnd Build halt nicht in 10min erledigt ist und somit das ganze nicht skaliert. Smart ist, dass im Fehlerfall alle Checkins automatisch einzeln behandelt werden und nur der Checkin, der den Fehler erzeugt zurückgewiesen wird. Zudem hat Brian angekündigt, dass diese Features in der nächsten TFS Version enthalten sein werden :-)
Chris
Blogengine.NET und die Theme Problematik...(read more)
Entwickelt man eine ASP.NET (MVC) Website mit dem ASP.NET Development Server und hat eine SQL Server Datenbank im Einsatz, funktioniert zunächst alles problemlos.
Verwendet man jedoch für die Entwicklung einen “echten” IIS 7.x, also z.B. unter Windows 7, erhält man bei Datenbankzugriffen die folgende Exception:

Die Lösung ist ziemlich einfach – man muss die Application Pool Identity “IIS APPPOOL\mvcapp” zu den Usern in der Datenbank zuweisen.
Leider klappt dies mit dem SQL Server Management Studio nicht.
Man kann zwar den Namen der Applicationpool Identity einfügen

und durch “Check Names” validieren lassen

Nach dem Klick auf “OK”, erhält man jedoch die Fehlermeldung, dass der User nicht gefunden wurde:

Abhilfe schafft die Generierung des Logins via Script:
CREATE LOGIN [IIS APPPOOL\mypool] FROM WINDOWS WITH DEFAULT_DATABASE=[master] USE [mydatabase] CREATE USER [IIS APPPOOL\mypool] FOR LOGIN [IIS APPPOOL\mypool]
Danach kann man im SQL Server Management Studio die Berechtigungen zuweisen, so dass die Website mit der Application Pool Identity funktioniert.
Wie mit Hilfe der in Visual Studio 2010 integrierten Testtools auf einfache Weise Last- und Stresstests durchgeführt werden können und welche Berichtsarten für die anschließenden Auswertungen zur Verfügung stehen, erläutert jetzt ein zwölf Seiten umfassendes Whitepaper, das im Visual Studio News Blog vorgestellt wird. Das Dokument ist im XPS- und PDF-Format kostenlos abrufbar.
Die erste Vorschau der Version 3 des ASP.NET Model-View-Controller-Musters auf Basis von .NET Framework 4 und Visual Studio 2010 steht interessierten Entwicklern jetzt zum kostenfreien Download zur Verfügung. Eine Parallelinstallation zu Version 2 ist problemlos möglich, auch die kostenfreie Web Developer Edition von Visual Studio 2010 wird voll unterstützt. Technische Details zu der Vorschauversion bieten über unsere Zielseite hinaus Scott Guthrie und Phil Haack in ihren Blogs.
Gute Nachricht für MSDN Subscriber: Microsoft hat die Azure-Leistungen für „Visual Studio Premium mit MSDN“ und „Visual Studio Ultimate mit MSDN“ erheblich erweitert. Bezieher dieser Abonnements erhalten bei Aktivierung der Azure-Leistungen jetzt ganze 16 Monate lang die so genannten Windows Azure Platform Benefits statt wie bisher acht Monate – und das mit vollen Produktivnutzungsrechten! Alle Details im Visual Studio News Blog von MSDN Deutschland.
Nach Abschluss der Microsoft-Veranstaltungsreihe “Web Camp”, die Anfang Juni auch in Deutschland Station gemacht hat, sind jetzt die kompletten Seminarunterlagen abrufbar – Präsentationen, Demos, Labs und vieles mehr. Das „Web Camps Training Kit“ ist im Microsoft Downloadcenter in englischsprachiger Fassung herunterladbar.
Zwei neue Software Development Kits für Entwickler sind ab sofort im deutschsprachigen Microsoft Downloadcenter abrufbar: das Expression Blend 4 SDK für Silverlight sowie das Expression Blend 4 SDK für .NET 4.0. Beide Pakete enthalten weitervertreibbare Komponenten, die bei der Erstellung von Expression Blend 4-Anwendungen benötigt werden.
Aus der Expression-Reihe stehen in deutschsprachigen Versionen außerdem neu zum herunterladen bereit: der Expression Encoder 4 sowie Testversionen von Expression Web 4 und Expression Studio 4 Ultimate.
Eine kleine Übersicht aller Shortcuts für Visual Studio 2010!
http://www.microsoft.com/downloads/details.aspx?FamilyID=92CED922-D505-457A-8C9C-84036160639F&displaylang=en
Eine kleine Übersicht aller Shortcuts für Visual Studio 2010!
http://www.microsoft.com/downloads/details.aspx?FamilyID=92CED922-D505-457A-8C9C-84036160639F&displaylang=en
http://downtocode.net ist momentan in Wartung und wird erst gegen Nachmittag wieder live gehen....(read more)
Ende letzten Jahres fand für 2 Tage in Wien der von Mario Meir-Huber und Norbert Eder (MVP) organisierte .Net Open Space statt. In diesen teils feucht fröhlichen Tagen wurde die Idee der .Net Open Space Pirates geboren. Die Grundidee ist eine Usergroup der etwas anderen Art. Es gibt einen Vortrag jedes Monat, dieser wird jedoch von den Teilnehmern vorher festgelegt und von Freiwilligen vorbereitet. Ein solcher Vortrag ist dann auch mehr eine Diskussionsrunde als ein moderierter Beitrag. Es gibt zwar einen Handlungsstrang der von den Vortragenden vorbereitet wurde. Dieser Handlungsstrang dient jedoch Aufgrund der Diskussion nur als Richtlinie. Der Vortrag wird von den Teilnehmern aktiv durch Diskussion erweitert. "Einsprüche" oder Unterbrechungen des Vortrages werden hierbei nicht als Boshaftigkeit, sondern vielmehr als Offenheit angesehen. Durch die zahlreichen Diskussionen lernt jeder Teilnehmer aktiv mit.
Wichtig für die Usergroup ist auch, das sich die Teilnehmer aktiv in Vorträgen einbringen. Dies betrifft nicht nur die Diskussion während eines Vortrages, sondern auch Vorträge selbst. Jeder Teilnehmer kann, wenn er möchte, einen Vortrag halten. Dies kann über technologische Themen gehen, welche während der Arbeit anfallen. Somit kann man anderen interessante Informationen weiter geben und man hat auch selbst einen Beitrag zur Community geleistet.
Essentiell für die Open Space Pirates ist auch das Socializing. So wird nach jedem Vortrag normalerweise noch essen bestellt. Oft sind die Teilnehmer noch Stunden beisammen gesessen und haben über verschiedenste Themen der Softwareentwicklung weiter diskutiert. Neben interessanten Dingen rund um die Softwareentwicklung lernt man somit auch neue Leute kennen.
Für alle die auf den Geschmack gekommen sind, ein kleiner Tipp:
Das nächste Treffen handelt über REST im .NET Framework und wird von Mario Meir-Huber vorbereitet. Genauere Details zum Treffen werden demnächst auf Codefest.at und dotnetopenspace.ning.com bekanntgegeben.
Vor kurzem ist die Preview1 des ASP.NET MVC 3. Die offensichtlichste Neuerung ist die Unterstützung für verschiedene View-Engines. In der Preview 1 gibt es dafür verschiedene Projektvorlagen, das wird aber später in den Konfigurationdialog, in dem unter anderem das Testprojekt ausgewählt wird, integriert.
Die View-Engine verwendet einen einfacheren Syntax um die Lesbarkeit zu verbessern. Details zu Razor sind hier auf scottgu’s blog zu finden. Ein Vergleich der beiden View-Engines ist hier zu finden.
Interessant ist, dass ein Projekt die Views aus beiden View Engines gleichzeitig verwenden kann. So ist es möglich, Razor-Views und ASPX-View beliebig zu mischen.
Ist eine View doppelt enthalten, also sowohl für Razor, als auch für ASPX, wird die ASPX View bevorzugt. Interessant hierbei ist, dass in meinem Fall die ContactView.cshtml die MasterPage der Index.aspx überschreibt.
Die .NET User Group Frankfurt hat seit kurzem ein neues Logo. Der Administrator und Entwickler der Web Site der User Group, Stephan Schneider, hat dieses wunderbare Logo neu entworfen
Ich war mal wieder auf meiner WinQual Seite um festzustellen ob es im aktuellen Release auffällige Crashes gibt.
Dabei viel mir ein Link ins Auge, der ein VeriSign ‘Microsoft Authenticode’ Class 3 Code Signing Digital Certificate für nur $99,– anbietet. Bei einem Normalpreis von $499,– ist das kein schlechter Preis!
Hier der Link auf die WinQual Seite:
http://winqual.microsoft.com/help/default.htm#obtaining_a_verisign_class_3_digital_id.htm
und hier der entsprechende Link direkt zu VeriSign:
https://securitycenter.verisign.com/celp/enroll/upsell?promo_code=THEDEAL99&application_locale=VRSN_US&originator=VeriSign:CELP&bundle_id=MSIECS002&enable_options=validity_1
Copyright © 2008 Martin Richter
Dieser Feed ist nur für den persönlichen, nicht gewerblichen Gebrauch bestimmt. Eine Verwendung dieses Feeds bzw. der hier veröffentlichten Beiträge auf anderen Webseiten bedarf der ausdrücklichen Genehmigung des Autors.
(Digital Fingerprint: bdafe67664ea5aacaab71f8c0a581adf)
Themenverwandte Beiträge:
Bei der Entwicklung von SharePoint 2010 Projekten kann man auf diverse Tokens zurückgreifen, die von MSBuild durch die entsprechenden “Live”-Werte ersetzt werden. Folgende Tokes stehen zur Verfügung $SharePoint.Project.FileName$ $SharePoint.Project.FileNameWithoutExtension...(read more)
Gestern habe ich über meinen ersten Eindruck von Event-Based Components (EBC) geschrieben. Mein Fazit war sehr positiv – mit dem einzigen Kritikpunkt, dass sich EBCs noch sehr ungewohnt anfühlen und bislang bewährte und etablierte Best Practices fehlen.
Worum geht es bei EBCs überhaupt? Prinzipiell hat Ralf Westphal dies bereits in seinem Blogeintrag EBCs – Der nächste Schritt der Komponentenorientierung? beschrieben. Doch da ich gestern in Reaktion auf meinen Blogeintrag gefragt wurde, ob ich Sinn und Zweck von EBCs noch einmal in meinen Worten erklären könnte, will ich dieser Bitte gerne nachkommen.
Das bisher favorisierte Verfahren zur komponentenorientierten Entwicklung von Software basiert im Wesentlichen auf der strikten Trennung von Implementierung und Kontrakt, so dass Komponenten austauschbar werden – sofern sie den gleichen Kontrakt erfüllen.
Da Komponenten in der Regel nur im Verbund in der Lage sind, eine gestellte Aufgabe zu bewältigen, müssen diese dabei irgendwie miteinander in Kontakt treten. Hierfür enthalten Komponenten Referenzen auf andere Komponenten, von deren Funktionalität sie abhängen. Diese Referenzen verweisen dabei stets nur auf die Kontrakte und nicht auf die konkrete Implementierung.
Um eine Abhängigkeit aufzulösen, muss dann zur Laufzeit entschieden werden, welche konkrete Komponente instanziiert werden soll – wofür üblicherweise die Idee eines Kernels eingesetzt wird, wie beispielsweise in Form eines Dependency Injection-Containers wie LightCore.
So weit, so gut.
Das Problem bei diesem Ansatz zur komponentenorientierten Entwicklung besteht nun darin, dass sich die Reduktion von Komponenten auf ihre Kontrakte zwar positiv auf ihre Austauschbarkeit auswirkt – nicht jedoch auf ihre Unabhängigkeit. Komponenten sind zwar unabhängig von der konkreten Implementierung einer anderen Komponente – aber funktional sind sie dennoch abhängig, nur eben über den Kontrakt.
Dass dies überhaupt ein Problem darstellt, zeigt die Tatsache, dass es nicht möglich ist, eine Komponente nur für sich und isoliert zu testen. In der Regel müssen für andere Komponenten zumindest Stubs, wenn nicht gar Mocks entwickelt und injiziert werden. Das ist aufwändig – zu aufwändig, könnte man mutmaßen.
Denn Komponenten werden häufig mit Lego-Bausteinen verglichen, die man einfach so aufeinander stecken kann – dem Kontrakt sei Dank – doch so wirklich will sich dieses Lego-Gefühl bei der herkömmlichen komponentenorientierten Entwicklung von Software nicht einstellen: Einem roten Lego-Baustein ist es nämlich egal, ob er für sich alleine genutzt wird, oder in Verbund mit einem grünem, einem gelben oder einem blauen.
Ralf nennt diese Eigenschaft topologieunabhängig, weil Lego-Bausteine eben nicht von ihrer Umgebung abhängen. Und genau diese Eigenschaft der Topologieunabhängigkeit trifft auf klassische Komponenten nicht zu: Sie sind abhängig von ihrer Umgebung, allein schon deshalb, weil sie andere Komponenten benötigen, um ihre volle Funktionalität entfalten zu können.
Wendet man sich von Lego-Bausteinen einem anderen Feld, das der IT ein wenig näher steht, zu, kann man problemlos die gravierenden und relevanten Unterschiede zur komponentenorientierten Softwareentwicklung erkennen: Dem Feld von Platinen und Chips.
Ein Chip verkörpert das EVA-Prinzip in Reinkultur: Er erhält Daten per Stromfluss als Eingabe, verarbeitet diese, und gibt Daten per Stromfluss als Ausgabe wieder nach außen. Einem Chip ist es dabei vollkommen gleich, woher die Eingabedaten stammen oder wohin die Ausgabedaten fließen.
Für die Funktionsweise eines Chips sind nun nur zwei Aspekte wichtig:
- Pins: Ein Chip verfügt über Ein- und Ausgabepins, über die er mit anderen Chips verdrahtet werden und mit diesen in Kontakt treten kann. Ob dies geschieht – und wenn ja, in welcher Form – beeinflusst die grundlegende Funktionsweise des Chips jedoch nicht.
- Stromfluss: Den einzigen gemeinsamen Nenner, den zwei Chips aufweisen müssen, damit sie auf funktionsfähige Art miteinander verdrahtet werden können, ist ein gemeinsames Verständnis des Stromflusses. Anders formuliert: Alle an einer Kommunikation beteiligten Chips müssen die gleiche “Sprache” verstehen.
EBCs schließlich sind ein Ansatz, genau dieses Konzept von topologieunabhängigen Komponenten, deren einzige Gemeinsamkeit eine gemeinsame “Sprache” ist, auf Software zu übertragen.
Die Idee dazu ist prinzipiell einfach: Das grundlegende Problem der funktionalen Kopplung von Komponenten basiert auf der Tatsache, dass gegenseitig Methoden aufgerufen werden müssen.
EBCs ziehen aus dieser Tatsache die einzig logische Konsequenz: Wenn diese Art der funktionalen Kopplung vermieden werden soll, dürfen Komponenten keine Methoden anderer Komponenten aufrufen. Statt dessen dürfen sie nur signalisieren, dass sie gerne Daten zur Weiterverarbeitung an andere Komponenten abgeben würden – ob darauf dann eine andere Komponente reagiert oder nicht, betrifft die ursprüngliche Komponente nicht mehr.
In einem Satz zusammengefasst bildet dies den Schlüssel zum Verständnis von EBCs:
Im Gegensatz zu klassischen Komponenten rufen EBCs keine Methoden anderer Komponenten auf, sondern stellen nur den Wunsch nach Weiterverarbeitung ihrer Daten in den Raum.
Damit gewinnt man Topologieunabhängigkeit, denn jede EBC ist von ihrer Umgebung entkoppelt und kann entweder für sich alleine oder transparent im Verbund mit anderen Komponenten genutzt werden. Dass sich die Funktionalität von EBCs damit perfekt per Unittest validieren lässt, liegt auf der Hand. Daher eignen sich EBCs ausgezeichnet auch für die Entwicklung mit 4-Step TDD.
Die einzige verbleibende Frage ist, wie dieses Schema umgesetzt werden kann. Die Antwort ist – nochmals – sehr einfach: Input-Pins entsprechen klassischen Methoden, Output-Pins werden als Events deklariert, woher auch der Name der EBCs rührt.
Passt die Signatur eines solchen Events einer Komponente zur Signatur einer Methode einer anderen Komponente, können beide per Eventbinding von außen miteinander verdrahtet werden – die Komponenten selbst bemerken dies nicht und agieren auch im Verbund ebenso wie sie es isoliert für sich täten.
Dadurch entsteht anders als bei klassischen Komponenten kein Codefluss von direkter Aktion und Reaktion, sondern ein Fluss von Daten: Jeweils eine Komponente verarbeitet Daten und reicht diese danach an eine oder mehrere andere Komponenten weiter.
Auch Rückgabewerte lassen sich auf diese Art realisieren: Statt aus der aufgerufenen Methode einen Rückgabewert zurückzuliefern, wird ein entsprechendes Event ausgelöst – auf das die ursprünglich “aufrufende” Komponente dann entweder reagieren kann oder nicht – je nach Belieben. Der “aufgerufenen” Komponente ist das gleich – sie löst unabhängig von ihrer Umwelt lediglich ihre Events aus.
Dies sind die grundlegenden Ideen von EBCs, im nächsten Blogeintrag wird es dann darum gehen, wie eine konkrete Implementierung einer solchen EBC aussehen und wie diese mit anderen EBCs verdrahtet werden kann.
Da ist es wieder. Der Compiler spuckt die Warnung CS0067 “The event ‘XXX’ is never used” aus. Das kann z.B. passieren, wenn das Interface ICommand implementiert wird, das Command aber immer ausführbar sein soll. Das Event CanExecuteChanged muss also nie gefeuert werden:
public class SomeCommand : ICommand
{
public void Execute(object parameter)
{
// do sth.
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
Bei obiger Imlementierung bekommt man beim Übersetzten die Compiler Warnung CS0067:
warning CS0067: The event ‘CommandDemo.SomeCommand.CanExecuteChanged’ is never used
Was einem als erstes (zumindest mir) einfällt die Warning zu “beheben”, ist sie mit einem #pragma Statement kurzzeitig aus- und wieder einzuschalten:
#pragma warning disable 0067
public event EventHandler CanExecuteChanged;
#pragma warning restore 0067
So richtig gut gefällt diese Lösung aber nicht. Bei Implementierungen, wo das Event später vielleicht doch einmal gefeuert wird, ist die Wahrscheinlichkeit ziemlich hoch, dass diese #pragma Statements vergessen werden und im Code verbleiben. Das ist nicht schön. Aber es gibt eine andere, (mittlerweile meiner Ansicht nach bessere) Lösung:
public event EventHandler CanExecuteChanged { add{} remove{} }
Durch das Hinzufügen der leeren Eventaccessors “add” und “remove” mache ich klar, dass dieser Event nicht verwendet wird. Da das Event sowieso nie gefeuert wird, ändert sich auch für den potentiellen Konsumenten des Events nichts. Wenn in einer späteren Phase dieser Event doch einmal ausgelöst werden soll, wird der Entwickler sofort mit einem Kompilerfehler darauf hingewiesen, das dies nicht möglich ist. Die leeren “add” und “remove” Accessoren müssen also entweder wieder entfernt oder entsprechend vollständig implementiert werden ==> der Code bleibt sauber.
Wie man sieht, muss man also nicht sofort zum #pragma greifen, wenn man mit der Warnung CS0067 konfrontiert wird. Das Hinzufügen der leeren Eventaccessoren bietet eine elegante und saubere Lösung mit dem Fall umzugehen.
Ich bin gerade 2 Wochen in Redmond zu Gast und arbeite im Marketing Team der DPE an kommenden Kampagnen im Herbst mit. Dabei stolpere ich über so manche Ressource die mir bis jetzt unbekannt war, oder auf Codefest bisher nicht erwähnt wurde.
Wer die Ideastorm Initiaitive von Dell kennt – das Prinzip gibts auch bei Microsoft, und zwar für Windows Azure: Das Product Team nimmt unter http://windowsazure.uservoice.com Feature Requests für die Weiterentwicklung von Azure entgegen. Wer heute schon mit Azure arbeitet kann hier mitwirken und diskutieren, oder einfach nur mitlesen was die Community wünscht.
Außerdem: Für alle die eine MSDN Subscription besitzen gibt es good news: Das 8 Monats Azure Intro Offer wurde auf 16 Monate erweitert!
Nicht fehlen darf eine eigene Fanpage rund um Azure in Facebook – Wer Facebook längst als Startseite für seine tägliche News-Dosis verwendet kann so einfach up2date bleiben. Das wird ergänzt durch: Ein eigener YouTube und Twitter Channel.
Lieber RSS? - das Azure Team Blog auf MSDN.
Have fun in the Cloud :)
Das Ermitteln einer View folgt dem Mechanismus “Von Spezialisierten zum Generalisierten”. Das folgende Bild zeigt die Reihenfolge in Kombination mit Areas.

Diese Reihenfolge gilt für den Lookup von sowohl Views als auch Partial Views. Das gilt für jedes (!) Lookup, auch innerhalb von Views (z.B. bei Html.RenderPartial()). Diesen Umstand kann man sich zu nutze machen um Polymorphie abzubilden. Gegeben ist folgendes Modell.
Damit bietet es sich an, eine Shared View für “Contact” zu erstellen. Aber was passiert mit den Eigenschaften, die nur in den spezialisierten Klassen “Person” und “Company” existieren. Die ContactView ist für die “Contact” Basisklasse typisiert.
An dieser Stelle hilft die Lookup-Reihenfolge. Benutzt man in der Shared View ein RenderPartial(), kann man spezialisierten Views in den Controllern definieren.
Das Ergebnis ist, dass im CompanyController zusätzlich die CompanyView “SpecialView” gerendert wird und im PersonController die PersonView “SpezialView”. Es wird also automatisch zuerst in den Views für den Controller nachgeschaut. Einziges Problem mit RenderPartial() ist eine Exception, wenn in einem dritten Controller die “SpezialView” nicht implementiert ist, die ContactView aber gerendert wird.
Zu bemerken ist abschließend noch, dass die SpezialViews auf die spezialisierten Klassen typisiert ist, also Person bzw. Company. Das MVC2 Framework übernimmt (vereinfacht dargestellt) die Umwandlung von Contact in Person bzw. Company.
Das Html-Helper Object des MVC2 Frameworks unterstützt kein Generieren von File-Input Steuerelementen. Hier ist ein Workaround für dieses Problem: Man überschreibt das Type-Attribut mit “file”.
<%: Html.TextBoxFor(model => model.File, new { size = "30", type = "file" }) %>
Der vollständige Code sieht folgendermaßen aus.
// Modell mit der Eigenschaft "File" für die hochgeladene Datei
public class UploadPartModel
{
[Required()]
public HttpPostedFileWrapper File { get; set; }
[Required()]
public string Name { get; set; }
[Required()]
public string Description { get; set; }
}
Das Formular wird mittels “Html.___For()” Methodne zusammengebaut. Nicht vergessen im Formular das “enctype”-Attribut mit dem Wert “multipart/form-data” zu setzen, sonst gibt es nicht serverseitig viel zu sehen.
<% Html.EnableClientValidation(); // used for client side validation with jQuery
using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
Html.ValidationSummary(true); %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Name) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Name, new { size = "30" })%>
<%: Html.ValidationMessageFor(model => model.Name) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Description)%>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Description, new { size = "30" })%>
<%: Html.ValidationMessageFor(model => model.Description)%>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.File) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.File, new { size = "30", type = "file" }) %>
<%: Html.ValidationMessageFor(model => model.File) %>
</div>
<p>
<input type="submit" value="Upload" />
</p>
</fieldset>
<% } %>
Die neuen Build-Workflows für den Team Foundation Server sind ein cooles Feature. Und wenn man Standard-Projekttypen verwendet funktioniert das auch alles super, da in den meisten Fällen Copy&Paste-Deployment ausreichend ist.
In meinem Anwendungsfall war es notwendig, am Ende des Builds eine Activity auszuführen, die auf einem Remote-Server (z.B. Integrationssystem) einen Prozess startet. Eine solche Activity habe ich im Standard nicht gefunden. Daher hab ich selbst eine geschrieben.
Eine eigene Activity zu erstellen ist gar nicht so schwer. Einen guten Einstieg geben die Posts von
Jim Lamb und
Ewald Hofman. Bei der Entwicklung der Activity hat sich die Projektaufteilung von Ewald als sehr praktisch erwiesen. Nur so war es mir möglich, die selbst erstellte Activity dem Workflow dann auch hinzuzufügen.
Um einen Prozess zu starten, sind folgende Informationen notwendig:
- Name oder IP-Adresse des Remote-Servers
- auszuführendes Command
- Credentials (abweichend vom TFS-Service-Account)
Daher erhält die Activity in Summe fünf Input-Argumente: Command, RemoteMachine, Domain, UserName und Password. Damit sieht der Rumpf der Actitvity wie folgt aus:
[BuildActivity(HostEnvironmentOption.All)]
public sealed class StartProcessOnRemoteMachine : CodeActivity
{
[RequiredArgument]
public InArgument<string> Command
{
get;
set;
}
[RequiredArgument]
public InArgument<string> RemoteMachine
{
get;
set;
}
[RequiredArgument]
public InArgument<string> Username
{
get;
set;
}
[RequiredArgument]
public InArgument<string> Password
{
get;
set;
}
[RequiredArgument]
public InArgument<string> Domain
{
get;
set;
}
}
Bei der Implementierung sollte man sich überlegen, so sensible Daten wie Credentials ggf. anders abzubilden als per Input-Parameter - für dieses Beispiel soll diese Lösung aber ausreichend sein, damit es nicht zu komplex wird. Die eigentliche Arbeit übernimmt dann die im folgenden dargestellte Methode
ExecuteProcessOnRemoteMachine, die per WMI einen Prozess auf einem anderen Rechner startet. Dazu ist es notwendig, die Assembly System.Management zu referenzieren.
private static void ExecuteProcessOnRemoteMachine(string remoteMachine, string username, string password, string domain, string commandLine)
{
ConnectionOptions connectionOptions = new ConnectionOptions();
connectionOptions.Authority = "ntlmdomain:" + domain;
connectionOptions.Username = username;
connectionOptions.Password = password;
connectionOptions.Authentication = AuthenticationLevel.Default;
connectionOptions.Impersonation = ImpersonationLevel.Impersonate;
connectionOptions.EnablePrivileges = true;
ManagementScope managementScope = new ManagementScope(string.Format(@"\\{0}\ROOT\CIMV2", remoteMachine), connectionOptions);
managementScope.Connect();
ManagementPath managementPath = new ManagementPath("Win32_Process");
ManagementClass processClass = new ManagementClass(managementScope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["CommandLine"] = commandLine;
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);
}
Der Rest ist trivial: In der Methode
Execute werden die Input-Parameter entgegengenommen und an die Methode
ExecuteProcessOnRemoteMachine übergeben.
protected override void Execute(CodeActivityContext context)
{
string startProcessCommand = context.GetValue(this.Command);
string remoteMachine = context.GetValue(this.RemoteMachine);
string username = context.GetValue(this.Username);
string password = context.GetValue(this.Password);
string domain = context.GetValue(this.Domain);
ExecuteProcessOnRemoteMachine(remoteMachine, username, password, domain, startProcessCommand);
}
Die Activity wird nun in den Workflow eingebunden und die notwendigen Daten bereitgestellt.

Am Ende des Builds werden nun die Dateien aus dem DropFolder genommen und mittels der Standard-Activity
CopyDirectory auf den Integrationsserver kopiert. Die neu erstellte Activity führt dann die Installation durch.
Vor ungefähr fünf Monaten hat Ralf Westphal in seinem Blog die Frage aufgeworfen, ob nachrichtenorientierte Komponenten den nächsten Schritt der Komponentenorientierung darstellen.
Als entsprechendes Architekturmodell hat die sogenannten Event-Based Components (EBC) vorgestellt: Ein einfaches Komponentenmodell mit einigen kompakten Regeln, dessen wesentliches Merkmal des Einsatz von Events als Kommunikationsmedium an Stelle klassischer Methodenaufrufe ist.
Im Prinzip ähnelt das Konzept der EBCs dem von Chips und Platinen: Ähnlich wie diese können auch EBCs zusammengesteckt werden und größere Komponenten bilden. Der Vorteil des Ganzen liegt in einer ausgesprochen geringen Kopplung – effektiv sind die einzelnen EBCs nicht mehr voneinander, sondern nur noch von gemeinsamen Typen abhängig.
Die funktionale Abhängigkeit ist aufgelöst. Die Vorteile liegen auf der Hand: EBCs sind einfacher zu instanziieren, einfach zu komponieren, einfacher zu isolieren und daher auch einfacher zu testen.
So weit, so gut. Die Frage ist, inwieweit dieses theoretisch interessante Konzept in der Praxis funktioniert.
In den vergangenen Tagen hatte ich die Gelegenheit, einen EBC-basierten Prototypen einer bestehenden Anwendung zu erstellen: Die zu erzielende Funktionalität war also vorgegeben, die einzige Aufgabe war also tatsächlich, das Konzept der EBCs in die Praxis umzusetzen.
Inzwischen ist dieser Prototyp so weit gediehen, dass er die ersten funktionalen Anforderungen der ursprünglichen Anwendung erfüllt. Zeit, im Rahmen einer kleinen Retrospektive zurückzublicken und ein erstes Fazit zu ziehen. Die aus meiner Sicht zwei wesentlichsten Aspekte sind:
- EBCs sind effizient: EBCs ersparen einem die Mühe, eine aufwändige Architektur zu entwickeln – sie implizieren eine kompakte Form. Diese kann in technischer Hinsicht auch leicht umgesetzt werden – auch oder erst recht mit TDD. Die geringe Kopplung wirkt sich hierfür enorm positiv aus. Die Erweiterbarkeit des gesamten Systems ist beeindruckend, und auch die Wart- und Evolvierbarkeit des Codes können sich sehen lassen. Noch mehr als von TDD alleine wird man durch den Einsatz von EBCs mit TDD gezwungen, sich zuvor fundierte Gedanken über das Design der Komponenten zu machen.
- EBCs sind ungewohnt: Der größte Makel von EBCs ist – für mich persönlich – derzeit ihre Andersartigkeit. Sie fühlen sich schlicht und ergreifend ungewohnt an, denn die Kommunikation zwischen den Komponenten folgt nicht mehr dem klassischen und zur Genüge gewohnten Aktion-Reaktion-Schema. Statt dessen modellieren EBCs einen Fluss von Daten durch die Anwendung. Die größte Herausforderung in den vergangenen Tagen war, gedanklich immer wieder zu diesem Fluss-basierten Modell zurückzukehren und sich zu überlegen, wie Funktionalität als Fluss implementiert werden kann.
Natürlich ist der zweite Punkt reine Gewohnheits- und Übungssache. Je öfter man EBCs modelliert, desto leichter gelingt dies. Der Einstieg in EBCs ähnelt daher dem Einstieg in TDD in gewissem Sinne – es ist gar nicht so sehr das rein technische Vorgehen, das kann kompakt und übersichtlich veranschaulicht werden, sondern es sind das fehlende Gefühl für EBCs und die fehlenden Best Practices.
Während das Gefühl nur jeder für sich entwickeln kann, indem er sich mit EBCs beschäftigt und die Arbeit mit ihnen ausprobiert, können Best Practices vermittelt werden – falls diese bereits bekannt sind. Hierzu ist es notwendig, einen Konsens oder zumindest verschiedene, fundierte und argumentativ belegte Meinungen zu finden.
Ralf hat zu EBCs seine Sicht der Dinge – ich habe meine. Aus diesem Grund werde ich in den kommenden Tagen viel über meine Erfahrungen und Erkenntnisse bezüglich EBCs schreiben.
Wenn ich eine andere Meinung vertrete oder EBCs anders nutze als Ralf, werde ich dies begründen – nicht, um Ralfs Position zu schwächen, sondern schlichtweg, um einen zweiten Standpunkt darzulegen.
Vielleicht ergeben sich in der Synergie aus Ralfs Sichtweise, meiner Sichtweise und der Sichtweise von allen anderen Verwendern von EBCs ja Best Practices, die es wert sind, weitergetragen zu werden.
EBCs an sich sind dies allemal wert – sie sind ein großartiges Konzept, das mir sehr gut gefällt. Der langfristige Nutzen wird sich noch beweisen müssen, aber so weit ich EBCs bislang beurteilen kann, sind sie definitiv ein wichtiger Schritt in die richtige Richtung für die moderne Softwareentwicklung.
Disclaimer: Ich bin kein Ajax-Entwickler. Als das Thema vor 5 Jahren auf den Tisch kam, hatte ich schon einige Jahre mit XmlHttpRequest im Internet Explorer gearbeitet, aber mich eigentlich schwerpunktmäßig immer auf die Serverseite konzentriert. Das hat sich auch bis heute noch nicht groß geändert - ich arbeite zwar mit jQuery, mootools und auch noch klassisch nativem JavaScript, aber meist wirklich nur für das Nötigste.
In normalen Formularen gehe ich was die Überprüfung der Eingaben angeht bislang recht klassisch vor - die Validierung findet im Service und damit auf der Serverseite statt. Sicherheit geht nunmal über alles. Entsprechend stiefmütterlich habe ich dann aber auch die clientseitige Validierung bisher behandelt.
Nun bin ich aber gestern zu einem Punkt gekommen, bei dem ich nur zwei Alternativen habe:
- Eine vernünftige Lösung mit Ajax und/oder einem Client-Validation-Framework bauen.
- Etwas serverseitiges und für den User schlecht benutzbares zusammenfrickeln.
Es geht um einen Dateiupload, genauer einen Multi-File-Upload, wie man ihn z.B. mit swfupload oder uploadify verwirklichen kann. Dieser Upload ist in ein normales Formular eingebettet, was einige Pflichtfelder besitzt. Wählt der Nutzer nun also Dateien aus und vergisst aber eines der Pflichtfelder auszufüllen und schickt das Formular ab - dann sind nach dem Roundtrip zum Server die ausgewählten Dateien wieder weg.
Keine gute Sache für den User. Also sollte der Workflow so aussehen:
- Formular ausfüllen
- Dateien auswählen
- Formular überprüfen
- Ggf. Eingaben korrigieren
- Bei erfolgreichem Ausfüllen Formular abschicken und Hauptdatensatz anlegen
- Anschließend die Dateien hochladen und dem Hauptdatensatz zuordnen
Dafür gibt es zwei brauchbare Wege (die mir einfallen):
Ajax
Validierung und Erzeugung des Hauptdatensatzes erfolgt per Ajax. Wenn dies geklappt hat, wird der Upload der ausgewählten Dateien angestoßen. Vorteil: die ID des Datensatzes steht hier vor dem Upload bereits zur Verfügung.
Client-Validation-Framework
Die Validierung erfolgt zusätzlich zur Serverseite, wo sie weiter obligatorisch bleibt, clientseitig, zum Beispiel mit dem jQuery-Validation-Plugin oder den Tools, die ASP.NET MVC dafür bereits mitbringt. Nachteile: doppelte Konfiguration (vernachlässigbar), im Zweifel verschieden aussehende Ausgaben der Fehler client-/serverseitig (vernachlässigbar), die ID des Hauptdatensatzes steht hier beim Dateiupload noch nicht zur Verfügung, da erst die Dateien hochgeladen müssen und dann der POST erfolgen kann.
Ein schwieriges Thema, bei dem wirklich hunderttausende Wege nach Rom führen. In Sachen Ajax stört mich der meist enorme Overhead. Ich habe relativ wenig Lust die Ausgabe der Fehler "zu synchronisieren".
Ein brauchbarer Weg erschien mir aber der von Jarrett Vance in seinem Post "Making an Ajax Form with jQuery in ASP.NET MVC" aufgezeigte. Vorteil: die Ausgabe bleibt exakt die gleiche, man muss sie nicht doppelt pflegen. Zudem braucht es auch keinen großen Aufwand auf der Client-Script-Seite. Nachteil: die Formulare müssen in PartialViews ausgelagert werden.
Ich habe es allerdings mal als Grundlage für eine Demo genommen und seinen Ansatz für meine Zwecke erweitert.
1: [HttpPost]
2: [ValidateAntiForgeryToken]
3: public ActionResult CreateTask(TaskViewModel model)
4: {
5:
6: // DEMO - Validierung wird im Service/BLL ausgeführt
7:
8: if(string.IsNullOrWhiteSpace(model.Name))
9: ModelState.AddModelError("Name", "Bitte geben Sie einen Namen an.");
10:
11: if(string.IsNullOrWhiteSpace(model.Text))
12: ModelState.AddModelError("Text", "Bitte geben Sie einen Text an.");
13:
14: if (ModelState.IsValid)
15: TempData["Success"] = "Task erfolgreich angelegt.";
16:
17: if(Request.IsAjaxRequest())
18: {
19: if(ModelState.IsValid)
20: return Json(new AjaxOperationResultInfo { Successfull = true, RedirectUrl = Url.Action("index") });
21: return PartialView("CreateTaskForm", model);
22: }
23:
24: if(ModelState.IsValid)
25: return RedirectToAction("index");
26: return View(model);
27:
28: }
Nur wenn es ein Ajax-Request ist, wird der PartialView zurückgegeben. Ansonsten, also in allen Fällen, in denen JavaScript im Browser deaktiviert ist, gibt es den normalen View zurück. Damit funktioniert der Spaß auch bei deaktiviertem JavaScript.
Nun ein Kompromiss:
Wenn ein Fehlerauftritt, wird der PartialView zurückgegeben. Wenn die Sache allerdings erfolgreich war, gebe ich ein JSON-Ergebnis zurück. Das ist zwar nicht astrein, erlaubt mir dann aber auf der Clientseite weitere Entscheidungen zu treffen. Im Beispiel übergebe ich die Ziel-URL, auf die nach erfolgreichem Verarbeiten des Formulars weitergeleitet werden soll. In meinem Praxisfall würde ich noch die ID des angelegten Datensatzes übergeben, damit diese beim Upload der Dateien gleich verwendet würden könnte.
Der JS-Code bleibt übersichtlich:
1: $(document).ready(function () {
2: $('#ajaxForm form').live('submit', function () {
3: $.post($(this).attr('action'), $(this).serialize(), function (result) {
4: if ($.isString(result)) {
5: $("#ajaxForm").replaceWith($(result));
6: }
7: else if (result["Successfull"]) {
8: window.location.href = result["RedirectUrl"];
9: }
10: else {
11: alert('Es ist ein Fehler aufgetreten. Bitte benachrichtigen Sie den Codemonkey.');
12: }
13: });
14: return false;
15: });
16: });
17:
18: jQuery.isString = function (o) {
19: return (typeof o === "string");
20: }
Die komplette Projektmappe habe ich mal angehangen.
Unterm Strich bleibt es ein scheinbar brauchbarer Weg, richtig glücklich bin ich damit aber noch nicht. Ich werde als nächstes mal jQuery Validation testen sowie die DataAnnotations wieder ausgraben, rein für die clientseitige-Validierung.
Downloads
| Kommentieren | © 2010 Thomas Bandt
Das .NET Framework 4 ist nun schon ein paar Tage alt und abgesehen von tollen neuen Features bringt es auch einige gewöhnungsbedüftige Sachverhalte mit sich. Einer davon ist die Änderung im Global Assembly Cache.
Wer bisher die Fusion-Ansicht des Global Assembly Cache verwendet hat und per Drag & Drop Assemblies installiert hat, wird bemerken, dass alle neuen Assemblies sich nicht mehr installieren lassen, obwohl keine Fehlermeldung erscheint. Installiert man die Assemblies hingegen per gacutil, so wird man feststellen, dass zwar eine Erfolgsmeldung kommt, aber unter C:\Windows\assembly keine Veränderung geschieht.
Woran liegt das? Mit dem .NET Framework 4 gab es einige Änderungen im Konzept des GAC. Hier ein kurzer Auszug aus der MSDN:
In .NET Framework 4.0, the GAC went through a few changes. The concept of placing assemblies into a global directory began in CLR v1.1. In case of .NET Framework 1.1 (which had CLR v1.1) and .NET Framework 2.0 (which had CLR 2.0), the GAC was split into two, one for each CLR. This avoided the leaking of assemblies across CLR versions. For example, if both .NET 1.1 and .NET 2.0 shared the same GAC, then a .NET 1.1 application, loading an assembly from this shared GAC, could get .NET 2.0 assemblies, thereby breaking the .NET 1.1 application.
The CLR version used for both .NET Framework 2.0 and .NET Framework 3.5 is CLR 2.0. As a result of this, there was no need in the previous two framework releases to split the GAC. The problem of breaking older (in this case, .NET 2.0) applications resurfaces in Net Framework 4.0 at which point CLR 4.0 released. Hence, to avoid interference issues between CLR 2.0 and CLR 4.0, the GAC is now split into private GACs for each runtime.
Zusammengefasst bedeutet das, dass der GAC nicht mehr für alle Assemblies C:\Windows\assembly ist, sondern dass dort alle Pre-Framework-4-Assemblies liegen. Alle neuen Assemblies liegen im Assembly-Ordner des .NET-Frameworks (also z.B. C:\Windows\Microsoft.NET\assembly).

Für mich erklärt der MSDN-Auszug nun nicht ganz, warum hier ein anderer Ordner notwendig war, der sich auch noch anders verhält als der bisherige (s. Bild) - irgendwie hatte man sich ja schon an die Fusion-Ansicht gewöhnt. Für mich bleiben bei der aktuellen Implementierung viele Fragen offen:
- Hätte man nicht unter C:\Windows\assembly noch eine Ordnerebene einführen können, die die Frameworkversion widerspiegelt?
- Weshalb hat der neue GAC keine Fusion-Ansicht, in der so wichtige Informationen wie der Public-Key-Token und die Culture auf einen Blick sichtbar sind?
- Weshalb kann die Fusion-Ansicht im alten GAC per Drag & Drop nicht selbstständig in die richtigen Ordner installieren?
- ....
Glücklich bin ich mit der jetzigen Lösung nicht, aber ich werde mich - wie viele andere - damit auch arrangieren. Und vielleicht kommt ja noch ein Update oder ServicePack, das alles wieder schön macht.
Wenn man bei WebForms nicht auf jeder Page oder jedem UserControl die notwendigen Namespace importieren möchte. So hat man diese in der Web.config hinterlegt.
Dies funktioniert wunderbar bei WebForms und der WebForm-ViewEngine unter ASP.NET MVC.
Mit den neuen WebPages (.cshtml) und der Razor-Syntax kann man mit
@using Regularly.Models
@using Regulary.Areas.Administration.Models
auch entsprechende Namespaces der Template bekannt machen, Jedoch funktioniert der Weg über die web.config nicht (ob sich dies ändert steht wohl nicht nicht fest).
Aber man einen anderen Weg gehen, dazu muss man beim Application-Start über die Klasse CodeGeneratorSettings die Namespaces hinzufügen.
CodeGeneratorSettings.AddGlobalImport("Regularly.Models");
CodeGeneratorSettings.AddGlobalImport("Regularly.Extensions");
Diese Klasse befindet sich im Namespace Microsoft.WebPages.Compilation
Bevor ich weitere Seiten mit Vereinsverwaltungsinhalt bastle, wollte ich zuerst noch eine Userverwaltung mit Anmeldung integrieren.
Wenn man ein neues Projekt mit der Vorlage “ASP.NET Webanwendung” beginnt, kommt ein Template mit einer integrierten Userverwaltung. Davon habe ich mir relevante Teile in mein Projekt rüberkopiert und – einen hartnäckigen Laufzeitfehler erhalten. ("Could not find stored procedure 'dbo.aspnet_CheckSchemaVersion'.") Im Web habe ich die Fehlermeldung auch oft angetroffen, aber leider bezogen sich alle gefundenen Hilfestellungen auf alte Versionen und die angeführten Lösungen haben bei mir nichts gebracht.
Die Ursache des Problems habe ich dann doch noch selbst gefunden (War ja auch selber schuld): Ich habe meinen Computer gewechselt und alle Tabellen inklusive der Userverwaltungs-Tabellen mit meinem SQL-Skript, das ich auch letzte Woche gepostet habe, neu erzeugt. Offensichtlich macht aspnet_regsql aber noch anderes, als nur diese Tabellen erzeugen.
Die Lösung: Mit aspnet_regsql die Userverwaltung entfernt und dann wieder neu installiert. Und – welch ein Wunder – das Problem war gelöst. Vielleicht geht’s auch ohne Deinstallation; das habe ich aber nicht mehr probieren wollen.
Heute hatte ich die nächste Herausforderung mit der Userverwaltung. Ich wollte die beiden Seiten zur Gruppen- und Personenverwaltung, die bisher ja schon ganz gut funktionierten, so umgestalten, dass in der DropDownList nur mehr jene Vereine aufscheinen, für die der eingeloggte User verantwortlich ist. Die SQL-Abfrage dazu mit WHERE Klausel war schnell erstellt. Nur – woher nehme ich die Daten des angemeldeten User? Ich habe nach langem Suchen dann auf http://www.velocityreviews.com/forums/t237583-asp-net-2-0-membership-current-user.html den entscheidenden Hinweis gefunden, (MembershipUser u = Membership.GetUser(HttpContext.Current.User.Identity.Name)
u hat dann eine public property UserName, die sich dann mit der Tabelle aspnet_Users vergleichen lässt.
Als nächstes versuche ich, mir ein User Control zu bauen, mit dem neue Vereinsmitglieder erfasst werden können und die Zugangsdaten dann gleich per E-Mail zu versenden.
Die 1. Preview vom ASP.NET MVC Framework ist draußen und ScottGu & Phil Haack haben bereits darüber gebloggt. Eine große Neuheit ist der Razor Syntax. Da das Tooling in diese Preview noch nicht soweit ist, fehlt standardmäßig jegliche Syntaxhighlighting in den “cshtml”. Mit einem kleinen Trick bekommt man wenigstens HTML Syntax Highlighting in den Views.
So sieht es nach der Installation aus:
Etwas trist. Das wird sich aber bis zum Release von MVC 3 natürlich noch verbessern. Hoff ich jedenfalls
Trick zum HTML Syntax Highlighting
Rechtsklick auf eine “.cshtml” dann “Open with…”/”Öffnen mit…” auswählen und dort den HTML Editor auswählen. Wichtig wäre noch das man auf die Auswahl als “Default” markiert, damit man diese Prozedur nicht ständig wiederholen muss.
Danach hat man immerhin einfaches HTML Syntax Highlighting:
Diese einfachen, aber effektiven Tipp hab ich von Tatham Oddie. Super Idee, solange das MVC Tooling noch nicht fertig ist.
Ein Blick auf die Preview lohnt.
ShareThis
Microsoft Österreich sucht einen Spezialisten rund um Cloud! Bewerbungen werden gerne online entgegen genommen! :)
Technology Solution Professional Azure (m/f)
The Azure™ Services Platform (Azure) is an internet-scale cloud services platform hosted in Microsoft data centers, which provides an operating system and a set of developer services that can be used individually or together. Azure’s flexible and interoperable platform can be used to build new applications to run from the cloud or enhance existing applications with cloud-based capabilities. Its open architecture gives developers the choice to build web applications, applications running on connected devices, PCs, servers, or hybrid solutions offering the best of online and on-premises.
Primary Responsibilities:
- Be the senior technical resource for the regional Windows Azure field sales force
- Work with the sales force on escalations to accelerate deals
- Aid in architecting solutions in a complex sales environment
- Remove technical sales roadblocks – including direct involvement in critical/strategic deals.
- Present to customers – from core developers to CIOs
- Identify key trends & drive necessary improvements in sales & product to capitalize on opportunities.
Requirements and Skills/Qualifications:
- 10+ years enterprise technology sales or consulting experience with deep expertise on Microsoft enterprise products and server platform
- Strong understanding of cloud computing technologies, emerging trends and related technology
- Knowledge of SaaS development methodologies – including ability to code solutions in both managed & native code
- Able to articulate pros & cons of design/architecture decision across a wide spectrum of factors – e.g. latency, storage, startup/shutdown, etc.
- Able to coach and mentor a broad constituency – TSPs, Consultants, Partners, & Customers
- Able to distill widely variant feedback into root cause issues and provide actionable feedback to other stakeholders
- Willingness to travel 30 – 50% of the time
- Perfect German- and English skills
If you are interested in this position based in Vienna (Austria), please apply online.
Die DNUG Braunschweig trifft sich am 03.08.2010 um 19:00 im Restaurant Zucker (Tagungsraum). Uns kommt Daniel Fisher mit einem Vortag über ASP.NET MVC besuchen.
Abstract:
Mit ASP.NET MVC ist ein völlig neues Konzept am Markt, mit dem Ergebnisse nicht nur im HTML-Format an den Browser ausgeliefert werden können. In dieser Session wird unter Anderem gezeigt, wie eine WebAnwendung Informationen für JavaScript nutzbar macht, Termine und Kontaktdaten für Outlook und Co. bereitstellt. Ein Muss für jede WebAnwendung die die interagiert oder selbst als Mash-up fungiert.
Bio:
Daniel Fisher ist Mitbegründer der Firma devcoach® (www.devcoach.biz), die Unternehmen beim Einsatz der Microsoft® .NET Plattform in konkreten Projekten von der Architektur bis zum Deployment ganzheitlich unterstützt und begleitet.
Im Mittelpunkt seiner Arbeit stehen Service-Orientierung (SOA), agile Methoden und Prozesse, das Web und Datenzugriff. Er ist ein ausgewiesener Experte im SOA-Umfeld und plant und realisiert seit mehreren Jahren verteilte Systeme auf der Microsoft Plattform.
Daniel ist einer der Windows Communication Foundation (WCF) -Experten der ersten Stunde und war Mitglied im Technical Adoption Program der Microsoft Corporation. Für das Bundesamt für Sicherheit in der Informationstechnik (BSI) hat er zusammen mit Michael Willers (Projectlead) eine umfangreiche Sicherheits-Analyse durchgeführt und praxiserprobte Leitfäden für den Umgang mit WCF im Projektalltag entwickelt. Als Experten zum Thema „SOA mit .Net“ bereisten sie im Auftrag von Microsoft den europäischen Kontinent, um bei Workshops mit Architekten aus aller Welt deren Anforderungen zu diskutieren und bei der Implementierung von konkreten Lösungen zu beraten.
Daniel entwickelt seit 1995 Software (seit 1999 auf der .NET Plattform) und verfügt über Projekterfahrung als Entwickler, Architekt, Projektleiter und Berater aus den Branchen Versicherung, Großhandel, Mobilfunk und Bankwesen.
Er ist Leiter der .NET-Entwickler-User-Group Niederrhein (www.netug-niederrhein.de) und Vorstand der JustCommunity e.V. (www.justcommunity.de), dem Veranstalter des größten regionalen Community-Events für Software-Entwickler und IT-Professionals (www.nrwconf.de). Sie finden sein Blog unter lennybacon.com.
Wie immer ist dieses Event kostenlos und jeder .NET Interessierte ist herzlich willkommen!
Weitere Informationen zur DNUG Braunschweig findest du hier.
Oder wie der Brite auch sagen würde “Cannot get IIS pickup directory”. Beim senden von EMails aus ASP.NET per SMTP Service kann ich immer nur raten, nicht die Network Methode zu wählen, sondern direkt ins Pickup Directory des IIS zu schreiben. Das geht schneller und ist ausfallsicher falls der SMTP Server mal nicht erreichbar ist. Ein typischer Anwendungsfall von asynchroner Architektur. Nun ist der SMTP Server beim IIS 7 (und 7.5) noch immer der alte aus dem IIS 6. Wenn man also WIndows Server 2008 oder 2008 R2 hat muss man den SMTP Server extra verwalten mit der alten MMC Console. Das heist die Metabase ist nach wie vor im Spiel und nicht die neuen Config Dateien. Übrigens war Microsoft auf so nett den POP3 Server rauszunehmen. Worauf mein Kollege Cosmin den kostenfreien VIsendo SMTP Extender programmiert hat.
Beim IIS7 7.5 wird per default der Benutzer ApplicationPoolIdentity verwendet um die Web Anwendung zu betreiben. Dieser Benutzer hat stark limiterte Rechte.

Ebenso fehlt wohl das Zugriffsrecht (ACL) auf die Einträge aus dem SMTP Bereich in der Metabase.
Wenn nun beim versenden einer Mail die Meldung kommt “IIS-Pickup-Verzeichnis kann nicht abgerufen werden” dann helfen folgende Lösungsansätze
1) In der Web.config den Namen des Pickupdirectorys manuell setzen
http://blogs.ppedv.de/hannesp/archive/Mail-Sender-Klartextnamen-in-web.config-setzen
2) Man kann dem Beutzer auch die Rechte geben. Dazu sollte man den Metabase Editor verwenden. Dieser ist im IIS6 Resource Kit enthalten. Per Rechtsclick auf den Knoten im Tree (hier 1) kann man dann die benötigten Rechte setzen.

3) Den Benutzer in den Einstellungen des Application Pools ( Abbildung 1) auf System ändern. Mit entsprechenden Konsequenzen in der Sicherheit.
Das nächste Treffen der .NET Developer Group Braunschweig findet am
Dienstag,
den
03.08.10, ab
19:00 Uhr im Zucker (ARTMax) statt.
Daniel
Fisher hält einen Vortrag über ASP.NET MVC.
Better "Results" with ASP.NET MVC
Mit ASP.NET MVC ist ein völlig neues Konzept am Markt, mit
dem Ergebnisse nicht nur im HTML-Format an den Browser ausgeliefert werden können.
In dieser Session wird unter Anderem gezeigt, wie eine WebAnwendung Informationen
für JavaScript nutzbar macht, Termine und Kontaktdaten für Outlook und Co. bereitstellt.
Ein Muss für jede WebAnwendung die die interagiert oder selbst als Mash-up fungiert.
Weitere Infos: >http://www.dotnet-braunschweig.de
Anfang Juni wurde die neue Version der Expression-Produktfamilie offiziell vorgestellt, seit letzter Woche ist Expression Studio nun auch auf Deutsch erhältlich: Auf der deutschen Expression-Webseite sind ab sofort deutschsprachige 60-Tage-Testversionen zum Download verfügbar. Microsoft Expression ist eine Sammlung professioneller Designwerkzeuge zur Entwicklung und Gestaltung von Webanwendungen, Benutzeroberflächen für Windows-Anwendungen und multimediale Rich-Media-Anwendungen. Ab Mitte August wird Expression 4 dann auch im Handel verfügbar sein.
Expression-User, die Expression 3 im Einzelhandel erworben haben, können sich die Testversionen von Expression Studio 4 Ultimate oder Expression Studio 4 Web Professional herunterladen. Sofern Expression 3... [... mehr in diesem Blogposting auf Giza-Blog.de]
This post is powered by
www.Giza-Blog.de |
Visit: MSDN Online |
Follow MSDN Online on
Twitter | Follow Kay
Giza on Twitter
Daily News on MSDN:
MSDN
Aktuell
©
Copyright 2006-2010 Kay Giza. All rights reserved.
Legal
Bei der Implementierung des Entwurfsmusters MVVM (Model-View-ViewModel) wird im ViewModel die Oberfläche durch Properties abgebildet. Dabei gelten für Aktionen, zum Beispiel die Logik beim Auslösen eines einfachen Buttons, dass das Handling mittels Commands umgesetzt wird. Das bringt einen enormen Vorteil mit sich, zum einen wäre das die 100% Entkopplung mittels Data-Binding. Allerdings erhält man zudem einen großen Nachteil. Commands beschränken sich nur auf die gängigen Standardaktionen. Dabei wird zum Beispiel nur beim betätigen eines Buttons das Command ausgelöst. Bei einer ListBox wäre dies wiederrum die Auswahl eines Items.
Diese Einschränkung kann unter WPF mittels EventTrigger elegant gelöst werden. Dennoch gibt es ein weiteres Problem, man erhält leider keine weiteren Informationen die im Normalfall von klassischen Events mittels EventArgs übergeben werden würden. Zudem gibt es unter Silverlight keine Trigger-Funktionalität.
In manchen Situationen können die Informationen zwar über CommandParameter übertragen werden, trotzdem tritt man immer wieder auf das fehlende Informationsproblem. Zudem möchte man Commands eventuell gar nicht auf die Standardaktionen binden. Viel eleganter wäre es, wenn man auf jeden beliebigen Event eines Controls ein Command auslösen könnte. Dazu müsste man dann vom ViewModel auf den EventManager zugreifen. Das benötigt leider etwas Source-Code und leider besteht eine gewisse Anhänglichkeit zu den jeweiligen Controls. Es müsste eine Lösung geben womit vom ViewModel entkoppelt auf Events Reagiert werden kann. Zudem wäre es wünschenswert, das kaum Source-Code dazu nötig ist.
Genau zu diesem Problem bietet Expression Blend 4 die Lösung mittels Behaviors. Behaviors sind fertige UI-Funktionalitäten die in gewisser Art als Snippet auf das gewünschte Control gezogen werden. Ab Expression Blend 4 bieten folgende Behavios die gewünschte Funktionalität:
| Name: | Funktion: |
| CallMethodAction | Löst bei einem bestimmten Event eine beliebige Methode eines Zielobjekts (z.B. ViewModel) aus. Dabei werden die jeweiligen EventArgs mit übertragen. |
| ChangePropertyAction | Bei einem Event wird ein Propertie eines beliebigen Zielobjekts (z.B. ViewModel), nach einer beliebigen Iteration ein Wert zugewiesen. |
| InvokeCommandBehavior | Erweitert ein Control durch ein Command, das auch auf jedem beliebigem Event ausgelöst werden kann. Es werden dabei keine EventArgs übertragen, nur CommandParameter . |
Ein Beispiel: Das SelectionChanged-Event einer Liste im ViewModel behandeln
Als einfaches Beispiel bietet sich das abfangen des SelectionChanged-Event einer Liste an. Dazu wird ein WPF oder Silverlight-Projekt mit einer ListBox benötigt. Die Beispieldaten können mit dem Data-Feature das es ab Expression Blend 3 gibt, zur Verfügung gestellt werden. Ein How-To dazu gibt es hier: Expression Blend 3 - Daten für Designer bereitstellen
Abb.1.1. – Eine Liste mit Beispieldaten
Der nächste Schritt ist das Anlegen des ViewModels. Dazu wird eine einfache Klasse mit dem Namen „ExampleViewModel“ erzeugt. Hier wird eine Methode und die dazugehörige SelectionChangedEventArgs-Signatur hinzugefügt.
1: using System;
2: using System.Windows.Controls;
3:
4: namespace WpfCallMethodExample.ViewModels
5: {
6: public class ExampleViewModel
7: {
8: public void SelectionChangedMethod(object sender, SelectionChangedEventArgs e)
9: {
10: throw new NotImplementedException();
11: }
12: }
13: }
Listing 1.1. – Source-Code des ViewModels „ExampleViewModel.cs“
Eine Instanz zum ViewModel wird von der View aus im Resourcen-Bereich der jeweiligen View erzeugt.
1: <Window
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
5: xmlns:local="clr-namespace:WpfCallMethodExample.ViewModels"
6: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="WpfCallMethodExample.MainWindow"
7: Title="MainWindow" Height="350" Width="525">
8: <Window.Resources>
9: <DataTemplate x:Key="ItemTemplate">
10: <StackPanel>
11: <TextBlock Text="{Binding Property1}"/>
12: </StackPanel>
13: </DataTemplate>
14: <local:ExampleViewModel x:Key="ViewModel" />
15: </Window.Resources>
16: <Grid DataContext="{Binding Source={StaticResource SampleDataSource}}">
17: <ListBox Margin="8,64,8,8" ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" />
18: <TextBlock Margin="8,8,8,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="52" FontSize="21.333"><Run Language="de-de" Text="CallMethodExample"/></TextBlock>
19: </Grid>
20: </Window>
Listing 1.2. – Eine Instanz zum ViewModel von der View erzeugen lassen.
Es folgt nun das Zuweisen eines Behaviors. Dabei wird im Assets-Fenster unter dem Reiterpunkt Behaviors, das Behavior CallMethodAction ausgewählt und mittels Drag & Drop auf die ListBox gezogen.
Abb.1.2. –Das CallMethodBehavior auf die ListBox ziehen
Um die Einstellungen des Behaviors zu Konfigurieren, muss im Objekt-Fenster das beliege Control aufgeklappt werden. Dahinter stehen dann alle definierten Behaviors. Nach der gewünschten Auswahl stehen die Einstellungen im Fenster Properties.
Abb.1.3 – Properties zum Behavior anzeigen
Bei den Properties zum CallMethodAction-Behavior wird nun unter Trigger das Event SelectionChanged ausgewählt. Dabei wird als Triggertype der EventTrigger belassen. Unter Common Properties muss als TargetObject ein Binding zum ViewModel erfolgen. Als MethodName wird dann der Methodenname manuell eingetragen. Ganz wichtig ist hierbei das auslasen der Klammern. Siehe Abb.1.4..
Abb.1.4. – CallMethodAction-Behavior konfigurieren
Der XAML-Code sollte nun wie unter Listing 1.3. aussehen. Dabei wurde das Projekt um die Microsoft.Expression.Interactions-Assembly erweitert. Diese bietet dann eine Erweiterung für Trigger-Funktionalitäten. Unter Silverlight wird somit die fehlende Trigger-Funktion zur Verfügung gestellt.
1: <Window
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
5: xmlns:local="clr-namespace:WpfCallMethodExample.ViewModels"
6: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="WpfCallMethodExample.MainWindow"
7: Title="MainWindow" Height="350" Width="525">
8: <Window.Resources>
9: <DataTemplate x:Key="ItemTemplate">
10: <StackPanel>
11: <TextBlock Text="{Binding Property1}"/>
12: </StackPanel>
13: </DataTemplate>
14: <local:ExampleViewModel x:Key="ViewModel" />
15: </Window.Resources>
16: <Grid DataContext="{Binding Source={StaticResource SampleDataSource}}">
17: <ListBox Margin="8,64,8,8" ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}">
18: <i:Interaction.Triggers>
19: <i:EventTrigger EventName="SelectionChanged">
20: <ei:CallMethodAction TargetObject="{Binding Mode=OneWay, Source={StaticResource ViewModel}}" MethodName="SelectionChangedMethod"/>
21: </i:EventTrigger>
22: </i:Interaction.Triggers>
23: </ListBox>
24: <TextBlock Margin="8,8,8,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="52" FontSize="21.333"><Run Language="de-de" Text="CallMethodExample"/></TextBlock>
25:
26: </Grid>
27: </Window>
Listing 1.3. – XAML-Code der View.
Zu guter Letzt wird nun die SelectionChangedMethod mit Logik erweitert. Dabei folgt nun ein MessageBox Aufruf mit den ausgewählten Datensatz. Wobei bemerkt werden muss, das ein MessageBox Aufruf in der Regel nicht vom ViewModel aus geschieht.
1: using System.Windows;
2: using System.Windows.Controls;
3: using Expression.Blend.SampleData.SampleDataSource;
4:
5: namespace WpfCallMethodExample.ViewModels
6: {
7: public class ExampleViewModel
8: {
9: public void SelectionChangedMethod(object sender, SelectionChangedEventArgs e)
10: {
11: Item selectedItem = (Item)e.AddedItems[0];
12: MessageBox.Show(selectedItem.Property1);
13: }
14: }
15: }
Listing 1.4. – Source-Code der Implementierung von SelectionChangedMethod.
Beim Ausführen der Anwendung, erscheint nun bei der Auswahl eines Items die jeweilige MessageBox.
Abb.1.5. – Die Auswahl eines Items wird in einer MessageBox dargestellt.
Fazit
Behaviors sind immer wichtiger geworden. Ich selbst sehe Sie auch als eine Art Aspektorientierte Programmierung für das Frontend. Es muss kein Code geschrieben werden, es kann zudem immer wieder verwendet werden und die Übersichtlichkeit des eigenen Codes bleibt gegeben. Die neuen Behaviors in Expression Blend 4 bringen eine große Abhilfe bei den Stolpersteinen der heutigen Entwicklung mit sich. Zum Beispiel bei der Entwicklung mit dem MVVM.
Ich habe mich heute mal einfach an die FizzBuzz Kata gesetzt und nach TDD gelöst. FizzBuzz ist meiner Meinung nach die einfachste Kata, die ich hier verwenden möchte um mein Anliegen kund zu tun. Ich möchte an dieser Stelle nicht alle Schritte erläutern...(read more)

Der Just Community e.V. veranstaltet nun bereits zum sechsten Mal in Folge die NRW Conf. War das Event in den Anfängen noch eine reine Abendveranstaltung, so wird in diesem Jahr ein zweitägiges Event, mit einem Workshop- und einem Konferenztag, angeboten.
Drei Workshops, mehr als 24 Fach-Vorträge und viel Gelegenheit für Networking bilden dabei die Grundlage für eine außergewöhnliche Community-Veranstaltung.
Das Feedback der letzten Jahre wurde eingearbeitet und so gibt es in diesem Jahr wieder kleine Änderungen, um die Wünsche der Teilnehmer noch besser zu erfüllen.
Das internationale Sprecherfeld ist hochkarätig besetzt und und es gibt wieder eine große Vielfalt an Sessions aus den Themengebieten Data, Design, Dev, IT-Pro, Soft Skills und Web.
Die Workshops am 09.09.2010 haben die Themen: „Developer – Effizientes C# – Die wirklich wichtigen Features“, „Microsoft Business Intelligence für Entwickler“ und „SharePoint/Nintex – Nintex for runaways“ und sind mit 59,– € für einen Ganztagesworkshop ein echtes Schnäppchen.
Genauso die Konferenz am 10.09.2010. Denn es hat sich nichts geändert an der Teilnahmegebühr von unschlagbar günstigen 15,– € und dem Veranstaltungsort, der Börse in Wuppertal.
Wer besonders schnell ist und sich bis zum 31.07.2010 zur Konferenz anmeldet und seine Gebühr überweist, hat als Early Bird zusätzlich noch die Chance auf interessante Preise.
Letzte Woche ist ein aktualisierter UI Design und Interaktions Guide für Windows Phone 7 erschienen. Unter anderem sind einige Photoshop Templates für Mockups dabei. Hier der englische Post Windows Phone 7 Design Resources – UI Guide and Design Templates...(read more)
Anfang Juni wurde die neue Version der Expression-Produktfamilie offiziell vorgestellt, seit letzter Woche ist Expression Studio nun auch auf Deutsch erhältlich: Auf der deutschen Expression-Webseite sind ab sofort deutschsprachige 60-Tage-Testversionen...(read more)
Serverseitige Validierung
Das ASP.NET MVC2 Framework biete eine einfache Möglichkeit zur Validierung eines Modells: Data Annotations. Mit Attributen wird die Validierung für das Modell definiert. Im MVC2 Framework sind vier Validatoren enthalten: Required, StringLength, Range und RegularExpression. Eine Klasse “Contact” könnte also folgendermaßen annotiert werden.
public class Contact
{
[Required(ErrorMessage = "Es muss ein Vorname angegeben werden.")]
[StringLength(200)]
public string Vorname { get; set; }
[Required(ErrorMessage = "Es muss ein Nachname angegeben werden.")]
public string Nachname { get; set; }
[Range(0, 69, ErrorMessage = "Mit mehr als 69 Kindern halten sie den Weltrekord. Unwahrscheinlich!")]
public int Kinder { get; set; }
[RegularExpression(@"[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}")]
public string Email { get; set; }
}
Damit kann anschließend das Modell entsprechend der Attributierung validieren und die Fehlermeldungen an den Anwender ausgeben lassen. Zusätzlich kann man noch eigene Meldungen an die View mittels “ModelState.AddModelError(<Eigenschaft>, <Nachricht>)” durchreichen.
[HttpPost()]
public ActionResult Logon(LogonModel model, string returnUrl)
{
// Validierung des Modells abfragen: “ModelState.IsValid”
if (!ModelState.IsValid) return View(model);
// Alternativ Meldungen für das gesamte Modell ausgeben
ModelState.AddModelError("", "Deine eingegebenen Daten sind falsch.");
return View(model);
}
Benutzerdefinierte serverseitige Validierung
Diese vier Validatoren decken die häufigsten Fällt ab, leider aber nicht alle. Manchmal kommt man um eine eigene Validierung nicht herum. Dazu sieht das MCV2 Framework die Basisklasse “ValidationAttribute” vor. Erbt eine Klasse von dem ValidationAttribute kann die Methoden “IsValid” überschrieben werden. In dieser Methode wird die Validierungslogik implementiert. Hier ist ein kleines Beispiel für eine Validierung, welches die Komplexität eines Kennworts prüft (mindestens ein Buchstabe groß geschrieben).
public class ComplexPasswordAttribute : ValidationAttribute
{
private readonly bool _capitalLetterRequired = true;
public ComplexPasswordAttribute(bool capitalLetterRequired)
{
_capitalLetterRequired = capitalLetterRequired;
}
public override bool IsValid(object value)
{
if (value == null) return false;
var valueString = value.ToString();
if (_capitalLetterRequired)
if (valueString.ToLower() == valueString)
return false;
return true;
}
}
Der Validator kann nun wie die anderen Attribute verwendet werden. Das MVC2 Framework ruft automatisch die “IsValid()” Methode auf und gibt bei einem negativen Ergebnis die Fehlermeldung “ErrorMessage” an die View weiter.
public class LoginData
{
[Required()]
public string LoginName { get; set; }
[ComplexPassword(true, ErrorMessage=”Kennwort ist zu einfach”)]
public string Password { get; set; }
}
Das nächste Treffen der .NET Usergroup Dresden findet am 25.08.2010 statt. Austragungsort ist dieses Mal die
Communardo Software GmbH.
Wie bereits im Juli-Treffen angekündigt werden dabei folgende Themen eine Rolle spielen:
Branching/Merging Strategien mit dem TFS (
Robert Mühsig)
Nachdem wir bei der letzten Usergroup den Themenkomplex Buildmanagement vertieft haben, setzt Robert seinen Vortrag zum Thema TFS fort. Dieses Mal geht es um Branching- und Merging-Strategien.
Einführung in die Entwicklung für Winows Phone 7 (
Martin Hey)
Windows Phone 7 wird dieses Jahr gelauncht. Der Vortrag soll eine Einführung in die Möglichkeiten und Besonderheiten der Entwicklung für Windows Phone 7 geben.
Das Treffen beginnt wie immer um 18:00 Uhr. Wer daran interessiert ist zu kommen, den bitten wir sich in einer der Teilnehmerlisten (
Xing oder
Doodle) einzutragen. Nach dem Treffen ist jeder gern eingeladen, beim Stammtisch an einem Plausch in lockerer Atmosphäre über .NET oder alles andere teilzunehmen.
Sehr erfreut waren viele C++ Entwickler darüber das es mit der MFC in VC-2008 weiter ging und MFC-Next veröffentlicht wurde. Das ganze wurde dann fest in VS-2008 SP1 integriert. Normalerweise sind wir es gewohnt, dass zur MFC nur Dinge hinzukommen und nichts wegfällt.
Für die MFC 10.0 aus VS-2010 gilt das diesmal nicht: MFC 10.0 < MFC-Next 9.0!
Irgendwie hat es CMFCRibbonPanel::EnableLaunchButton nicht in die MFC 10.0 geschafft, obwohl die Funktion vollständig in der MFC-Next 9.0 implementiert war. Das soll mal einer verstehen
ich jedenfalls nicht!
Diese Funktion sorgt für den kleinen netten Schalter in einem Panel:

Erstaunlicherweise gibt es diese Funktion nun nicht mehr! Wer also 100% Office-kompatible Anwendungen schreiben will ist hier schon mal aufgeschmissen, wenn er das mit MFC 10.0 machen will.
Im Header finden wir diese Funktion noch mit einem #ifdef auskommentiert. Allerdings nützt es nichts diesen #define zu setzen, denn es gibt keine Implementierung und entsprechend keinen Code in der DLL/Library. Ja und in der MFC Doku finden wir die Funktion auch noch.
Und auch dieses Problem war noch in der Beta-Phase bekannt und wurde abgebügelt, wie man in den nachfolgenden Links lesen kann.
http://social.msdn.microsoft.com/Forums/en/vcmfcatl/thread/29ad2859-6341-4ffb-85c2-f5f056a6ca48
https://connect.microsoft.com/VisualStudioJapan/feedback/details/533876/cmfcribbonpanel-enablelaunchbutton?wa=wsignin1.0
Wem es möglich ist, sollte hier bitte Abstimmen und diesen Bug als wichtig kennzeichnen!
Langsam frage ich mich ob es nicht gescheiter gewesen wäre bei MFC-Next 9.0 zu bleiben und mit VS-2008 weiter zu arbeiten.
Tja und so hat die BCG-Library in Verbindung mit VS-2010 auch eine Daseinsberechtigung. Die kann diesen LaunchButton natürlich darstellen.
PS: Auf Nachfrage bei Microsoft bekam ich eine Antwort aber keinerlei Begründung. Jetzt habe ich eine Support-Anfrage dies bzgl. laufen, allerdings mit wenig Hoffnung.
Copyright © 2008 Martin Richter
Dieser Feed ist nur für den persönlichen, nicht gewerblichen Gebrauch bestimmt. Eine Verwendung dieses Feeds bzw. der hier veröffentlichten Beiträge auf anderen Webseiten bedarf der ausdrücklichen Genehmigung des Autors.
(Digital Fingerprint: bdafe67664ea5aacaab71f8c0a581adf)
Themenverwandte Beiträge:
Manchmal lassen sich Fehler in einer Anwendung nicht vermeiden. Sei es weil ein externer Service nicht erreichbar ist, die Datenbank streikt oder irgendwas anderem. Ich bin mir sicher das JEDEM haufenweise Gründe hierfür einfallen würden.
Damit man seinen Besuchern aber nicht regelmäßig voller Scham den Yellow Screen of Death präsentieren muss gibt es in ASP.NET MVC das HandleError-Attribut.
[HandleError]
public class HomeController : BaseController
{
public ActionResult Index()
{
return View();
}
}
Das Attribut wird einfach auf die Klasse des Controllers oder aber auf einzelne Resultmethoden gesetzt. Tritt im Code nun eine Exception auf wird im Verzeichnis des aktuellen Controllers nach einer Datei mit dem Namen “Error.aspx” gesucht und dorthin weitergeleitet. Falls die Datei nicht existiert wird als nächstes das “Shared”-Verzeichnis durchsucht. Darüberhinaus besteht die Möglichkeit explizit eine View anzugeben:
[HandleError(View = "ItemNotFound")]
Schön ist auch das man nur auf bestimmte Exceptions reagieren kann bzw. diese jeweils anders behandeln kann.
[HandleError(View = "ItemNotFound", ExceptionType = typeof(NullReferenceException))]
[HandleError(View = "InvalidArgument", ExceptionType = typeof(ArgumentNullException))]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
Damit die Fehlerseiten angezeigt werden müssen die “customErrors” in der Web.config eingeschaltet werden.
<system.web>
<customErrors mode="On" />
</system.web>