Blog Page

CRM 2011 – Metadaten auslesen (z.B. von Picklist)

CRM hat die Angewohnheit, dass es nicht den selektierten Wert (z.B. in einer Picklist) speichert, sondern den dahinterliegenden (internen) Code. Das mag durchaus Sinn machen, erschwert einem aber die Arbeit, wenn man programmatisch etwas bearbeiten möchte

Nachfolgend eine Funktion, welche die Metadaten entsprechend umwandelt, also z.B. selektierter Text in die interne Nummer.

[sourcecode language=“csharp“]
/// <summary>
/// Gets the metadata value.
/// </summary>
/// <param name="organization">The organization.</param>
/// <param name="entity">The entity.</param>
/// <param name="attribute">The attribute.</param>
/// <returns></returns>
public static string GetMetadataValue(contracts.IOrganization organization,Entity entity, string attribute)
{

IOrganizationService _service = OrgService(organization);
RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest
{
EntityLogicalName = entity.LogicalName,
LogicalName = attribute,
RetrieveAsIfPublished = true
};

RetrieveAttributeResponse attMetadata = (RetrieveAttributeResponse)_service.Execute(attributeRequest);
object oAttribute = entity[attribute];

string sReturn = string.Empty;
if (oAttribute.GetType().Equals(typeof(Microsoft.Xrm.Sdk.OptionSetValue)))
{

OptionMetadata[] optionList = null;
// Access the retrieved attribute.
//’Microsoft.Xrm.Sdk.Metadata.PicklistAttributeMetadata‘
if (attMetadata.AttributeMetadata.GetType().FullName.Contains("PicklistAttributeMetadata"))
{
PicklistAttributeMetadata retrievedPicklistAttributeMetadata =
(PicklistAttributeMetadata)attMetadata.AttributeMetadata;
// Get the current options list for the retrieved attribute.
optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();
}
else if (attMetadata.AttributeMetadata.GetType().FullName.Contains("StatusAttributeMetadata"))
{
StatusAttributeMetadata retrievedPicklistAttributeMetadata =
(StatusAttributeMetadata)attMetadata.AttributeMetadata;
// Get the current options list for the retrieved attribute.
optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();
}
else if (attMetadata.AttributeMetadata.GetType().FullName.Contains("StateAttributeMetadata"))
{
StateAttributeMetadata retrievedPicklistAttributeMetadata =
(StateAttributeMetadata)attMetadata.AttributeMetadata;
// Get the current options list for the retrieved attribute.
optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();
}
else
return string.Empty;
// get the text values
int i = int.Parse((oAttribute as Microsoft.Xrm.Sdk.OptionSetValue).Value.ToString());
for (int c = 0; c < optionList.Length; c++)
{
OptionMetadata opmetadata = (OptionMetadata)optionList.GetValue(c);
if (opmetadata.Value == i)
{
sReturn = opmetadata.Label.UserLocalizedLabel.Label;
break;
}
}

}
else if (oAttribute.GetType().Equals(typeof(Microsoft.Xrm.Sdk.Money)))
{
sReturn = (oAttribute as Microsoft.Xrm.Sdk.Money).Value.ToString();
}
else if (oAttribute.GetType().Equals(typeof(Microsoft.Xrm.Sdk.EntityReference)))
{
sReturn = (oAttribute as Microsoft.Xrm.Sdk.EntityReference).Name;
}
else
{
sReturn = oAttribute.ToString();
}
if (sReturn == null || sReturn.Length == 0)
sReturn = "No Value";
return sReturn;
}

[/sourcecode]

Ich hoffe, ich konnte euch damit helfen 🙂

Cheers,
Chris

WCF/Soap Service aus Javascript konsumieren

In einem aktuellen Projekt habe ich die Aufgabe bekommen, ein WCF Service zu erstellen, welches dann mittels Javascript konsumiert werden können soll.
Doch ein SOAP-Service aus Javascript konsumieren ist eine recht aufwendige Geschichte und die gefundenen Tutorials im Internet oft alles andere als einfach verstÀndlich.

Um das Service anzusprechen, verwende ich einen XMLHttpRequest, welchem man einen SoapBody/XML und eine SoapAction mitgeben muss. Um das SOAP-XML nicht selbst produzieren zu mĂŒssen, was ja auch gar nicht so einfach wĂ€re, habe ich SOAP UI (http://www.soapui.org/) verwendet. Dort mĂŒsst Ihr nur das WSDL des Service eingeben und bekommt fĂŒr jede Methode, die euer Service zur VerfĂŒgung stellt, einen Request (welcher das XML enthĂ€lt) erzeugt:

Hier seht Ihr auch schon das XML, welches Ihr in eurer JS-Methode verwenden könnt, z.B.:

[sourcecode language=“xml“]
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:Login>
<!–Optional:–>
<tem:organization>?</tem:organization>
</tem:Login>
</soapenv:Body>
</soapenv:Envelope>
[/sourcecode]

Die SoapAction findet Ihr, wenn Ihr euch euer WSDL anseht.

In meinem Beispiel lautet diese: http://tempuri.org/IAuthentication/Login

Nun geht’s ans Javascript. Ich habe mir hierzu eine kleine Helper-Klasse geschrieben.

[sourcecode language=“javascript“]
var Utils = new function () {

var Request = new XMLHttpRequest();
var fbFunction = null;

this.CallWS = function CallWS(soapHeader, feedbackFunction) {

// Create CRM Request
fbFunction = feedbackFunction;
var url = ‚http://localhost/crm.service/Operation.svc‘;

Request.open("POST", url, true);
Request.setRequestHeader("SOAPAction", soapHeader.SoapAction);
Request.setRequestHeader("Accept", "application/xml");
Request.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
Request.onreadystatechange = WSFinished;

Request.send(soapHeader.SoapXml);
}

function WSFinished() {

if (Request.readyState == 4) {

if (Request.status == 200) {

// parse webservice result
Result.Xml = Request.responseXML;
Result.ParseResult();
}
else {

Result.StatusCode = -2;
Result.StatusMessage = Request.responseText;
}
fbFunction(Result);
}
}
}

var SoapUtils = new function () {

var SoapHeader = new function () {
this.SoapXml = null;
this.SoapAction = null;
}

this.GetSoapHeaderForLogin = function (org) {
SoapHeader.SoapXml = "<soapenv:Envelope xmlns:soapenv=’http://schemas.xmlsoap.org/soap/envelope/‘ xmlns:tem=’http://tempuri.org/‘><soapenv:Header/><soapenv:Body><tem:Login><tem:organization>" + org + "</tem:organization></tem:Login></soapenv:Body></soapenv:Envelope>";
SoapHeader.SoapAction = "http://tempuri.org/IAuthentication/Login";
return SoapHeader;
}

}
[/sourcecode]

Der Aufruf sieht dann wie folgt aus:

[sourcecode language=“javascript“]
var header = SoapUtils.GetSoapHeaderForLogin("xyz");
Utils.CallWS(header, LoginFinished);
[/sourcecode]

Nachdem ich diese Webservice-Methoden mehrmals in meinem Projekt aufrufen muss, habe ich das SoapXML und die SoapAction in eine eigene Methode gepackt (SoapUtils), welche ich mittels SoapUtils.GetSoapHeaderForLogin("xyz"); aufrufe. Die zurĂŒckgelieferte Informationen gebe ich dann der CallWS-Methode mit (Utils.CallWS(header, LoginFinished);) und gebe zudem als zweiten Parameter eine Methode bekannt, welche aufgerufen werden soll, sobald der Webservice-Call fertig ist.

Wichtig: Ein Webservice-Aufruf ist immer asynchron! Es gibt zwar Spielereien (Ă€hnlich Thread.Sleep() in C#), diese sollten jedoch erfahrungsgemĂ€ĂŸ nicht verwendet werden.

Wenn Ihr euch den Source-Code genauer anseht, werdet Ihr feststellen, dass ich in der Methode „CallWS“ dem Request.onreadystatechange-Event subscribed habe. In der Methode WSFinished() prĂŒfe ich nun State des Requests und Status des Requests und erzeuge ein Result-Objekt, welches ich der Feedback-Funktion (ĂŒbergeben bei CallWS) retour liefere. Hier seid Ihr selbst gefordert, euer Webservice wird naturgemĂ€ĂŸ andere Resultate erzeugen als meines.

Ich hoffe, ich konnte euch ein wenig weiterhelfen 🙂

Cheers,
Chris

Microsoft Unity 2.0 – Einstieg leicht gemacht

Statische Referenzen auf Assemblies haben Vor- und Nachteile. Wer im Zeitalter von dynamischen Systemen und verteilten Anwendungen flexibel sein möchte nutzt oft Dependency Injection Frameworks. Unity ist so eines.

Unity befindet sich aktuell in Version 2.0. Hier findet Ihr den Download: http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9093

In einem neuen Projekt habe ich die Aufgabe gestellt bekommen, ein manndantenfĂ€higes Webservice zu entwickeln, welches unabhĂ€ngig vom Manndanten, bestimmte Methoden zur VerfĂŒgung stellen soll. Es soll Methoden geben, welche fĂŒr alle Manndanten gleich sind, aber auch Spezialimplementierungen fĂŒr einen bestimmten Manndanten zulĂ€sst.

Nachdem ich möglichst flexibel sein wollte, habe ich mir folgende Architektur ĂŒberlegt:

Wie man sieht, kennt das Webservice lediglich die VertrĂ€ge/Interfaces. Unity soll fĂŒr mich entscheiden, welche Implementierung (KundeA, KundeB, KundeDefault) „ziehen“ soll. Man könnte natĂŒrlich auch noch KundeA und KundeB (die Spezialimplementierungen) auf KundeDefault referenzieren lassen, habe ich aber vorlĂ€ufig nicht gemacht.

Um Unity einzubinden, muss man dem ausfĂŒhrenden Projekt (in meinem Fall „WcfService“) folgende Assemblies hinzufĂŒgen: Microsoft.Practices.Unity und Microsoft.Practices.Unity.Configuration.

In der web.config habe ich eine eigene Section eingerichtet und lasse diese auf die Datei Unitiy.config verweisen:

[sourcecode language=“xml“]
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>
<unity configSource="Unity.config" />
[/sourcecode]

In der Unity.config habe ich folgende Zeilen hinzugefĂŒgt:

[sourcecode language=“xml“]
<?xml version="1.0" encoding="utf-8" ?>
<unity>
<typeAliases>

<typeAlias alias="IVerkaufschance"
type="Contracts.IVerkaufschance, Contracts" />

<typeAlias alias="VerkaufschanceKundeDefault"
type="KundeDefault.Verkaufschance, KundeDefault" />

<typeAlias alias="VerkaufschanceKundeA"
type="KundeA.Verkaufschance, KundeA" />

<typeAlias alias="VerkaufschanceKundeB"
type="KundeB.Verkaufschance, KundeB" />

</typeAliases>

<containers>
<container>
<types>

<!– Default (un-named) mapping for IVerkaufschance to VerkaufschanceKundeA –>
<type type="IVerkaufschance" mapTo="VerkaufschanceKundeDefault"></type>

<!– Named mapping for IVerkaufschance to VerkaufschanceKundeA –>
<type type="IVerkaufschance" mapTo="VerkaufschanceKundeA" name="KundeA"></type>

<!– Named mapping for IVerkaufschance to VerkaufschanceKundeB –>
<type type="IVerkaufschance" mapTo="VerkaufschanceKundeB" name="KundeB"></type>

</types>
</container>
</containers>
</unity>
[/sourcecode]

Wie man sieht, habe ich drei Typen angelegt. Die Default-Implementierung (un-named mapping, hier fehlt das „name“-Attribut) fĂŒr den Vertrag „IVerkaufschance“ und die Spezialimplementierungen fĂŒr KundeA und KundeB (named mappings). Zudem muss man natĂŒrlich die Typen fĂŒr Vertrag und Implementierungen bekannt geben, dies geschieht ĂŒber die TypeAliases.

Wie man im Architektur-Diagramm erkennt, hat das WcfService keine Kenntnis ĂŒber die Implementierungen fĂŒr Default, KundeA und KundeB. Daher ist es notwendig, in den jeweiligen Projekten, im Post-Build-Event die erzeugte Assembly in das bin-Verzeichnis der ausfĂŒhrenden Applikation (WcfService) zu kopieren. Das geht am einfachsten mittels XCOPY

xcopy "$(SolutionDir)KundeAbindebugKundeA.dll" "$(SolutionDir)WcfServicebin" /Y

Im Code mĂŒsste man nun die Unity-Container (in meinem Fall gibt es nur einen) nach den entsprechenden Typen durchsuchen. Um das zu vereinfachen, habe ich eine Helperklasse geschrieben:

[sourcecode language=“csharp“]
public interface IContainerAccessor
{
IUnityContainer Container { get; }
}

public class Dependency
{
public static void Register(IUnityContainer container)
{
UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
config.Containers.Default.Configure(container);
}

public static T Resolve<T>()
{
return Container.Resolve<T>();
}

public static T Resolve<T>(object name)
{
try
{
return Container.Resolve<T>(name.ToString());
}
catch (ResolutionFailedException)
{
return Container.Resolve<T>();
}
}

private static IUnityContainer _container;
public static IUnityContainer Container
{
get
{
// Testing only – see Set method below
if (_container != null) return _container;

var context = HttpContext.Current;

// The most likely situation where this occurs is because we are on a different thread. These threads
// are not part of the ‚current‘ context, hence explosion
if (context == null)
{
try
{
IUnityContainer c = new UnityContainer();
Register(c);
return c;
}
catch
{
// When running from the TEST projects, the above fails.
return null;
}
}

// Get it from global. This is cached in the Application, so is the ideal scenario
var accessor = context.ApplicationInstance as IContainerAccessor;
if (accessor == null)
{
return null;
}

var container = accessor.Container;

if (container == null)
{
throw new InvalidOperationException("No Unity container found");
}

return container;
}

// Only for testing purposes
set
{
_container = value;
}
}

}
[/sourcecode]

Meinem Webserviceprojekt (WcfService) habe ich nun ein ein Wcf Service hinzugefĂŒgt, welches ich „Verkaufschance“ benannt habe. Im generierten Code der Verkaufschance.svc.cs-Klasse habe das Interface auf „contracts.IVerkaufschance“ abgeĂ€ndert und das generierte Interface weggeschmissen und die Copy-Funktion, welche im Interface definiert ist, implementiert.

[sourcecode language=“csharp“]
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Verkaufschance" in code, svc and config file together.
public class Verkaufschance : contracts.IVerkaufschance
{
public string Copy(string organization)
{
return Dependency.Resolve<contracts.IVerkaufschance>(organization).Copy("something");
}
}
[/sourcecode]

In der Copy-Funktkion kommt nun Unity zum Einsatz. Mittels Dependency.Resolve löse ich gegen die erzeugte Konfiguration (Unity.config) die entsprechende Assembly auf. Der Parameter „organization“ ist der SchlĂŒssel zu den Named-Mappings. Findet er den SchlĂŒssel nicht, zieht die Default-Implementierung.

Die Implementierung, fĂŒr KundeA, sieht dann wie folgt aus:

[sourcecode language=“csharp“]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace KundeA
{
class Verkaufschance : Contracts.IVerkaufschance
{
#region IVerkaufschance Members

public string Copy(string organization)
{
return "Sonderlösung fĂŒr " + organization;
}

#endregion
}
}
[/sourcecode]

Ich habe somit einen Vertrag (im Contracts-Projekt), welchen ich fĂŒr die Implementierung des Webservices und fĂŒr die eigentliche Implementierung der Methoden verwende. So ist sichergestellt, dass die Implementierung fĂŒr jeden Manndanten gleich ist bzw. die gleichen Methoden zur VerfĂŒgung stellt.

Ich hoffe ich konnte euch damit den Einstieg in Unity etwas erleichtern.

Cheers
Chris

Internal Methods mit Unittests testen

Jeder der anfĂ€ngt sich mit Unittests zu beschĂ€ftigen wird sich wahrscheinlich frĂŒher oder spĂ€ter fragen wie man interne Methoden testen kann. Es ist ganz Einfach!

Ich habe eine Class-Library (Implementation) erstellt und auch Visual Studio 2010 Test Project (API.Test).

Nun gehe zur AssemblyInfo meiner Class-Library und fĂŒge folgenden Code hinzu:

[sourcecode language=“csharp“]
//Set internal methods visible
//API.Test ist der Name der Assembly
[assembly:InternalsVisibleTo("API.Test")]
[/sourcecode]

Nun könnt ihr sÀmtliche internen Methoden im Projekt API.Test aufrufen und testen!

Viel Spass!
Waldi

Code Analyse mit Visual Studio 2010 – Teil 1

„Mein Code ist sauber“ diese Aussage wird sicher jeder
Programmierer schon gehört haben und auch schon mal gesagt haben. Nur leider
trifft das nicht sehr oft zu! Deshalb stelle ich euch kurz die in der Premium
und Ultimate Edition enthaltene Code Analyse vor.

SpĂ€testens wenn ihr in einem Team arbeitet wo die Kollegen ein unterschiedliches Level besitzen werdet ihr die Code Analyse zu lieben anfangen. Auch wenn sie nicht vor allen Dummheiten schĂŒtzt  wird sie euch das Leben  einiges erleichtern.

Ich habe ein Demo Projekt erstellt mit einer Class Library
und eine Demo-Methode. Nun aktivieren wir die Code Analyse:

Im Dropdown stehen schon mehrere rulesets zur VerfĂŒgung ich wĂ€hle
das passende aus und kompiliere mein Projekt, der folgenden(recht dummen) Code enthÀlt:

[sourcecode language=“csharp“]
public int CreateCodingAnalysisError(string input)
{
int rv = 0;
int.TryParse(input.ToString(), out rv);
return rv;
}
[/sourcecode]

Nach dem erfolgreichen kompilieren erscheint folgende Warnung

Jetzt gibt es zwei Möglichkeiten diese Warnung zu beheben. Die erste Möglichkeit ist diese Warnung zu Ignorieren:

Rechtklick auf die Warnung und wĂ€hle Suppress Message und eine der zwei Optionen „In Source“ oder „In Projekt Suppression File“ aus. Ich wĂ€hle die Option „In Source“.

Nachdem ich „In Source“ geklickt habe wird etwas Code hinzugefĂŒgt. Im Grunde sollten keine Warnung ignoriert werden und wenn, ist es ideal wenn sie den Grund dafĂŒr dokumentieren.

Die zweite Möglichkeit ist den Code entsprechend auszubessern Microsoft bietet zu jeder Warnung entsprechende Artikel inkl. ErklÀrungen in der MSDN an, einfach nach der Nummer suchen z.B.: CA 1806. Der korrekte Code sieht wie folgt aus:

[sourcecode language=“csharp“]
public int CreateCodingAnalysisError(string input)
{
int rv = 0;
if (int.TryParse(input.ToString(), out rv))
return rv;
else
return -1;
}
[/sourcecode]

Im Teil 2 zeige ich euch wir ihr euer eigenes ruleset erstellen könnt und welche weiteren Möglichkeiten es gibt.

Viel Spass!

Waldi

Einfacher Import in CRM 2011 von SQL Datenbank (ADO.NET Entity Framework)

Heute hat mich ein typisches Migrationsprojekt erreicht. Eine Marketinganwendung des Kunden soll durch CRM 2011. DafĂŒr ist es zunĂ€chst notwendig, die Daten in CRM zu importieren. Erst danach sollen eventuell fehlende Features, etc. besprochen/entwickelt werden.

Heute zeige ich euch, wie man mittels den von CRM zur VerfĂŒgung gestellten Webservice-Schnittstellen Firmen aus einer SQL-Datenbank möglichst einfach in CRM importieren kann.

Um den Zugriff auf den SQL-Server so einfach wie möglich zu halten, habe ich mich dafĂŒr entschieden auf ADO.NET Entity Data Model zu setzen und habe dem Model sĂ€mtliche Tabellen der Quell-Datenbank hinzugefĂŒgt.

Nun lÀsst sich im C# Code ganz einfach auf die Datenbank zugreifen, z.B.

[sourcecode language=“csharp“]
conventoEntities context = new conventoEntities();
var firmen = (from p in context.Tbl_Firmen
orderby p.Firmenname1
select p);
[/sourcecode]

Im nĂ€chsten Schritt habe ich die notwendigen CRM-Webservices hinzugefĂŒgt. Im nachfolgenden Beispiel wird das Discovery und das Organization-Webservice benötigt. Eine genauere Beschreibung der Services findet ihr u.a. hier:

http://technet.microsoft.com/de-ch/library/gg309557(en-us).aspx
http://technet.microsoft.com/de-ch/library/gg328127(en-us).aspx
http://technet.microsoft.com/de-ch/library/microsoft.xrm.sdk.discovery.idiscoveryservice(en-us).aspx
http://technet.microsoft.com/de-ch/library/microsoft.xrm.sdk.iorganizationservice(en-us).aspx

Die Webservices, sind in der Standard-CRM-Installation enthalten, findet Ihr nach folgendem Schema:
http://[servername]:[port]/[Organisation]/XRMServices/2011/Organization.svc
http://[servername]:[port]/[Organisation]/XRMServices/2011/Discovery.svc

Wie man eine WebReferenz hinzufĂŒgt, habe ich in einem meiner letzten Blogs beschrieben.

Bevor wir zum eigentlichen Import-Code kommen, sei erwÀhnt, dass ich die Late-Binding-Variante verwendet habe. Man kann auch das sog. Early-Binding verwenden, das ist jedoch nicht Scope dieses Blog-Eintrags. Eventuell widme ich dem Thema noch spÀter im Laufe des Projekts. Falls das so ist, werde ich diesen Artikel aktualisieren oder erweitern.

Nachfolgend nun das eigentliche Code-Snippet, welches folgende Aufgaben erledigt:

  1. Auslesen sÀmtlicher Firmen aus der Quell-Datenbank (mittels ADO.NET Entity Framework)
  2. Loopen durch alle Firmen
  3. CRM-EntitÀts-Record erzeugen (Account/Firma)
  4. Eigenschaften mappen
  5. Einfacher Dublettencheck
  6. EinfĂŒgen / Aktualisieren

[sourcecode language=“csharp“]
static void Main(string[] args)
{
xxxEntities context = new xxxEntities();
var firmen = (from p in context.Tbl_Firmen
orderby p.Firmenname1
select p);

foreach(var firma in firmen)
{
Guid pk;
bool insert = InsertOrUpdate(firma.Firmenname1, out pk);

Entity account = new Entity("account");
account["name"] = firma.Firmenname1;
account["address1_line1"] = firma.Strasse;
account["address1_postalcode"] = firma.PLZ;
account["address1_city"] = firma.Ort;
account["telephone1"] = firma.Telefon;

account["telephone2"] = firma.Telefon1;
account["fax"] = firma.Telefax;
account["emailaddress1"] = firma.EMail;
account["emailaddress2"] = firma.EMail1;
//account["industrycode"] = firma.Branche1;
account["address1_county"] = firma.Bundesland;
account["address1_country"] = firma.Land;
//account["revenue"] = firma.Umsatz_Tsd;

if (insert)
Helper.OrgService.Create(account);
else
{
account["accountid"] = pk;
Helper.OrgService.Update(account);
}
}
}

private static bool InsertOrUpdate(string accountName, out Guid pk)
{
pk = new Guid();
var accounts = (from a in Helper.OrgContext.CreateQuery("account")
where (string) a["name"] == accountName
select a).FirstOrDefault();

if (accounts != null)
pk = new Guid(accounts["accountid"].ToString());

return pk == new Guid();
}
[/sourcecode]

Nachfolgend noch der Source meiner kleinen Helper-Klasse, welcher nur zwei Properties zum Arbeiten mit dem Organization-Webservice beinhaltet:

[sourcecode language=“csharp“]
public static class Helper
{
private static IOrganizationService _orgService;
public static IOrganizationService OrgService
{
get
{
if(_orgService == null)
{
ServerConnection serverConnect = new ServerConnection();
ServerConnection.Configuration config = serverConnect.GetServerConfiguration();
OrganizationServiceProxy proxy = new OrganizationServiceProxy(config.OrganizationUri, config.HomeRealmUri, config.Credentials, config.DeviceCredentials);

proxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
_orgService = (IOrganizationService)proxy;
}
return _orgService;
}
}

private static OrganizationServiceContext _orgContext;
public static OrganizationServiceContext OrgContext
{
get
{
if(_orgContext == null)
{
_orgContext = new OrganizationServiceContext(OrgService);
}
return _orgContext;
}
}

}
[/sourcecode]

Viel Spaß beim Probieren!

Microsoft Loopbackadapter unter Windows 7 64bit installieren

Der Loopbackadapter wird sehr hÀufig verwendet, wenn man eine Virtualisierungssoftware wie Virtual PC oder VMWare verwendet. WÀhrend VMWare die Treiber gleich automatisch installiert, ist das bei Virtual PC (leider?) nicht der Fall.

Geht dafĂŒr zunĂ€chst in den GerĂ€te-Manager. Ihr findet ihn wie folgt:

  1. Start => Rechtsklick auf „Computer“ (im grau/schwarzen Bereich) => Eigenschaften
  2. Im aufgehenden Fenster findet Ihr links die Option „GerĂ€te-Manager“ (blaugrauer Bereich)

Um nun den Adapter hinzuzufĂŒgen, selektiert zunĂ€chst in der Liste der GerĂ€te den Knoten „Netzwerkadapter“. Im HauptmenĂŒ (oben) klickt auf „Aktion“ und „Legacyhardware hinzufĂŒgen“.
Klickt auf „weiter“ und wĂ€hlt „Hardware manuell aus einer Liste wĂ€hlen und installieren (fĂŒr fortgeschrittene Benutzer)“.

In der nun dargestellten Liste wĂ€hlt nochmals „Netzwerkadapter“ aus und klickt auf weiter. Nach kurzem Warten könnt ihr in der Liste der Hersteller „Microsoft“ auswĂ€hlen. Ist das passiert, scheint in der rechten Liste an 3 oder 4. Stelle der „Microsoft Loopbackadapter“ auf.

Nach Klick auf weiter, wird der Treiber installiert und der Adapter hinzugefĂŒgt.

Viel Spaß beim Testen!

Virtual PC auf Windows 7 installieren | IE7 auf Windows 7 testen

Wer kennt das nicht. Man schreibt eine Webanwendung, welche in allen gÀngigen Browsern 1a aussieht. Dann meldet sich der Kunde, dass in einer bestimmten, uralten Version des IE7 ein Darstellungsfehler auftritt. Was tun?

Nachdem IE7 unter Windows 7 nicht installierbar ist, muss eine virtuelle Maschine her.

Microsoft bietet Besitzern der Windows 7 Ultimate Version den sog. XP-Mode an. Das hat mir leider nicht weitergeholfen, da ich nur Enterprise in Verwendung habe. Allerdings bietet Microsoft direkt ein Image an, welches Windows 7 und IE7 enthÀlt.

http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=11575

Auf dieser Seite lassen sich noch weitere Images finden, wie z.B. Windows XP mit IE6 oder Windows 7 mit IE8.

Unter nachfolgendem Link könnt Ihr Virtual PC 2007 kostenlos herunterladen. ACHTUNG: Bitte auf die Systemarchitektur achten, denn die 32-Bit Version lÀsst sich auf einem 64-Bit-System nicht installieren.

http://www.microsoft.com/downloads/de-de/details.aspx?displaylang=de&FamilyID=04D26402-3199-48A3-AFA2-2DC0B40A73B6

Nach der Installation und dem HinzufĂŒgen des heruntergeladenen Images, stellt sich das nĂ€chste Problem. Wie kommunizieren die beiden (Host & Client) miteinander?

Hier gibt es nun mehrere Varianten. Um diese zu konfigurieren, geht man in die Einstellungen der virtuellen Maschine unter den Punkt „Netzwerk“.
Neben der Anzahl der Netzwerkadapter, ist hier primÀr die Einstellung bei Adapter 1 wichtig.

  •  Nicht verbunden – selbsterklĂ€rend, kein Netzwerkadapter wird am Client hinzugefĂŒgt.
  • Nur Lokal – Netzwerkadapter wird hinzugefĂŒgt, jedoch nicht verbunden
  • „Name d. Netzwerkkarte“ – Netzwerkadapter wird am Client hinzugefĂŒgt, am Host wird die ausgewĂ€hlte Netzwerkkarte zur Kommunikation verwendet. ACHTUNG: Solltet Ihr mit einem Netzwerk verbunden sein, könnte das zu Problemen fĂŒhren, da die TCP/IP-Einstellungen verĂ€ndert werden!!
  • Microsoft Loopbackadapter – Netzwerkadapter wird am Client hinzugefĂŒgt, am Host wird die virtuelle Netzwerkkarte zur Kommunikation verwendet. Falls diese Eigenschaft nicht bei euch auftaucht, lest bitte folgenden Blog-Eintrag.
  • Gemeinsames Netzwerk (NAT) – Netzwerkadapter wird am Client hinzugefĂŒgt, der Client versucht, die gleichen Netzwerkeinstellungen zu beziehen wie die prim. Netzwerkkarte am Host.

Nachdem bei uns im Firmennetzwerk durch div. Restriktionen die Option „Gemeinsames Netzwerk (NAT)“ wegfĂ€llt, musste ich zunĂ€chst den Loopbackadapter installieren. Wie das funktioniert, erfĂ€hrt Ihr im nachfolgenden Blog-Eintrag. Loopbackadapter unter Windows 7 installieren.
Viel Spaß beim Testen!

IIS7: ApplicationPool auf 32bit umstellen

Wenn Ihr eine Webanwendung habt, welche Ihr z.B. noch unter Windows XP entwickelt habt oder welche 3rd-Party-Komponenten verwendet, die nicht 64-bit-tauglich sind, so bekommt Ihr auf einem 64-Bit-System folgende Fehlermeldung:

Die Datei oder Assembly „DirectShowLib“ oder eine AbhĂ€ngigkeit davon wurde nicht gefunden. Es wurde versucht, eine Datei mit einem falschen Format zu laden.

Das Problem lĂ€sst sich einfach lösen, in dem man den Application Pool konfiguriert. Geht dafĂŒr in den IIS-Manager, Anwendungspools und selektiert den Pool eurer Anwendung. Klickt dann im rechten MenĂŒ auf „Erweiterte Einstellungen…“.

Im sich öffnenden Fenster findet Ihr die Einstellung „32-bit-Anwendungen aktivieren“ und setzt dieses auf „True“.

Danach sollte eure Anwendung wie erwartet funktionieren!

Viel Spaß!

Javascript als Resource verpacken/entpacken (Arbeiten mit ScriptReferences/ScriptManager)

Manche von euch haben vielleicht meinen letzten Blog-Entry
Javascript als Resource verpacken/entpacken (Arbeiten mit Embedded Resources) gelesen. Mein Mitstreiter domiwaldi hat mich auf die Idee gebracht, statt dem ClientScriptManager den ScriptManager zu verwenden. Nachdem das Vorteile bei u.a. der Komprimierung hat, habe ich mir das mal angesehen und siehe da, der Quellcode liess sich auch gleich um einiges VerkĂŒrzen.

Nachfolgend seht ihr die aktualisierte Version der RegisterBasicLib-Funktion:

[sourcecode language=“csharp“]
public const string FnJquery = "Utils.Web.Javascript.BasicLib.Resources.jquery-1.4.1.js";
public const string FnJqueryUi = "Utils.Web.Javascript.BasicLib.Resources.jquery-ui-1.8.5.custom.min.js";
public const string FnCookie = "Utils.Web.Javascript.BasicLib.Resources.jquery.cookie.min.js";
public const string FnJson = "Utils.Web.Javascript.BasicLib.Resources.jquery.json-2.2.min.js";
public const string FnBasicLib = "Utils.Web.Javascript.BasicLib.Resources.Utils.BasicLib.js";

/// <summary>
/// Registers the basic lib.
/// </summary>
/// <param name="p">The p.</param>
public static void RegisterBasicLib(ScriptManager sm)
{
Page p;
if (IsBasicLibEnabled)
{
ScriptReference sr = new ScriptReference(FnJquery, System.Reflection.Assembly.GetExecutingAssembly().FullName);
sm.Scripts.Add(sr);

sr = new ScriptReference(FnJqueryUi, System.Reflection.Assembly.GetExecutingAssembly().FullName);
sm.Scripts.Add(sr);

sr = new ScriptReference(FnJson, System.Reflection.Assembly.GetExecutingAssembly().FullName);
sm.Scripts.Add(sr);

sr = new ScriptReference(FnCookie, System.Reflection.Assembly.GetExecutingAssembly().FullName);
sm.Scripts.Add(sr);

sr = new ScriptReference(FnBasicLib, System.Reflection.Assembly.GetExecutingAssembly().FullName);
sm.Scripts.Add(sr);

}
}
[/sourcecode]

Wichtig ist jedoch zu bedenken, dass die Resourcen als Webresourcen in der Assembly.info deklariert werden mĂŒssen.

[sourcecode language=“csharp“]
[assembly: System.Web.UI.WebResource(Utils.Web.BasicLib.FnBasicLib, "text/javascript")]
[assembly: System.Web.UI.WebResource(Utils.Web.BasicLib.FnCookie, "text/javascript")]
[assembly: System.Web.UI.WebResource(Utils.Web.BasicLib.FnJquery, "text/javascript")]
[assembly: System.Web.UI.WebResource(Utils.Web.BasicLib.FnJqueryUi, "text/javascript")]
[assembly: System.Web.UI.WebResource(Utils.Web.BasicLib.FnJson, "text/javascript")]
[/sourcecode]

Die Markierung als Embedded Resource im Properties-Dialog ist hingegen nicht mehr notwendig. Auch das vorherige entpacken und abspeichern der Datei ist nicht lÀnger notwendig, was die Geschichte sehr angenehm macht.

Viel Spaß !