Results for tag "c"

ASP.net – Bitmap (.bmp) in den OutputStream rendern – Render Bitmap into Outputstream

Hallo Leute,

mal wieder was Spannendes. Beim Versuch eine BMP-Grafik direkt in den Outputstream einer ASP.net Webpage zu rendern, tritt eine Exception auf (System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.).

Spannende Geschichte, denn der gleiche Quellcode funktioniert mit JPG-Grafiken:

[sourcecode language=“csharp“]
Response.Clear();
Response.ContentType = "image/jpeg"; // "image/bmp" does not work!

Image orgChartImage = (System.Drawing.Image)Cache[imageName];
orgChartImage.Save(this.Response.OutputStream);
orgChartImage.Dispose();
[/sourcecode]

Bei meiner Recherche bin ich über den Blog von Rick Stahl gestolpert, der das gleiche Problem mit PNG-Grafiken beschreibt, siehe:

Rick Stahl’s Blog

In den Kommentaren schreibt u.a. Russ Garner, dass das daran liegen kann, dass der Encoder von PNG-Grafiken einen bi-directional Stream benötigt und postet gleichzeitig auch die Lösung:

Png’s are a special case. Their encoder requires a bi-directional stream. The solution is to stream to a memory stream first and write the contents of that memory stream to OutputStream afterwards.

When you get to caching (which I’ve just implemented) you’ll be writing to a memory stream anyway just to cache the output, so this isn’t a big deal.

As far as the GIF end of things goes, the default palletization of the built in GIF encoder leaves something to be desired. I’ve built in support for an Octree quantizer to deal with this.

Seriously – it’s a lot of work. It’s probably easier if I just send you some code 😉

Umgemünzt auf unser Beispiel bedeutet dies folgenden Sourcecode:

[sourcecode language=“csharp“]
string imageName = Request.QueryString["ID"];
Bitmap bm2 = (System.Drawing.Bitmap)Cache[imageName];
Bitmap bm3 = new Bitmap(bm2);

MemoryStream ms = new MemoryStream();

Response.ContentType = "image/bmp";
bm3.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.WriteTo(Response.OutputStream);

bm2.Dispose();
bm3.Dispose();
Response.End();
[/sourcecode]

Eine schlüssigere Fehlermeldung wäre mal was gewesen …
Ich hoffe, ich rette euch mit diesen Codesnippets ein paar Stunden eurer wertvollen Zeit!

Cheers,
Chris

CRM 2011 – N:M Beziehung via C# setzen

CRM 2011 beherrscht nun endlich eine vernünftige N:M Beziehung.
Folgendes Code-Snippet zeigt, wie man via C# und dem SDK-Webservice zwei Entitäten verbindet.

[sourcecode language=“csharp“]
/// <summary>
/// Clones the entity.
/// </summary>
/// <param name="organization">The organization.</param>
/// <param name="entity1">The entity1.</param>
/// <param name="entity2">The entity2.</param>
/// <param name="relationShipName">Name of the relation ship.</param>
public static void AssociateEntities(contracts.IOrganization organization, Entity entity1, Entity entity2, string relationShipName)
{
// Create an AssociateEntities request
AssociateEntitiesRequest request = new AssociateEntitiesRequest();

// Set the ID of Moniker1 to the ID of the lead.
request.Moniker1 = new EntityReference { Id = entity1.Id, LogicalName = entity1.LogicalName};

// Set the ID of Moniker2 to the ID of the contact.
request.Moniker2 = new EntityReference { Id = entity2.Id, LogicalName = entity2.LogicalName};

// Set the relationship name to associate on.
request.RelationshipName = relationShipName;

// Execute the request.
Helper.OrgService(organization).Execute(request);
}
[/sourcecode]

Viel Spaß beim Ausprobieren!

CRM 2011 – Verbundene Objekte finden/laden (Get related Entities)

Verbindungen zwischen Entitäten werden in CRM, wie in einer Datenbank, mittels Referenzen abgebildet. Das funktioniert im GUI bzw. im Customizing (welches direkt im CRM passiert) auch sehr gut. Wenn man mittels C#-SDK versucht Entitäten zu bearbeiten/erstellen wird es dann schnell etwas umständlich.

Jeder kennt z.B. folgendes Beispiel: Angebot –> Positionen (1:n). Über die Eigenschaften (Attributes) der Angebot-Entität lässt sich nun nicht mehr ermitteln, wieviele Positionen das Angebot nun besitzt. Um diese Aufgabe zu lösen, habe ich mir zwei Helper-Methoden geschrieben, welche mir eine Collection der (Positionen-)Entitäten oder nur deren PrimaryKeys zurückliefern:

[sourcecode language=“csharp“]
/// <summary>
/// Gets the related projekt standorte.
/// </summary>
/// <param name="organization">The organization.</param>
/// <param name="entityName">Name of the entity.</param>
/// <param name="foreignKeyFieldName">Name of the foreign key field.</param>
/// <param name="foreignKeyFieldValue">The foreign key field value.</param>
/// <param name="columnSet">The column set.</param>
/// <returns></returns>
public static EntityCollection GetRelatedEntities(IOrganization organization, string entityName, string foreignKeyFieldName, string foreignKeyFieldValue, ColumnSet columnSet)
{
ConditionExpression condition = new ConditionExpression();
condition.AttributeName = foreignKeyFieldName;
condition.Operator = ConditionOperator.Equal;
condition.Values.Add(foreignKeyFieldValue);

// Create query expression.
QueryExpression query1 = new QueryExpression();
query1.ColumnSet = columnSet;
query1.EntityName = entityName;
query1.Criteria.AddCondition(condition);

EntityCollection result1 = Helper.OrgService(organization).RetrieveMultiple(query1);

return result1;
}

/// <summary>
/// Gets the related object ids.
/// </summary>
/// <param name="organization">The organization.</param>
/// <param name="entityId">The entity id.</param>
/// <param name="relatedEntityName">Name of the related entity.</param>
/// <param name="relatedEntityPrimaryKeyName">Name of the related entity primary key.</param>
/// <param name="foreignKeyName">Name of the foreign key.</param>
/// <returns></returns>
public static List<string> GetRelatedObjectIds(IOrganization organization, string entityId, string relatedEntityName, string relatedEntityPrimaryKeyName, string foreignKeyName)
{
var relEntities = EntityHelper.GetRelatedEntities(organization, relatedEntityName, foreignKeyName,
entityId, new ColumnSet(relatedEntityPrimaryKeyName));

return relEntities.Entities.Select(entity => entity.Attributes[relatedEntityPrimaryKeyName].ToString()).ToList();
}
[/sourcecode]

Ich hoffe ich konnte euch damit die Arbeit ein wenig verkürzen 😉

Cheers,
Chris

C# – Ausführenden Methodennamen zur Laufzeit auslesen

Ich hatte in einer Applikation die Notwendigkeit, ungeachtet der Performance, immer mitzuliefern, an welcher Stelle im Code ein Fehler auftritt bzw. aufgetreten ist – steinigt mich daher bitte nicht gleich für den Einsatz der Diagnostics-Klassen, welche bekanntermassen nicht die schnellsten sind!

Mittels folgendem Codesnippet kann man ganz einfach den Methodennamen und andere interessante Dinge auslesen:

[sourcecode language=“csharp“]
/// <summary>
/// Whoes the called me.
/// </summary>
/// <param name="frame">The frame.</param>
/// <returns></returns>
public static string GetCurrentFunctionName(int frame)
{
StackTrace stackTrace = new StackTrace();
StackFrame stackFrame = stackTrace.GetFrame(frame);
MethodBase methodBase = stackFrame.GetMethod();

return methodBase.ToString();
}

[/sourcecode]

Mit dem Parameter „frame“ gebt Ihr an, wieviele Methoden „zurückgesprungen“ werden soll. frame = 0 würde „GetCurrentFunctionName()“ retour liefern. frame = 1 liefert den Methodennamen, welcher die Methode „GetCurrentFunctionName()“ aufgerufen hat. Nach diesem Schema könnt Ihr entsprechend zurückspringen.

Cheers,
Chris

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

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!

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ß !