Welcome to MSDN Blogs Sign in | Join | Help

In meinem vorherigen Blogpost habe ich beschrieben wie man mit einem http module dem Upload und Download Mechanismus von Dateien in SharePoint auf die Spur kommt.

Hier eine Übersicht der Ergebnisse:

Upload mit Office 2007 HTTP Post (Frotpage RPC - put document)
Download mit Office 2007 HTTP Post (Frontpage RPC - get document)
Upload über Web UI HTTP Post (upload.aspx)
Download über Web UI HTTP Get (direkte Dokumenten-URL)
Upload über Windows Explorer HTTP Put (direkte Dokumenten-URL)
Download über Windows Explorer HTTP Get (direkte Dokumenten-URL)
Upload über Objektmodell (SPFiles.Add) HTTP Post

Bei den Requests über Frontpage RPC ist zu beachten dass der eigentliche Request zunächst die Metadaten des Dokuments enthält., dann ein non printable character (0x0A) - also 10Hex und dann das eigentliche Dokument.

Das wäre nun eine wunderbare Stelle die Dokumente serverseitig zu manipulieren, allerdings at your own risk

Grüße

Sven

Bei den Recherchen zum Thema Dateiverschlüsselung hat sich mir die Frage gestellt wie der Upload und Download von Dateien in Dokumentenbibliotheken auf Webserver Ebene handhabt.

Um hier weiter zu kommen habe ich mir ein http module geschrieben das einfach die wichtigsten Daten der Requests in eine Logdatei schreibt - hier der Quellcode der Klasse:

using System;
using System.Web;
using System.Diagnostics;

namespace LogWriter 
{
    /// <summary>
    /// 
    /// </summary>
    public class LogModule : IHttpModule
    {
        System.IO.StreamWriter logWriter;
        HttpApplication applicationContext;
        EventHandler beginRequestEventHandler;
        EventHandler endRequestEventHandler;
        String logName;

    public void Init(HttpApplication context)
    {
      //Store file name for later use.
      logName = context.Context.Request.PhysicalApplicationPath + "log.txt";

      //Open file and write information.
      logWriter = new System.IO.StreamWriter(logName, true);
      logWriter.WriteLine("Date :" + System.DateTime.Now.ToString("MM.dd.yyyy HH:mm:ss"));
      logWriter.WriteLine("Event : Init");
      logWriter.Flush();
      logWriter.Close();

      //Store for access in other events.
      applicationContext = context;
      beginRequestEventHandler = new System.EventHandler(this.OnBeginRequest);
      applicationContext.BeginRequest += beginRequestEventHandler;
      endRequestEventHandler = new System.EventHandler(this.OnEndRequest);
      applicationContext.BeginRequest += endRequestEventHandler;
    }

    public void Dispose()
    {

      //Open file and write information.
      logWriter = new System.IO.StreamWriter(logName, true);
      logWriter.WriteLine("");
      logWriter.WriteLine("Date :" + System.DateTime.Now.ToString("MM.dd.yyyy HH:mm:ss"));
      logWriter.WriteLine("Event : Dispose");
      logWriter.WriteLine("");
      logWriter.Flush();
      logWriter.Close();

      //Clean-up.
      applicationContext.BeginRequest -= beginRequestEventHandler;
      beginRequestEventHandler = null;
      applicationContext.EndRequest -= beginRequestEventHandler;
      beginRequestEventHandler = null;
    }

    protected void OnBeginRequest(object sender, EventArgs e)
    {
      //Open file and write log information.
      logWriter = new System.IO.StreamWriter(logName, true);
      logWriter.WriteLine("");
      logWriter.WriteLine("Date :" + System.DateTime.Now.ToString("MM.dd.yyyy HH:mm:ss"));
      logWriter.WriteLine("Event : Application_BeginRequest ");
      logWriter.WriteLine("HTTP :" + applicationContext.Request.HttpMethod.ToString());
      logWriter.WriteLine("UserAgent : "  + applicationContext.Context.Request.UserAgent);
      logWriter.WriteLine("HostAddress : " + applicationContext.Context.Request.UserHostAddress);
      logWriter.WriteLine("HostName : " + applicationContext.Context.Request.UserHostName);
      logWriter.WriteLine("SiteURL : " + applicationContext.Context.Request.Url.ToString());
      if (applicationContext.Request.CurrentExecutionFilePath != null)
      {
        logWriter.WriteLine("File : " + applicationContext.Request.CurrentExecutionFilePath.ToString());
      };
      logWriter.WriteLine("");
      logWriter.Flush();
      logWriter.Close();
    }
        protected void OnEndRequest(object sender, EventArgs e)
        {
            //Open file and write log information.
            logWriter = new System.IO.StreamWriter(logName, true);
            logWriter.WriteLine("");
            logWriter.WriteLine("Date :" + System.DateTime.Now.ToString("MM.dd.yyyy HH:mm:ss"));
            logWriter.WriteLine("Event : Application_EndRequest ");
            logWriter.WriteLine("HTTP :" + applicationContext.Request.HttpMethod.ToString());
            logWriter.WriteLine("UserAgent : " + applicationContext.Context.Request.UserAgent);
            logWriter.WriteLine("HostAddress : " + applicationContext.Context.Request.UserHostAddress);
            logWriter.WriteLine("HostName : " + applicationContext.Context.Request.UserHostName);
            logWriter.WriteLine("SiteURL : " + applicationContext.Context.Request.Url.ToString());
            if (applicationContext.Request.CurrentExecutionFilePath != null)
            {
                logWriter.WriteLine("File : " + applicationContext.Request.CurrentExecutionFilePath.ToString());
            };
            logWriter.WriteLine("");
            logWriter.Flush();
            logWriter.Close();
        }

    }
   
}

Das Ganze mit Visual Studio 2008 übersetzt und die DLL in den GAC gepackt. In der web.config der SharePoint Webapplication noch folgenden Eintrag hinzugefügt:

        <httpModules>
            <add type="LogWriter.LogModule" name="LogWriteModule"/>
        </httpModules>

Nun kann man in der Logdatei wunderbar verfolgen wie Uploads und Downloads von Dateien funktionieren.

viel Spass

Sven

Beim Arbeiten in InfoPath 2007 Formularen setzt häufig per XPathNavigator die entsprechenden Werte in der Datenquelle des Forumulars.

Für Strings klappt dies auch recht gut mit der SetValue Funktion, und für alles andere gibt es ja die SetTypedValue Funktion.

Dachte ich zumindestens, für einige Datentypen (int, bool) funktioniert dies auch, wenn man jedoch darum geht Datums-Werte zu setzen gibt es eine SchemaValidation Exception mit dem Text "Not implemented".

Lösen läßt sich das Thema indem man das Datum als formatierten-String übergibt, und zwar genau so wie InfoPath ihn gerne hätte.

XPathNavigator myForm = MainDataSource.CreateNavigator();

// Ein Datumsfeld im Formular auswählen
XPathNavigator myDate = myForm.SelectSingleNode("/my:myFields/my:myDate", NamespaceManager);

// Abhandeln des nil-Attributs
DeleteNil(myDate);

// Datums-String im richtigen Format erzeugen (yyyy-mm-dd)
string curDate = DateTime.Today.Year + "-" + DateTime.Today.Month + 
   "-" + DateTime.Today.Day;

// Feldwert setzen
myDate.SetValue(strCurDate);

viel Erfolg

Sven

InfoPath 2007 Formulare sind sehr mächtig um schnell und strukturiert Daten zu erfassen. Eine der von mir sehr häufig verwendeten Elementen ist die Repeating Group um wiederkehrende Daten zu erfassen.

Oft muss man aus der Repeating-Group heraus Werte von Feldern in derselben Repeating Group manipulieren, die ersten Ideen gingen in die Richtung "Hol Dir doch das Parent" sind leider kläglich gescheitert. Nach etwas Recherche, vor allem dieser Artikel war sehr hilfreich war die Lösung dann klar.

Gehen wir von folgendem Beispiel einer Adresseingabe (mehrere Anschriften pro Person) aus:

Blog1

Nun möchte ich nach dem Verlassen der PLZ Felds den Ort dazu per Datenbank ermitteln. Ich bediene mich hier des Changed Events und lege per VSTA etwas Code dahinter.

Hier das Formular:

blog2

Um die Aufgabenstellung "Ermitteln des Ortsnamens aus der PLZ" greife ich im Changed-Event der PLZ Felds mit C# auf die PLZ zu, und ändere dann entsprechend den Ortsnamen.

Hierzu nutze ich die eigebauten Funktionen für den Zugriff auf die Formulardaten XPathNavigator, dieser hat Hilfsfunktionen für Repeating Sections, diese lassen sich wie C# Arrays mit [index] direkt ansprechen, haben aber darüber hinaus auch noch Hilfsfunktionen wie last() oder first() und für unseren Fall sehr wichtig position() zur Verfügung.

In unserem Beispiel heißt das:

XPathNavigator DOM = this.MainDataSource.CreateNavigator();
XPathNavigator nodePLZ= DOM.SelectSingleNode("/my:myFields/my:Adresse[position()]/my:PLZ", this.NamespaceManager);
XPathNavigator nodeOrt = DOM.SelectSingleNode("/my:myFields/my:Adresse[position()]/my:Ort", this.NamespaceManager);
string strPLZ = nodePLZ.Value;
string strOrt = "";

//Ermtteln des Orts z.B. aus einer Datenbank
// Code einsetzen der den String Ort füllt

//Setzen des Ort-Werts im Formular
nodeOrt.SetValue(strOrt);

Durch die Funktion postion() brauche ich mir keine Gedanken drum zu machen in welchem Element der Repeating Section ich mich gerade befinde.

Happy repeating

Sven

In meinem Blogeintrag http://blogs.msdn.com/mtcmuc/archive/2008/10/24/architekturaufbau-einer-multilayer-anwendung-die-mit-dem-persistence-ignorance-poco-adapter-for-entity-framework-v1-efpocoadapter-geschrieben-wurde.aspx habe ich über den Aufbau einer WCF Servicebasierten Architektur gesprochen, deren Businessentities oder Data Transfer Objekte POCOs sind. Diese POCOs haben wir mit dem Kommandozeilen Tool "EFPocoClassGen.exe" generiert und ganz normal mit einem EfPoco Adapter gefüllt. Diese POCO Objekte lassen sich auch in WCF Serviceschnittstellen verwenden.

Wenn man mit dem generierten Adapter ein solches POCO Objekt füllt und versucht das Objekt als DataContract in einem WCF Service zu verwenden, bekommt man beim Ausführen der Servicemethode eine Fehlermeldung die ungefähr so aussieht ...

 

 System.Runtime.Serialization.InvalidDataContractException: Type 'NorthwindPoco.PocoProxies.CustomersProxy' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.  See the Microsoft .NET Framework documentation for other supported types. 
    at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type) 
    at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type) 
    at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type) 
    at System.Runtime.Serialization.DataContract.GetDataContract(RuntimeTypeHandle typeHandle, Type type, SerializationMode mode) 
    at System.Runtime.Serialization.XmlObjectSerializerContext.GetDataContract(RuntimeTypeHandle typeHandle, Type type) 
    at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) 
    at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) 
    at WriteArrayOfCustomersToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract ) 
    at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) 
    at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) 
    at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) 
    at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph) 
    at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph) 
    at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph) 
    at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph) 
    at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph) 
    at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph) 
    at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameters(XmlDictionaryWriter writer, PartInfo[] parts, Object[] parameters) 
    at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest) 
    at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest) 
    at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer) 
    at System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer) 
    at System.ServiceModel.Channels.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer) 

Es handelt sich um eine System.Runtime.Serialization.InvalidDataContractException, die beim Serialisieren des POCOs Objektes erzeugt wird.

Der Grund für diesen Serialiiserungsfehler liegt in dem POCO, das vom Persistence Ignorance Adapter generierten Code gefüllt wurde.

Das Adapter füllt im Defaultmodus die POCO-Objekte mit sogenannten Proxies. Hierzu gibt es mehr in der Dokumentation der Persistence Ignorance Adapters.

Um die POCOs auch in einer WCF Serviceschnittstelle nutzen zu können, muss man folgenden Code verwenden ...

                NorthwindPocoEfContext dbCtx = new NorthwindPocoEfContext();
                dbCtx.EnableChangeTrackingUsingProxies = false;              
                c = (from cust in dbCtx.Customers
                     where cust.CompanyName.StartsWith(searchString)
                     select cust).ToList<BE.Customers>();  

Mit dem Setzen des Properties "dbCtx.EnableChangeTrackingUsingProxies = false;" wird die Benutzung von Proxies verhindert.

 

Viel Spass beim Ausprobieren ... GunnarD

Leider lassen sich die Visual Studio Extensions (VSeWSS 1.2) nicht auf einem Entwicklungsrechner mit Windows Vista oder Windows XP installieren. Wenn jedoch die Notwendigkeit besteht nicht auf dem Server zu entwickeln hat Martin Vollmer eine Lösung entwickelt - Sie finden diese hier

Viel Spass

Sven

Wie ich schon am 22.09. in diesem Blog geschrieben habe, arbeitet das Pattern und Pratices Team an einer neuen Version des Application Architecture Guide.

Seit gestern ist die Beta 1 Version dieses Guides verfügbar. J.D. Meier, der an diesem Projekt mitarbeitet, berichtet hier über die neue Version http://blogs.msdn.com/jmeier/archive/2008/10/27/new-release-patterns-practices-app-arch-guide-2-0-beta-1.aspx.

Der Application Architecture Guide betrachtet auch für verschiedene Anwendungstypen architekturelle Aspekte ...

Und beschreibt eine Referenzarchitektur für verteilte Anwendungen ...

Viel Spass beim Lesen ... GunnarD

Monate der Spekulation und Irrungen über Betriebssysteme im Internet, Cloud Services, Rote Hunde oder Städte in der Schweiz haben soeben ein Ende gefunden: Die Azure Services Platform wurde auf der PDC enthüllt. Mehr dazu unweigerlich in den nächsten Wochen und Monaten auch auf diesem Blog, in der Zwischenzeit bietet die Homepage eine Fülle von Informationen zur Plattform, ihren verschiedenen Diensten und APIs. Und die vielleicht spannendste Erkenntnis des Tages: Azure steht nicht nur .NET-Entwicklern offen, erste SDK-Drops für Java und Ruby stehen bereits zum Download bereit.

Cheers!

Jörg Jooss

In meinem letzten Eintrag habe ich kurz vorgestellt, dass man mit dem Entity Framework und POCOs mit effektiv auf relationale Daten zugreifen kann. Zusammenfassend kann man sagen, dass ich in diesem Eintrag http://blogs.msdn.com/mtcmuc/archive/2008/10/24/mehr-poco-plain-old-clr-objects-und-entity-framework-der-persistence-ignorance-poco-adapter-for-entity-framework-v1.aspx, die Vorteile von POCOs und deren Nutzung mit dem EfPocoAdapter und Entity Framework dargestellt.

In diesem Eintrag möchte ich kurz auf die architekturellen Aspekte der Nutzung dieses Adapters in einer mehrschichtigen Architektur eingehen.

Damit man den EfPocoAdapter erfolgreich in einer mehrschichtigen Architektur einsetzen, kann sollte man die generierten Klassen auf verschiedene Assemblies aufteilen.

Der Grund dafür ist, dass man zwar sehr wohl alle EfPocoAdapter generierten Klassen in einem Assembly belassen kann, aber wenn anderen Architekturschichten die POCOs nutzen will, gibt man immer die gesamte "Datenzugriffs"-Schicht frei. Damit wäre zum Beispiel aus der Benutzeroberfläche dann direkt auf Funktionen des Datalayers zugreifbar. Mit dieser Vorgehensweisen untergräbt man die Idee, die hinter dem Konzept des Schichtenaufbaus einer Architektur steht.

Die Ursache für dieses Szenario ist folgende --> Die POCO Klassen werden in allen Schichten der Architektur gebraucht:

  1. Im Datalayer, um Daten aus der Datenbank in eigenen Properties aufzunehmen oder die eigenen Properties als Daten in die Daten zu speichern - via Entity Framework.
  2. Im Businesslayer werden die POCO Klassen Prüfungen unterworfen, sind Bestandteil von Geschäftsabläufen etc.
  3. Im UI - oder Service Interface - Layer werden die POCOs zu Anzeige oder zum Bearbeiten von Service-Requests benötigt.

Deswegen empfiehlt es sich die generierten POCO Objekte in ein eigenes Assembly zu isolieren.

Die Anwendungsarchitektur für einen mehrschichtigen/multilayer Service sähe dann so aus.

Untitled

Die vom EfPocoAdapter generierten Klassen sind in 2 Assemblies aufgeteilt.

Das Assembly BENorthwindPoco.csproj enthält die eigentlichen POCOs und wird von der Datenzugriffsschicht, Businesslogikschicht und vom Service referenziert und benutzt.

image

Die vom EfPocoAdapter generierten Datenzugriffsmethoden sind in dem NorthwindPoco.csproj Assembly gekapselt und nur die direkt darüber liegende Architekturschicht - der Businesslayer - hat Zugriff auf diese Funktionen.

image

 

Die POCO Klassen sind wie gesagt überall verfügbar und können sogar im Interface des WCF Dienstes verwendet werden. Zum Beispiel so ...

using BE = NorthwindPoco;
namespace SvcNorthwindPoco
{    
    [ServiceContract]
    public interface INorthwindSvc
    {
        [OperationContract]
        BE.Customers GetCustomerByName(string searchString);
    }
}

Die im oben Architekturschaubild dargestellte Lösungsstruktur sieht also als Visual Studio 2008 Solution folgendermassen aus.

image

Viel Spass beim Ausprobieren ... GunnarD

Reminder auf Kalendereinträgen ein- und ausschalten, ohne dabei die eingestellte Zeit zu verlieren schien ein großes Problem zu sein.

Zuständig für das Aktivieren/Deaktrivieren ist die Eigenschaft

ReminderIsSet

des CalendarItemType Objekts.  Wenn ich dies jedoch für sich alleine in einen UpdateTypeRequest packe bekomme ich immer eine Fehler vom WebService zurückgeliefert:
One and only one property can be set ....

Nach etwas Probieren kam ich drauf: Sobald das ReminderIsSet angefasst wird muss ich zusätzlich noch das
ReminderIsSetSpecified
mitgesetzt werden - also eigentlichj das Gegenteil von dem getan werden was die Exception aussagt.

Anbei ein Beispiel aus meinem anderen Blog-Eintrag:

CalendarItemType setCalendar2 = new CalendarItemType();
setCalendar1.ReminderIsSet = true;
setCalendar1.ReminderIsSetSpecified = true;

Happy calendering

Sven

Wie man Einträge in Exchange über die WebServices Schnittstelle anlegt ist in einigen Artikeln schon erwähnt. Link

Wer schon einmal versucht hat diese Einträge per WebService zu ändern kann unter Umständen verzweifeln. Es sieht zunächst so aus als könne man pro Update-Request nur eine Eigenschaft ändern.

Im Prinzip funktioniert ein Update so:

  • ExchangeServiceBinding erstellen
  • Eintrag per GetItem oder FindItem ermitteln
  • Einen UpdateItemRequest mit den Änderungen zusammenbauen und wegschicken


Als Basis für einen solchen Request (in unserem Fall Appointments im Kalender) wird ein CalendarItemType zusammengebaut welches nur die Änderungen enthält:

//Exchange ItemID aus FindItem oder GetItem
ItemIdType itemId = "xxxx";
//ChangeKey aus FindItem oder GetItem
itemId.Id = "yyyy";
itemId.ChangeKey = this.txtChangeKey.Text.ToString();
 
// Objekt für die Änderungen anlegen
CalendarItemType setCalendar1 = new CalendarItemType();
setCalendar1.ReminderMinutesBeforeStart = "0";
 
 
// Feldliste für den Update-Request vorbereiten
 
SetItemFieldType setItemField = new SetItemFieldType();
setItemField.Item = new PathToUnindexedFieldType();
(setItemField.Item as PathToUnindexedFieldType).FieldURI = 
UnindexedFieldURIType.itemReminderMinutesBeforeStart; setItemField.Item1 = setCalendar1; // Update-Request erstellen. UpdateItemType updateItemRequest = new UpdateItemType(); updateItemRequest.ItemChanges = new ItemChangeType[1]; ItemChangeType itemChange = new ItemChangeType(); itemChange.Item = itemId; itemChange.Updates = new ItemChangeDescriptionType[1]; itemChange.Updates[0] = setItemField; updateItemRequest.ItemChanges[0] = itemChange; //und wegschicken updateItemRequest.SendMeetingInvitationsOrCancellations = false; //nur für Kaendereinträge notwendig updateItemRequest.SendMeetingInvitationsOrCancellationsSpecified = true; CalendarItemUpdateOperationType.SendToNone; updateItemRequest.ConflictResolution = ConflictResolutionType.AlwaysOverwrite; UpdateItemResponseType updateItemResponse = esb.UpdateItem(updateItemRequest); // Was kommt zurück ? ItemInfoResponseMessageType responseMessage = updateItemResponse.ResponseMessages.Items[0]
as ItemInfoResponseMessageType;

Wenn man jetzt mehrere Felder im setCalendar1 Objekt setzt und die Feldliste entsprechend aufbaut wird man jedoch keinen Erfolg haben. Foldender Fehler würde vom UpdateItemRequest Call zurückgeliefert:

Property count mismatch

Abhilfe für dieses Problem ist jedoch möglich. Jeder Change (also jede geänderte Eigenschaft) braucht ihr eigenes CalendarItemType Objekt.

Für 2 Eigenschaften sieht der Code dann wie folgt aus:

// Objekt für die Änderungen anlegen
CalendarItemType setCalendar1 = new CalendarItemType();
setCalendar1.ReminderMinutesBeforeStart = "0";
 
// Feldliste für den Update-Request vorbereiten
 SetItemFieldType setItemField = new SetItemFieldType();
setItemField.Item = new PathToUnindexedFieldType();
(setItemField.Item as PathToUnindexedFieldType).FieldURI = UnindexedFieldURIType.itemReminderMinutesBeforeStart;
setItemField.Item1 = setCalendar1;
            
// 2. CalendarItemType Objekt für die 2. Eigenschaft 

CalendarItemType setCalendar2 = new CalendarItemType();
setCalendar1.ReminderIsSet = true;
setCalendar1.ReminderIsSetSpecified = true;

//2. Feld für die Feldliste
SetItemFieldType setItemField2 = new SetItemFieldType();
setItemField2.Item = new PathToUnindexedFieldType();
(setItemField2.Item as PathToUnindexedFieldType).FieldURI = UnindexedFieldURIType.itemReminderIsSet;
setItemField2.Item1 = setCalendar2;

// Update-Request erstellen.
UpdateItemType updateItemRequest = new UpdateItemType();
updateItemRequest.ItemChanges = new ItemChangeType[1];
ItemChangeType itemChange = new ItemChangeType();
itemChange.Item = itemId;

//Jetzt sind es 2 Felder / also auch 2 Änderungen
itemChange.Updates = new ItemChangeDescriptionType[2];
itemChange.Updates[0] = setItemField;
itemChange.Updates[1] = setItemField2;
updateItemRequest.ItemChanges[0] = itemChange;

//und wegschicken
updateItemRequest.SendMeetingInvitationsOrCancellations = 
//nur für Kaendereinträge notwendig
updateItemRequest.SendMeetingInvitationsOrCancellationsSpecified = true;
CalendarItemUpdateOperationType.SendToNone;
 
updateItemRequest.ConflictResolution = ConflictResolutionType.AlwaysOverwrite;
UpdateItemResponseType updateItemResponse = esb.UpdateItem(updateItemRequest);
 
// Was kommt zurück ?
ItemInfoResponseMessageType responseMessage = updateItemResponse.ResponseMessages.Items[0] 
as ItemInfoResponseMessageType;

Trickreich aber schön :-)

Happy Calendaring

Sven

 

 

 

 

 

 

 

 

In diesem Blogs habe ich schon 2-mal über die Themen Entity Framework und POCO geschrieben. Aus meiner Sicht sind das interessante Themen, da das Entity Framework mit Linq einen guten und effizient programmierbaren Zugriffsweg auf relationale Daten bietet. Andererseits sind aus architektureller Sicht an vielen Stellen POCOs eine gute Lösung für die Implementierung von Business Entities oder Data Transfer Objects in einer Architektur.

Diese Überlegungen haben auch andere Architekten angestellt und einen sogenannten "Persistence Ignorance (POCO) Adapter for Entity Framework V1" erstellt. Dieser Adapter erlaubt die Verwendung von POCO's mit dem Entity Framework.

In seinem Blogeintrag schreibt Jaroslaw Kowalski über die Motivation und die Grundmechanismen des EfPocoAdapters --> http://blogs.msdn.com/jkowalski/archive/2008/09/09/persistence-ignorance-poco-adapter-for-entity-framework-v1.aspx

Der Code des Adapters (mit Tests und Minisample) kann auf der MSDN Code Seite heruntergeladen werde --> http://code.msdn.microsoft.com/EFPocoAdapter

Da ich bin der Nutzung des Adapters nicht sofort erfolgreich war, und es ein paar kleine Eigenheiten gab, will ich ganz kurz nochmal die Schritte beschreiben, die man machen muss, um den EfPocoAdapter zu nutzen.

Um die speziellen POCOs und den Zugriffsadapter für eine bestimmte Datenbank zu erzeugen, benötigt man ein Entity Framework EDMX Modell File ".EDMX" oder die Entity Framework ".CSDL/.SSDL/.MSDL" Files.

Für mein Sample habe ich mich für die CSDL Variante entschieden. Ein CSDL File für eine Datenbank kann man sich mit dem Tool EdmGen.exe erzeugen lassen. Dem Tool muss ein Connectionstring auf eine Datenbank, ein Zielnamespace und der Modus übergeben werden. Um sich die CSDL/SSDL/MSL Files auf einmal erzeugen zu lassen, startet man das EdmGen.exe Tool am besten mit dem "Fullgeneration" Modus.

Die Kommandzeile sieht so aus ...

C:\Windows\Microsoft.NET\Framework\v3.5\edmgen.exe 
/connectionstring:"Data Source=(local);Initial Catalog=Northwind;Integrated Security=True" 
/namespace:NorthwindPoco 
/mode:FullGeneration 
/project:NorthwindPocoEf

Nach diesem Aufruf hat man die notwendigen Entity Framework Files, um mit dem Generator des EfPocoAdapaters den notwendigen Code erzeugen zu lassen. Wichtig: Die erzeugten Files ".CSDL", ".SSDL" und ".MSL" müssen in das Projekt includiert werden und müssen zum Projekt Output Directory kopiert werden!

image

 

Mit der Kommandozeile ...

c:\SdkSamples\EFPocoAdapter\EFPocoClassGen\bin\Debug\EFPocoClassGen.exe /incsdl:NorthwindPocoEf.csdl 
/mode:PocoClasses 
/outputfile:NorthwindPocoClasses.cs

erzeugt man die PoCo Klassen für die Datenbank. Ein typisches Beispiel für eine so generierte Klassen sind zum Beispiel das ...

   public class CustomerDemographics
    {
        public String CustomerTypeID { get; set; }
        public String CustomerDesc { get; set; }
        public IList<NorthwindPoco.Customers> Customers { get; set; }
    }
    public class Customers
    {
        public String CustomerID { get; set; }
        public String CompanyName { get; set; }
        public String ContactName { get; set; }
        public String ContactTitle { get; set; }
        public String Address { get; set; }
        public String City { get; set; }
        public String Region { get; set; }
        public String PostalCode { get; set; }
        public String Country { get; set; }
        public String Phone { get; set; }
        public String Fax { get; set; }
        public DateTime? LastEditDate { get; set; }
        public DateTime? CreationDate { get; set; }
        public IList<NorthwindPoco.Orders> Orders { get; set; }
        public IList<NorthwindPoco.CustomerDemographics> CustomerDemographics { get; set; }
    }

 

In den fett markierten Code ist erkennbar, dass der EfPocoAdapter nicht nur die Tabellenspalten als Code abbildet, sondern auch im Datenmodel hinterlegte relationale Beziehungen abbildet.

Danach muss man sich den eigentlich, speziellen Adapter generieren mit dem die POCO Klassen via Entity Framework auf die relationalen Daten zugreifenn können. Die Kommandoezeile für diese Generierung ist folgendermassen ...

c:\SdkSamples\EFPocoAdapter\EFPocoClassGen\bin\Debug\EFPocoClassGen.exe /incsdl:NorthwindPocoEf.csdl 
/mode:PocoAdapter 
/outputfile:NorthwindPocoAdapterClasses.cs

Damit kann man jetzt schon die Funktionalität des EfPocoAdapters nutzen. Optional kann man sich noch einen Container, zum zentralen Zugriff auf alle POCO Klassen des Datenmodells erstellen lassen. Das geht mit der Kommandozeile ...

c:\SdkSamples\EFPocoAdapter\EFPocoClassGen\bin\Debug\EFPocoClassGen.exe 
/incsdl:NorthwindPocoEf.csdl 
/mode:PocoContainer 
/outputfile:NorthwindPocoContainerClasses.cs

Der hier erzeugte Sourcecode für POCO Klassen, den Adapter und optional für den Container muss man ebenfalls in das betreffende Projekt aufnehmen und eine Referenz auf das EfPocoAdapter Assembly setzen. Eine Referenz auf "System.Data.Entity" braucht man übrigens auch.

image

Wenn diese POCO-Klassen in eine Library verpackt werden, muss man auch im aufrufenden Assembly, z.B. Webservice- oder UI-Projekt diese beiden Referenzen setzen. In der Config Datei, die das so erstellt Assembly nutzt, muss man jetzt noch folgenden Connectionstring zur Verfügung stellen.

 

  <connectionStrings>
    <add name="NorthwindPocoEfContext" 
         connectionString="metadata=NorthwindPocoEf.csdl|NorthwindPocoEf.ssdl|NorthwindPocoEf.msl;
            provider=System.Data.SqlClient;provider connection string=&quot;Data Source=(local);Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>

Wichtig: Im Bereich Metadata müssen die erzeugten ".CSDL", ".SSDL" und ".MSL" Files eingetragen werden!

Und nun kann man die POCO mit Adapter zum Zugriff auf die relationalen Daten verwenden; - ungefähr so ...

       try
            {
                NorthwindPocoEfContext nwCtx = new NorthwindPocoEfContext();
                List<NorthwindPoco.Customers> lstCustomers =
                    (from c in nwCtx.Customers
                     where c.CompanyName.StartsWith(tbxCustomerName.Text)
                     select c).ToList<NorthwindPoco.Customers>();
                lvwResults.ItemsSource = lstCustomers;
                if (lstCustomers.Count > 0)
                {
                    if (!nwCtx.IsPropertyLoaded(lstCustomers[0], c => c.Orders))
                    {
                        nwCtx.LoadProperty(lstCustomers[0], c => c.Orders);
                    }
                    foreach (Orders o in lstCustomers[0].Orders)
                    {
                        System.Diagnostics.Trace.WriteLine(o.OrderID);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }

 

Okay, das war mein Weg zu meinen ersten selbst erstellten POCO Adpater mit dem "Persistence Ignorance (POCO) Adapter for Entity Framework V1".

Viel Spass beim Ausprobieren ... Gunnard

 SLLogo_thumb_375BA17AHeute ist der Tag an dem Silverlight 2.0 released wird. Silverlight ist ein Cross-Browser Plugin, mit dem man reichhaltige, interaktive Internetanwendungen erstellen kann. Das Silverlight Browser Plugin kann in verschiedenen Browsern auf verschiedenen Plattformen gehostet werden. Die Silverlight kompatible Version für verschiedene Linux Varianten heisst "Moonlight".

Interessant an Silverlight 2.0 ist, dass in dieser Version eine sogenannte Mini CLR (=Common language runtime) integriert ist. D.h. man kann diese reichhaltigen, interaktiven Browseranwendung mit managened Sprachen, zum Beispiel in C#, entwickeln. Als Entwicklungsplattform kann man Visual Studio 2008 benutzen. Dazu gibt es weitere Downloads, die in Visual Studio 2008 ein neues Projekttemplate speziell für Silverlight-Projekte. Mit dieser Visual Studio 2008 Erweiterung kann man Silverlight "Applets"-Browser  basierende  erstellen, debuggen, etc.. Zur Unterstützung bei der Erstellung von Oberflächen wird es eine Expression Version geben, mit dem man Silverlight XAML komfortabel bearbeiten kann.

Selbst wenn man nicht vorrangig an extrem gut gestalteten Webanwendungen interessiert ist, kann Silverlight die Web-Entwickler unterstützen. Mit Silverlight kann man sehr schnell, einfach, mit guter Toolunterstützung Browser basierte Anwendungen erstellen, ohne dabei alles in HTML und JavaScript entwickeln zu müssen. Vorhandenes Wissen von C# und WPF vereinfachen den Einstieg in die Silverlight Welt.

--> Also definitiv eine Sache, die für einen Entwickler und Architekten interessant ist.

Die offizielle Seite zu Silverlight ist hier: http://www.microsoft.com/silverlight

Die Seite, die man als Entwickler und Designer besuchen sollte ist: http://www.silverlight.net

 

Viel Spass  beim Ausprobieren ... GunnarD

Als flexible und platformübergreifende Methode auf Exchange Mailbox Daten (Termine, Kalender, Mails, Kontakte) zuzugreifen haben sich seit Exchange 2007 die Exchange Webservices etabliert.

Die meisten vorhandenen Beispiele zeigen die Verwendung der Webservices für die eigene Mailbox. Ich hatte jetzt jedoch die Anforderung Einträge in Kalendern von anderen Mailboxen vorzunehmen. Voraussetzung hierfür ist natürlich dass mein Benutzer Zugriff auf die andere Mailbox hat - dies wurde in unserem Fall mit Outlook über die Delegates-Funktion eingerichtet (Tools + Options + Delegates)

Als Basis findet hier das Beispiel aus MSDN - Kalendereintrag erzeugen

Dies zeigt recht schön wie das für die eigene Mailbox geht. Wenn man sich genauer ansieht wo das Ziel der Operation festgelegt wird kann man sehr leicht sehen dass hier die DistinguishedFolderID des eigenen Kalenderordners ermittelt wird.

    // Identify the destination folder that will contain the appointment.
    DistinguishedFolderIdType folder = new DistinguishedFolderIdType();
    folder.Id = DistinguishedFolderIdNameType.calendar;

es geht nun also nur noch darum die DistinguishedFolderID des Kalenders einer anderen Mailbox herauszufinden - das geht so:

DistinguishedFolderIdType[] folder = { new DistinguishedFolderIdType() };
folder[0].Id = DistinguishedFolderIdNameType.calendar;
folder[0].Mailbox = new EmailAddressType();
folder[0].Mailbox.EmailAddress = emailAddressOfOtherPerson;

CreateItemType createItemRequest = new CreateItemType();
createItemRequest.SavedItemFolderId = new TargetFolderIdType();
createItemRequest.SavedItemFolderId.Item = folder;

Beides kann man nun kombinieren und kann somit Einträge in anderer Leute Kalender vornehmen - sehr elegant wie ich finde.

Das Ganze ist natürlich vom Konzept her auch auf andere Objekte (Mails, Aufgaben, Kontakte) übertragbar)

 

viel spass beim Probieren

Sven

Hallo zusammen,

in meinen vorherigen Blog-Posts habe ich gezeigt habe ich gezeigt wie man an die Property-Bags einer Site herankommt. Link 

nun zu einer praktischen Anwendung - ihr kennt sicher alle den Link in den Sitecollection Settings bei dem man die Masterpages auch für bestehende Sites setzen kann. Das alleine geht sehr einfach mit dem MasterUrl Property des SPWeb Objekts, jedoch muss ja zuvor die Vererbung der Masterpages gebrochen werden, und das kann nur mit der Eigenschaft __InheritsCustomMasterUrl der Site-Property-Bag erfolgen.

MasterPages

Das u.g. Codesnippet bricht zunächst die Einstellung der Masterpage-Vererbung und setzt danach eine eigene Masterpage (myown.master)

SPSite site = new SPSite("http://moss.litwareinc.com"); 
SPWeb web = site.OpenWeb("/sites/proposal"); 
web.AllProperties["__InheritsCustomMasterUrl"] = "False"; 
web.MasterUrl = "/_catalogs/masterpage/myown.master";
web.Update();

Viel Spass mit dem neuen Look & Feel der Sites

Sven

More Posts Next page »
 
Page view tracker