Welcome to MSDN Blogs Sign in | Join | Help

Windows 7 ist schon als Release Canditate verfügbar. Ein ganze Reihe von neue Funktionen steht mit Windows 7 bereit, die man in seine Anwendungen integrieren kann, um die Funktionalität und Windowsintegration zu erhöhen.

Um hier schnell einen Einstieg zu bekommen, gibt es ein kostenloses Trainingskit – das “Windows 7 Trainingskit”. Herunterladen kann man das Kit von 

http://www.microsoft.com/downloads/details.aspx?FamilyID=12100526-ed26-476b-8e20-69662b8546c1&displaylang=en .

Im Trainingskit selbst kann man sich mit Presentationen einen Überblick aus Entwicklersicht über die neuen Windows 7 Features machen. Vorgestellt werden:

Wenn man Windows 7 RC, Visual Studio 2008 Sp1 und das Windows 7 SDK RC installiert, kann man auch mit HandsOn Labs praktisch die vorgestellten Features anprogrammieren. Die HandsOn Labs bestehen aus dem erklärenden PPT Slides zum Feature, einem Dokument das schrittweise die Programmierung erklärt und einer fertigen VisualStudio Lösung.

 

Viel Spass beim Ausprobieren … GunnarD

Zum ersten Mal wird in diesem Jahr die TechEd Europe in Deutschland stattfinden. Die TechEd wird vom 9.-13. November in Berlin stattfinden. Die TechEd ist aus meiner Sicht die Veranstaltung, die man als .NET Entwickler, Windows Entwickler und IT Professional besuchen muss.

Dieses Jahr gibt’s definitiv auch viel Neues da ja .NET 4.0, VisualStudio 10 und Windows 7 vor der Tür stehen.

Ab heute, dem 22.06., kann man sich auf http://www.microsoft.com/europe/teched/ registrieren. Es gibt interessante Frühbucheroptionen.

See you on TechEd … GunnarD

In verschiedenen Einträgen in diesem Blog habe ich über die Verwendung von POCO’s mit dem Entityframework berichtet. In seinem Buch “Applying Domain-Driven Design and Patterns: (Pearson Education, Inc., 2006)”, Jimmy Nilsson definiert POCO’s als "…ordinary classes where you focus on the business problem at hand without adding stuff for infrastructure-related reasons. ... The classes should focus on the business problem at hand. Nothing else should be in the classes in the Domain Model."

Mit der .NET Version 3.5 SP1 des Entityframeworks ist es nur indirekt möglich das Entityframework und POCO’s zu nutzen. In diesen Blog habe ich einige Möglichkeiten aufgezeigt, wie das aktuelle Entityframework und POCO’s zusammenarbeiten.

Nun gibt es auf dem Teamblock des ADO.NET Teams eine Ankündigung, dass in der .NET Version 4.0 ein Entityframework enthalten sein wird, welches POCO’s unterstützt. Dazu sollte man den Artikel “Sneak Preview: Persistence Ignorance and POCO in Entity Framework 4.0” lesen --> http://blogs.msdn.com/adonet/archive/2009/05/11/sneak-preview-persistence-ignorance-and-poco-in-entity-framework-4-0.aspx

Im Artikel selbst äussert sich der Autor auch zu den Vorteilen von POCO’s:

“… In Entity Framework 3.5 (.NET 3.5 SP1), there are more than a few restrictions that were imposed on entity classes. Entity classes in EF needed to either be sub classes of EntityObject, or had to implement a set of interfaces we collectively refer to as IPOCO – i.e. IEntityWithKey, IEntityWithChangeTracker and IEntityWithRelationships. These restrictions made it difficult if not downright impossible to build EF friendly domain classes that were truly independent of persistence concerns. It also meant that the testability of the domain classes was severely compromised. …”

GunnarD

Die Verwendung der SharePoint Webservices (hier speziell dem Lists.asmx) ist hinreichend beschrieben (z.B. hier)

Bei der Verwendung sind mir jedoch zwei Dinge aufgefallen die mir beim Entwickeln immer Zeit (und manchmal auch Nerven) kosten:

  • Wie sind die Feld/Spaltennamen die ich als FieldRef eintragen muss ?
  • Wie sieht die Filterbedingung in CAML aus die ich verwenden muss ?

Lösung: CAMLQueryBuilder

Es gibt jedoch in den einschlägigen SharePoint Tools von U2U den CAMLQueryBuilder der genau diese Probleme adressiert. Das Schöne an dem Tool ist dass man die Queries direkt auf Demo/Testdaten bauen kann und die Ergebnisse auch gleich auf Plausibilität überprüfbar sind.

Wenn man diese Hürde genommen hat lassen sich Abfragen gegen den Lists.asmx zum Ermitteln der Listeneinträge recht komfortabel wie folgt erstellen:

// Declare and initialize a variable for the Lists Web Service.
server.Lists listService = new server.Lists();
/* Authenticate the current user by passing their default
credentials to the Web Service from the system credential cache.*/
listService.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Set the Url property of the service for the path to a subsite.
listService.Url = "http://server/_vti_bin/Lists.asmx";
// Instantiate an XmlDocument object
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
/* Assign values to the string parameters of the GetListItems method,
using GUIDs for the listName and viewName variables*/
string listName = "Contacts";
string rowLimit = "150";

/*Use the CreateElement method of the document object to create elements
for the parameters that use XML*/
System.Xml.XmlElement query = xmlDoc.CreateElement("Query");
System.Xml.XmlElement viewFields = xmlDoc.CreateElement("ViewFields");
System.Xml.XmlElement queryOptions = xmlDoc.CreateElement("QueryOptions");

/*To specify values for the parameter elements (optional), assign CAML fragments
to the InnerXml property of each element*/
query.InnerXml = "<Where><Gt><FieldRef Name=\"ID\" />" + "<Value Type=\"Counter\">3</Value></Gt></Where>";
viewFields.InnerXml = "<FieldRef Name=\"Title\" />";
            
viewFields.InnerXml="<FieldRef Name='ID'/><FieldRef Name='Title'/>" +
                    "<FieldRef Name='FirstName'/><FieldRef Name='FullName'/>" +                                    
                    "<FieldRef Name='Email'/><FieldRef Name='Company'/>" +
                    "<FieldRef Name='JobTitle'/><FieldRef Name='WorkPhone'/>" +
                    "<FieldRef Name='HomePhone'/><FieldRef Name='CellPhone'/>" +
                    "<FieldRef Name='WorkFax'/><FieldRef Name='WorkAddress'/>" +
                    "<FieldRef Name='WorkCity'/><FieldRef Name='WorkState'/>" +
                    "<FieldRef Name='WorkZip'/><FieldRef Name='WorkCountry'/>";
                                    
            
    queryOptions.InnerXml = "";

/* Declare an XmlNode object and initialize it with the XML response from
the GetListItems method.*/
System.Xml.XmlNode nodeListItems = listService.GetListItems(listName, "",query,viewFields,rowLimit,queryOptions);
// Loop through each node in the XML response and display each item.
System.Data.DataSet  myds = new DataSet("myds");
System.IO.StringReader xmlSR = new StringReader(nodeListItems.InnerXml);
myds.ReadXml (xmlSR);
//dataGrid1.DataSource=myds.Tables[1];

Viel Spass beim ausprobieren

Sven

diese Kombination hat mich bisher in den Wahnsinn getrieben, und der Versuch mit FBA aus Office heraus ein Dokument zu öffnen endete meist so:

FBA1

Inzwischen geht das auch besser, und sieht so aus:

FBA3

Erreichen kann man das wesentlich bessere Verhalten von Office durch Einstellungen und Fixes - eine genaue Anleitung findet sich hier.


Viel Spass beim Öffnen von Dokumenten


Sven

In meinen Projekten bin ich neulich über eine Migration eines Lotus Notes Workflows gestolpert. Normalerweise benutzt man hier aus Gründen der Gestaltung Infopath 2007 Formulare um die Dateneingabe zu validieren.

Jetzt bietet Notes ein sogenanntes selbstlernendes Feld (also eine Combobox die es erlaubt neue eigene Einträge zu erstellen).

Wenn man Infopath als Client verwendet ist das auch sehr einfach, dort ist eine Combobox als Control vorgesehen. Soll das ganze jetzt aber über die Infopath Forms Services laufen wird`s etwas schwieriger, denn die Combobox ist dort nicht verfügbar.

Abhilfe schafft hier ein eigenes kleines Control welches aus 3 Bausteinen besteht:

Einer Textbox für die Eingabe, einem Button für die Steuerung sowie einer Listbox für die Anzeige bestehender Felder. Datenquelle ist hier eine SharePoint Liste. In unserem Fall soll ein Bearbeiter entweder aus der bestehenden Liste ausgewählt werden oder ein neuer Bearbeiter direkt eingegeben werden können - das Ganze ist nicht an irgendein Directory, sondern nur an eine einfache SharePoint Liste gebunden.

Das Ganze sieht dann so aus:

 Combo1

Die Idee dahinter ist die Combobox mit Regeln zu emulieren - d.h. folgende Aktionen hinter die Steuerelemente zu legen:

Button: Blendet die Listbox ein/aus - dies wird über Regeln gesteuert

RulesBearbeiter

Textbox: nimmt die Eingabe auf, sonst benötigt diese Textbox keinerlei Regeln

Listbox: Ist an die SharePoint Werteliste gebunden, beim Klick-Event wird der ausgewählte Eintrag in die Textbox kopiert, auch das geht über Regeln

RegelnBearbeiter

Beim Speichern des Formulars wird geprüft ob der Eintrag schon in der Werteliste vorhanden ist und ggf. eingetragen.

Hierzu kann man den Code in der Submit-Methode verwenden oder wie es hier gemacht wurde in einem nachgeschalteten SharePoint Workflow.

Um die Daten aus dem Formular zu bekommen ist es sehr einfach dies zu deserialisieren und ein typisiertes .net Objekt in Händen zu halten, Details hierzu sind hier.

Viel Spass

Sven

Jeder der mit SharePoint Workflows und Infopath 2007 Formularen zur Datenerfassung arbeitet weiss dass das XML welches die Daten enthält sehr komplex werden kann.

Der XMLReader ist hier schnell überfordert, jedoch gibt es mit Hilfe des .net Framework Tools XSD eine schöne Alternative um an die Daten sehr komfortabel heranzukommen.

Als Basis dient uns hier der XML Deserializer aus dem .net Framework - dieser muss jedoch mit einer Klasse gefüttert werden um zu funktionieren. Diese Klasse muss man jedoch nicht selbst schreiben sondern können diese aus dem Infopath-Formular generieren lassen. Dies geht wie folgt:

  • Das InfoPath Formular wie gewohnt speichern (zur Sicherheit)
  • Danach nochmal ein "Save as Source", damit wird eine Datei myschema.xsd erzeugt.
  • Aus dieser Datei wird mit XSD und die Schalter /CLASSES in eine myschema.cs erzeugt
  • Diese CS Datei kann man jetzt in das Projekt einbinden.

Der Code für die Deserialisierung sieht dann so aus:

XmlSerializer serializer = new XMLSerializer(typeof(MyFields));
xmlTextReader reader = new XMLTextReader(new 
System.IO.StringReader(workflowProperties.InitiationData));
MyFields fields = serializer.Deserialize(reader);

Ggf. ist MyFields noch durch den Namen der eigneen Classe in myschema.cs zu ersetzen.

Happy Deserializing

Sven

Mit dem „Software Strategy Summit“ fand am 24. April in Köln das aus Microsoft-Sicht in diesem Jahr wichtigste Strategie-Event für Softwarehersteller und System-Integratoren statt. Inzwischen sind jetzt auch im MSDN Event-Archiv die Video-Aufzeichnungen aller dort gehaltenen Vorträge abrufbar. Neben der bereits veröffentlichten Ansprache von Steve Ballmer sind mit dabei die Keynotes von MSDN Deutschland-Chef Dr. Said Zahedani und Bitkom-Vizepräsident Heinz Paul Bonn sowie insgesamt sieben Fachreferate, u.a. zu den Themen „Microsoft Software + Services Strategie, Windows Azure und die Azure Services Platform“, zur Microsoft Business Intelligence, zur neuen Windows Experience und zu den Office Business Applications.

Aud dem MTC dabei: Unser Kollege Sven „SharePoint“ Maier, der natürlich über sein Lieblingsthema vortragen durfte.

Vielen Dank für die rege Teilnahme letzten Dienstag! Code und Slide Deck sind auf meinem SkyDrive verfügbar oder über Lars Keller zu beziehen. Interessierte zum Thema Presentation Model (bzw. Model-View-ViewModel) sollten sich auch meine beiden Blogeinträge OOP Anmerkungen Teil 1 und 2 anschauen.

Bei dem Vortrag ist mir ein kleiner, aber peinlicher Fehler unterlaufen… ich habe vergessen, die letzte Folie mit den Links zu den Ressourcen rund um die Composite Application Guidance zu zeigen. Daher hier noch mal alle wichtigen Links:

 

Weiter geht’s am 14. Mai in Regensburg!

 

Jörg

There is a new sample with the name HtmlEvents on the http://code.msdn.microsoft.com/SpicIEContrib page.

This sample demonstrates how your managed Plug-In code can register on Html events for specific Html elements. When the registered event is fired then your managed C# will be called.

This mechanism could be helpful for the implementation of complex Plug-Ins.

The sample consists of the sample code (Visual Studion 2008 project) and a HowTo guide which explains in 8 pages the base idea of the Plug-In.

 

GunnarD

After you have written a useful plug-in it comes the time when you have to deploy the plug-in on other computers But there may be some pitfalls during the deployment.

First there is an “Install.bat” in your project which can be used for the plug-in registration.

More difficult is the determination of assemblies which the plug-in depends on. You have to identify all necessary assemblies and must deploy these assemblies with your setup if they are not on the target computer.

Important: SpicIE base framework depends on “Microsoft.mshtml.dll” and “Interop.SHDocVw.dll”.

Both assemblies are part of the Visual Studio 2008 redistribuable file “C:\Program Files\Common Files\Merge Modules\vs90_piaredist.exe”. You can use this file to deploy these assemblies to a target computer.

And finally ... Don't forget that you need a .NET framework on the target computer.

GunnarD

The SpicIE framework uses the .NET System.Diagnostics.TraceSource class to output a lot of information during the runtime.

During the development and debug process these messages could be really helpful to understand how the plugin works.

But there are situations where you want to reduce or switch totally off these messages.

With the following line of code you can switch off all trace messages from the Host.TraceSink property. If you want to do that, you can put the lines of code if the constructor of your own plug-in.

#if !DEBUG
            Host.TraceSink.Listeners.Clear();
#endif

If you want to reduce the number of output you can set a TraceSwitch to the Host.TraceSink.SourceSwitch property.

Host.TraceSink.Switch = new SourceSwitch("ErrorSwitch", "Error");

By doing that you can control what messages will displayed. Following levels you can use to reduce the message output:

  • Error – see only error trace messages
  • Warning – see only error and warning trace messages
  • Information – see only error, warning and information trace messages
  • Verbose – see all.

 

GunnarD

On the discussion part of the SpicIE home page (http://code.msdn.microsoft.com/SpicIE/Thread/List.aspx) I found an interesting question “Constructor is not called…”.

The reason for this scenario is following. If you build IE extensions with SpicIE you finally constructs COM objects which Internet Explorer creates for you.

In your code these classes seems to be normal objects. But from IE perspective this classes are COM objects which are created only once per IE process room. To understand how Internet Explorer 7/8 handles processes and tabs I recommend you this reading about “Loosely-coupled Internet Explorer Internet Explorer” --> http://code.msdn.microsoft.com/ie8whitepapers/Release/ProjectReleases.aspx?ReleaseId=565

So in your own SpicIE code exists two categories of classes:

  • Internet Explorer extension classes
  • Normal .NET classes

The Internet Explorer extension classes have following characteristics:

  • Any Internet Explorer extension class has a ProgID and a GUID
  • Internet Explorer creates the COM object and the corresponding .NET class instance once for you
  • To get access to the object instance you should use the “HostInstance” pattern
  • You can’t control from your code at what time Internet Explorer creates your objects. This means for instance: it could happen that an invisible toolbar has no instance variable until he will be visualized

Here follows a short sample of the HostInstance pattern which could be used to access the Internet Explorer extension class instances.

    public partial class HtmlTreeToolbar : SpicIE.Controls.Toolbar
    {
        public static HtmlTreeToolbar toolbarInstance;

Assignment of the instance in the object constructor:

        public HtmlTreeToolbar()
        {
            try
            {
#if DEBUG
                Host.TraceSink.TraceInformation(string.Format("HtmlTreeToolbar.HtmlTreeToolbar - DEBUG {0} - {1}",
                    System.Reflection.Assembly.GetExecutingAssembly().FullName, DateTime.Now.TimeOfDay.ToString()));
#else
                 Host.TraceSink.TraceInformation(string.Format("HtmlTreeToolbar.HtmlTreeToolbar - RELEASE {0} - {1}",
                    System.Reflection.Assembly.GetExecutingAssembly().FullName, DateTime.Now.TimeOfDay.ToString()));
#endif

                toolbarInstance = this;

Finally usage of the instance variable to access functionality of the Internet Explorer extension class instance:

 

    void PageHtmlTree_OnDocumentComplete(object pDisp, ref object url)
        {
            if (HtmlTreeToolbar.toolbarInstance != null)
            {
                HtmlTreeToolbar.toolbarInstance.StartDisplayNewPageTree( (url!=null) ? url.ToString() : string.Empty );   
            }
        }

GunnarD

Jeder, der in seinem Leben einmal Software entwickelt hat, kam bestimmt irgendwann an einen Punkt, dass nach einer harmlosen Änderung im Quellcode, der Installation einer x-beliebigen Softwarekomponente oder dem Ändern eines Konfigurationsparameters, der sonst nie Seiteneffekte mit sich brachte, die Welt plötzlich Kopf steht: Anwendungen stürzen ab, Unit Test produzieren nur noch rote Balken oder auch nach mehrmaligen Redeployment mit offensichtlichen Änderungen à la MessageBox.Show() verhält sich eine Anwendung stets identisch.

Diese seltsamen Phänomene bezeichne ich als Probleme aus der Bizarro Welt. Ein Problem aus de Bizarro Welt weist einige interessante Eigenschaften auf:

  • Die Lösung (oder ein Workaround) für ein solches Problem ist vergleichsweise simpel zu realisieren.
  • Die Erklärung der jeweiligen Ursache für ein solches Problem ist vergleichsweise kompliziert, so dass sie niemand wirklich Lust auf eine Erklärung hat (die Lösung ist ja simpel…).
  • Eine Lösung oder ein Workaround für das Problem ist im Web nicht aufzuspüren – Live Search wie auch Google lassen den Suchenden ratlos zurück, weil man entweder gar nichts zu den anwendbaren Stichworten findet oder die Suchergebnisse nutzlos sind und im schlimmsten Fall sogar in die Irre führen.

Die letztgenannte Eigenschaft ist besonders tückisch, da viele Probleme aus der Bizarro Welt augenscheinlich Tausende von Entwicklern betreffen oder von den jeweiligen Technologie-Anbietern als Bugs/Issues/FAQ dokumentiert sein sollten – das ist zumindest immer mein erster Gedanke.

Damit diese bemerkenswerte Gattung von technischen Problemenfällen endlich eine hinreichende Würdigung erhält, möchte ich eine Blog-Serie über (meine) Probleme aus Bizarro Welt starten. Den Auftakt macht dabei ein Problem, das ASP.NET MVC-Entwickler ereilen kann, die eine RESTful Architektur verwenden wollen.

Das Phänomen

Gegeben ein RESTful entworfener Controller wie dieses Mini-Beispiel, dessen MVC-Anwendung unter IIS 7 läuft…

   1:  public class RestController : Controller
   2:  {
   3:      private ActionResult CoreAction(int id)
   4:      {
   5:          RestViewModel model = new RestViewModel()
   6:          {
   7:              Id = id,
   8:              Action = Request.HttpMethod.ToUpperInvariant()
   9:          };
  10:          return Content(model.ToString());
  11:      }
  12:   
  13:      //
  14:      // GET: /Rest/Get/1
  15:   
  16:      [AcceptVerbs(HttpVerbs.Get)]
  17:      public ActionResult Get(int id)
  18:      {
  19:          return CoreAction(id);
  20:      }
  21:   
  22:      //
  23:      // POST: /Rest/Post/1
  24:   
  25:      [AcceptVerbs(HttpVerbs.Post)]
  26:      public ActionResult Post(int id)
  27:      {
  28:          return CoreAction(id);
  29:      }
  30:   
  31:      //
  32:      // PUT: /Rest/Put/1
  33:   
  34:      [AcceptVerbs(HttpVerbs.Put)]
  35:      public ActionResult Put(int id)
  36:      {
  37:          return CoreAction(id);
  38:      }
  39:   
  40:      //
  41:      // DELETE: /Rest/Delete/1
  42:   
  43:      [AcceptVerbs(HttpVerbs.Delete)]
  44:      public ActionResult Delete(int id)
  45:      {
  46:          return CoreAction(id);
  47:      }
  48:  }

 

… warum fahren Clients, die PUT oder DELETE Requests senden, mit The remote server returned an error: (405) Method Not Allowed an die Wand?

405

An unserem Beispielcode kann es ja wohl kaum liegen, oder?

Die Lösung

In diesem Fall heißt der Schuldige WebDAV. Genauer gesagt ist es das WebDAV HTTP-Modul, das auch wenn das WebDAV-Feature selbst deaktiviert ist, sich trotzdem alle PUT- und DELETE-Requests schnappt und überprüft. Wenn aber WebDAV nicht aktiviert ist, verwirft es PUT und DELETE, ohne dass unser Controller den Request zu sehen bekommt.

Auch das Aktivieren des Feature ist hier nutzlos, da das Modul dann PUT und DELETE (vergeblich) zu verarbeiten versucht – wieder bleibt unser Controller bei der Request-Verarbeitung außen vor.

Die Lösung in diesem Fall ist simpel: Das Modul muss in der Web.config der MVC-Applikation deaktiviert werden.

   1:  <system.webServer>
   2:      <validation validateIntegratedModeConfiguration="false"/>
   3:      <modules runAllManagedModulesForAllRequests="true">
   4:          <remove name="WebDAVModule" />
   5:        ...
   6:      </modules>
   7:      ...
   8:  </system.webServer>

Der Effekt tritt übrigens sowohl bei Version 7.0 als auch 7.5 der WebDAV-Extension für IIS 7 auf.

Und wer WebDAV gar nicht installiert hat? Ja, er oder sie sollte auch nicht auf dieses Problem stoßen – oder wurde Opfer eines ganz anderen Effekts aus der Bizarro Welt…

Jörg

There is a new project at http://code.msdn.microsoft.com/SpicIEContrib. This project hosts HowTo Samples, Code, Guidance, … related to the SpicIE Framework

If you actually start using SpicIE you should have a look at the Samples.

Regards … GunnarD

More Posts Next page »
 
Page view tracker