Fasil Hayat's blog

...C#, .Net, Sharepoint, BizTalk, JQuery...

SOAP beskeder består af et 'MessageBody' og et 'MessageHeader' segment, hvor man typisk i 'MessageBody' segmentet udveksler forretningsdata, kan man benytte sig af SOAP header til udveksling af sekundære data. Det kan være data, der altid skal sendes med over via SOAP headeren fx. den kaldende parts autorisations data.

Her er et simpelt eksempel på, hvordan man kan berige data udvekslingen med en SOAP header fra en klient, som bagefter kan udvindes hos service udbyderen.

Kontrakter:

   1:  namespace MitNamespace.Foo {
   2:      public class MinMessageHeader{
   3:          public string BrugerId { get; set; }
   4:          public string ApplikationId { get; set; }
   5:      }
   6:  }
   7:   
   8:  namespace MitNamespace.Foo {
   9:      public class HentEtEllerAndetRequest {
  10:          public MessageHeader<MinMessageHeader> Header { get; set; }
  11:          public string EnForretningsId { get; set; }
  12:      }
  13:  }


Klient kode:

   1:  var klient = new MinProxy.MinServiceClient();
   2:   
   3:  using (var scope = new OperationContextScope(klient.InnerChannel)) {
   4:      var req = new HentEtEllerAndetRequest();
   5:      req.EnforretningsId = "25";
   6:      req.Header = new MessageHeader<MinMessageHeader>(new MinMessageHeader {
   7:                      BrugerId = "FHA", 
   8:                      ApplikationId = "NavnPaaApplikation", 
   9:                  });
  10:      var messageHeader = req.Header;
  11:      var header = messageHeader.GetUntypedHeader("MinCustomHeader", 
  12:                                          "http://www.fasil.dk/Applikation");
  13:      OperationContext.Current.OutgoingMessageHeaders.Add(header);
  14:      var result = klient.HentNogleData(req);
  15:  }


Serverkode:

   1:  [OperationContract]
   2:  public HentNogleDataResponse HentNogleData(HentEtEllerAndetRequest request) {
   3:      MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders;
   4:      var headerData = headers.GetHeader<MinMessageHeader>("MinCustomHeader", 
   5:                                          "http://www.fasil.dk/Applikation");
   6:      ...
   7:      ...
   8:      ...
   9:  }


Man kan også benytte sig af data kontrakter istedet. Dette eksempel virker dog helt ubeklageligt. 
Klient koden kan naturligvis bestå af en 'abstract' klasse, hvor header oplysningerne sættes. Alle andre service kaldende klasser skal herefter implementere / nedarve fra denne 'abstract' klasse.

På server delen kan man naturligvis tjekke for headerdelen, inden et response sendes tilbage til klienten. Alternativt sendes en autentificerings- / autorisationsfejl tilbage til klienten, hvis header oplysningerne ikke er gyldige.



Der er masser af muligheder for at folde sig ud i .Net, og der masser af muligheder for at bygge nogle stærke web sider udfra solide design principper. Følgende indlæg kommer til at omhandle, hvordan man dynamisk kan indlæse usercontrols (*.ascx) ind i en anden 'UserControl', og hvordan man kan neste 'UserControls, og tilgå deres postede væerdier fra et ydre niveau.

Følgende illustration kan virke en smule overdrevet. I mit lille projekt giver det dog god mening, hvorimod man nok kunne fjerne 'SubControl.ascx' fra dette eksempel. Dog kan overdrivelsen somme tider fremme forståelsen. Eksemplet synliggør i virkeligheden hierakiet af kontrollerne.

Som man kan se ligger det væsentlige i de inderste filer ('UsrControl01.ascx' og 'UsrControl02.ascx'). Ideen er at man skal kunne udvikle flere komponenter med tiden og kunne tilføje dem dynamisk. Jeg har undladt at vise håndtering af fremtidige 'UserControls'. Definition og lokation kan gemmes i en xml fil, man så kan indlæse og dynamisk løbe alle de definerede UserControls i xml filen igennem.
I dette eksempel viser jeg hvordan, man kan tilføje dem og arbejde med deres indhold.

Side struktur

'Main.Master' er blot en master side.
'Default.aspx' baserer sig på master sidens definition af ContentPlaceHolders.
'MainControl.ascx' er den første userkontrol hvor jeg i dette eksempel har en registrering af 'SubControl.ascx' og en 'asp.net button' kontrol som fungerer som en submit knap.
'SubControl.ascx' sørger for at (fx. indlæse en xml fil med en liste af UserControls) dynamisk indlæse diverse UserControls.
'UsrControlxx.ascx' repræsenterer små komponenter eller samling af logiske form felter.

I de enkelte 'UserControls' filer har jeg i dette eksempel en TextBox kontrol i 'UsrControl01.ascx' og i 'UsrControl02.ascx'har jeg en simpel DropDownList.

Disse to UserControls bliver indlæst i 'SubControl.ascx' på følgende måde:

   1:  namespace NestedUserControls.Controls {
   2:      public partial class SubControl : System.Web.UI.UserControl {
   3:          protected void Page_Load(object sender, EventArgs e) {
   4:              Controls.Add(LoadControl("~/Controls/UsrControl01.ascx"));
   5:              Controls.Add(LoadControl("~/Controls/UsrControl02.ascx"));
   6:          }
   7:      }
   8:  }


Igen istedet for at skrive dem ind én af gangen kan man sagtens læse dem ind fra en xml fil og løbe antallet igennem og tilføje dem dynamisk. Jeg har simplificeret for eksemplets skyld.
I 'MainControl.ascx' er 'SubControl.ascx' registreret. 'MainControl.ascx' indeholder 'SubControl.ascx' og en submit knap:

   1:  <uc1:SubControl ID="SubControl" runat="server" />
   2:  <asp:Button ID="SubmitBtn" runat="server" Text="Send data" 
   3:      onclick="SubmitBtn_Click" />


I 'MainControl.ascx.cs' håndteres Submit knappens onclick event, hvor jeg vha. LINQ løber kontrol hierakiet igennem og herefter gennem generics returnerer værdien af asp.net kontrollen i det format den eksisterer i.

   1:  protected TT GetControl<T, TT>(string controlId) 
   2:   where T : UserControl 
   3:   where TT : class {
   4:     var res = Controls.OfType<T>().Select(s => s.FindControl(controlId)).Single();
   5:     return res as TT;
   6:  }


Værdierne tilgås på følgende vis:

   1:  var value1 = GetControl<UsrControl01, TextBox>("MyTextBox").Text;
   2:  var value2 = GetControl<UsrControl02, DropDownList>("DropDownList1").SelectedItem;


Meget elegant og stærk brug af kombination af generics og LINQ. Man kan spørge sig selv, hvornår man har brug for at klippe ting i x antal 'Usercontrols'. Det gør man, hvis man vil lave en wizard, hvilket i virkeligheden mit lille projekt går ud på. Frameworket baserer sig på ovenstående, og antal af steps i wizard bestemmes af antallet af 'UsrControlxx.ascx'. I mit lille wizard projekt ligger defintionerne af 'UserControls' i en xml fil.

Der vil inden længe komme et JQuery baseret wizard eksempel sat sammen med ASP.Net.



Tilbage i Februar 2011 var jeg involveret i mit første Biztalk 2010 projekt. Denne orkestrering viser parallelle aktiviteter, der kalder andre delorkestreringer, decide shapes samt 'try - catch' shapes i orkestreringen.

Til orkesteringen hører der desuden en transform shape, der transformerer beskeder i en anden struktur. Til dette formål blev mapping brugt, som man kan se et udsnit af forneden.

Inden længe kommer der et indlæg om, hvordan man begiver sig rundt indenfor Biztalk 2010. Tips og tricks på hvordan de forskellige shapes, pipelines og mappings kan anvendes.



Selvom jeg principielt er modstander af brugen af sharepoint lister til at holde på forretningskritiske data, kan man ikke komme udenom det faktum, at man kan udstille en sharepoint liste som en ganske almindelig service. Indlægget kommer til at omhandle, hvordan denne service kan konsumeres af andre applikationer. Man skal dog rent arkitektur mæssigt forholde sig særligt kritisk overfor dette mønster, da sharepoint lige pludselig fungerer som backend, hvilket kan utydeliggøre en en klar skillelinje mellem backend systemer og frontend portaler. Det kan hurtigt blive et virvar af lagdelinger, der overlapper hinanden, istedet for at have klare afgrænsede ansvarsområder. Det overholder selv ikke principperne bag onion arkitektur, så hellere klare afgrænsede snitflader frem for en blanding af lagene.

Bliver en sharepoint liste derimod populeret med data fra en database, er problemet nok ikke så stort, men i Sharepoint 2007 findes der stadigvæk ikke mulighed for at implementere relationer fuldt ud. Det hele bliver nogle logiske relationer i form af lookup tabeller her og der uden referentiel integritet. Dette skulle være taget hånd om i Sharepoint 2010. Dog retfærdiggør dette stadigvæk ikke placering af forretningskritiske data i en Sharepoint liste, men Sharepoint 2010 giver derimod en mere sikker og renere repræsentation af relationel datastruktur.

Har man en intern kaffeklub eller andet i en liste, der skaber værdi i interne arbejdsprocesser i en organisation, og man gerne vil udstille denne liste som en service, så kan det opnås på følgende måde:

List Data Retrieval Service
http://<server-url>/_vti_bin/dspsts.asmx

Har man allerede en liste oprettet med noget data, så åbnes Visual Studio og man tilføjer ganske enkelt referencen: 'http://<server-url>/_vti_bin/dspsts.asmx'.

En sharepoint liste er beskyttet af rettigheder, og derfor skal der sættes credentials op for at tilgå listen med tilstrækkelige rettigheder. I dette eksempel tilgås listen med Network Credentials. Dvs. listen tilgås med den bruger, der er logget på netværket.

var stsAd = new StsAdapter();
stsAd.Credentials = CredentialCache.DefaultNetworkCredentials;

//Resten af kode snippet ser således ud:
string[] vArray = new string[1];
vArray[0] = "1.0";
var ver = new Versions();
ver.version = vArray;

stsAd.versions = ver;
var reqHeader = new RequestHeader();
reqHeader.document = DocumentType.content;
reqHeader.method = MethodType.query;

stsAd.request = reqHeader;

//Listen refereres ved hjælp af GUID.
//Find GUID på listen under instillingerne for listen.
const string listGuid = "{08D53A6B-947E-406F-B3C0-74534FC6BE07}";

var req = new QueryRequest();

var xpq = "/list[@id='" + listGuid + "']";
var sQuery = new DSQuery();
sQuery.select = xpq;
req.dsQuery = sQuery;

var spQuery = new DspQuery();
req.dsQuery.Query = spQuery;

XmlNode myNode = stsAd.Query(req);


Xmlnoden 'myNode' holder på listen, og skal herefter pakkes ud.

Er der behov for at filtrere svaret, kan man benytte sig af CAML. Fremadrettet vil / kan LINQ blive brugt til dette. Her er et eksempel på, hvordan man sender CAML med i et request.

var xmlDoc = new XmlDocument();
var eqQuery = xmlDoc.CreateElement("Eq");
eqQuery.InnerXml = "<FieldRef Name='Vaerdi'/><Value Type='Text'>fisk</Value>";

var spQuery = new DspQuery();
spQuery.Where = eqQuery;
req.dsQuery.Query = spQuery;

XmlNode myNode = stsAd.Query(req);


Find yderligere informationer ved brug af 'List Data Retrieval Web Service' her.



Fejlhåntering er et essentielt krav for at sikre kvalitet og effektiv drift af software og webapplikationer. Der er desværre stadigvæk en ret stor tendens til fejlhåndtering ikke får det nødvendige fokus, og man ender tit med at bruge uhyre mængder af tid på detektiv arbejde for at finde fejl, når noget lige pludseligt ikke virker mere. Microsoft har udviklet nogle hjælpe biblioteker, som man frit og gratis kan benytte sig af i eget udviklet software. Nederst i dette indlæg vil man finde et download link til Enterprise Library 5.0.

Før man begiver sig ud i at at sætte et framework op til fejlhåndtering, bør man sørge for en effektiv 'exception handling' strategi. Man bør gøre udviklere opmærksomme på at håndtere exceptions på korrekt vis, og helst undgå brug af generelle catch() block i kode, der fanger alt.

Fx. bør man i en beregning sørge for at skrive en:
catch(DivideByZeroException ex){...}

Istedet for en generel:
catch(Exception ex){...}

Dette vil sikre, at fejl bliver håndteret efter deres natur, og logging af fejlene ligeledes kan håndteres særskilt og korrekt. Der findes masser af retningslinjer på nettet over dette emne. En anden dimension af fejlhåndtering, som man bør gøre en del af rammeværket, er differentiering mellem tekniske- og forretnings fejl. Tekniske fejl dækker over håndtering samt logging af fejl som fx. en afbrudt database forbindelse eller eksemplet med division med nul. Forretnings fejl dækker over forretningsregler, hvor noget programlogik ikke må udføres i noget kode, hvis fx. en pris er over et bestemt beløb. Disse fejl skal naturligvis også logges, og her kan man stadigvæk benytte sig af Enterprise Library. Der vil ikke fordybes yderligere i Exception handling eller fejlhåndterings strategi. Det kommer der nok et indlæg om på et senere tidspunkt.  

Der er for ikke længe siden kommet en ny version af Microsoft Enterprise Library i April 2010, og konfigurations interfacet er ligeledes blevet re-designet. Enterprise Library kommer med forskellige features fx. Dataaccess Application Block, Caching Block, Logging Application Block etc. Dette indlæg vil beskrive, hvordan man sætter Application Logging Block op, og hvordan man logger fejl fra en helt almindelig web applikation.

Efter endt installation kan Enterprise Library Configuration åbnes "C:\Program Files (x86)\Microsoft Enterprise Library 5.0\Bin\EntLibConfig.NET4.exe". Dette vil starte et konfigurations interface op.
For opsætning af Application Logging Block på en eksisterende webapplikation gør følgende:

- File -> Open og find web.config fil på webapplikationen.

Herefter vil man se følgende:

Enterprise Library 5.0

Man kan starte med at sætte database forbindelsen op. For at generere log databasen kan dette sql script køres på en eksisterende SQL Server (hvis man ikke vil gøre brug af 'Database Trace Listener' dvs. logging af fejl i en database, kan kørslen af scriptet samt database forbindelses opsætningen udelades).
Dette gøres ved at folde 'Database Settings' menuen ud. Som default vil 'LocalSqlServer' være angivet. Denne værdi skal ændres til noget andet fx. 'MyBlogConnection'.  'MyBlogConnection' menuen foldes ud og den rigtige connection streng angives til databasen. Der kan ligeledes vælges en anden database provider. I dette tilfælde benyttes 'System.Data.SqlClient'. Nu bør database forbindelsens konfiguration være på plads.

Vi starter med at definere et 'Message format', dvs. en definition af hvordan vores log message ser ud. Under 'Log Message Formatters' kan det eksisterende formatter foldes ud og navnet rettes til 'MyCompanyText Formatter' (brug firma navn eller andet). Under 'Template' kan man se en hel masse værdier, som log beskeden består af. Disse værdier kan tilpasses, men lad default værdierne stå.

Herefter knyttes der nogle 'lytteposter' til dvs. 'Target Listeners'. Under 'Logging Target Listeners' klikkes på '+' knappen og 'Add Logging Target Listener' vil dukker op. Ved klik på feltet, vil man blive præsenteret for forskellige muligheder. Vælg 'Event Log Listener', som sørger for at dirrigere fejlloggen ned i event loggen. Fold herefter 'Event Log Listener' ud og under 'Formatter' vælges 'MyCompanyText Formatter'. Herefter opretter man en 'Database Trace Listener' som sørger for persistering af data i en database. Under database Instance angives SQL Serveren. I dette tilfælde vælges 'LocalSqlServer'. Under 'Formatter' vælges 'MyCompanyText Formatter'. Tilsidst tilføjes 'Flat File TraceListener' og placering af logfilen angives under File Name (Til test kan Skrivebordet eller Desktop stien angives). Under 'Formatter' vælges 'MyCompanyText Formatter' igen.

Nu mangler vi blot kategorierne. I dette tilfælde angives der tre kategorier under 'Categories'. Disse tilføjes ved at trykke på '+' knappen. Her dukker en 'Add category' op og denne vælges. Navnet ændres til 'Debug' og en 'RaiseAlert' kategori tilføjes ligeledes.
Planen er at 'Debug' kategorien skal håndtere dirrigering af fejl i en flad fil. Dvs. at der under 'Debug' kategorien tilføjes vores allerede oprettede 'Flat File Tracelistener' ved at trykke på '+' under 'Listeners' og 'Name'. 'Debug' vil således kun have en enkel Trace listener knyttet til sig. 'RaiseAlert' foldes ud og udfra 'Listeners' og under 'Name' trykkes igen på '+' tre gange og i hver enkelt drop down angives henholdsvis 'Database Trace Listener', 'Event Log Listener' og 'Flat File Listener'. Under 'General' angives samme 'Listeners' som under 'RaiseAlert'.

Billedet vil se nogenlunde således ud:

Enterprise Library 5.0

For at kunne logge fejlene, skal der bruges en LogManager klasse. Man kan selvfølgelig selv programmere det. Alternativt kan klassen hentes her. Klassen kan placeres i et utility eller shared assembly, og kan benyttes på følgende måde:

} catch (InvalidOperationException ex) {
     ILog log = new LogManager();
     log.Error("Attempt to match an entity property with a reader field failed.", ex);
}

 

For at benytte sig af LogManager klassen, skal følgende *.dll filer refereres i den assembly, hvor LogManageren er placeret:

Microsoft.Practices.EnterpriseLibrary.Logging.Database.dll
Microsoft.Practices.EnterpriseLibrary.Logging.dll

Ved at fremprovokere en fejl vil man herefter se, at fejlen bliver persisteret ned i en database og i event loggen samt i en flad fil på skrivebordet. Debug kategoriseret fejl bliver kun skrevet ned i en flad fil.
For at bruge et web baseret logviewer kan NDashboard benyttes.

Hent Enterprise Library 5.0 her.
Find masser af hjælp og inspiration på codeplex.



Så har jeg omsider fået færdiggjort en ny version af NDashboard. Dette er et lille webbaseret logviewer site, der kan læse bugs persisteret i en database gennem Log4Net eller Enterprise Library. En lille vejledning kan hentes nederst i indlægget. Her man vil finde en forklaring på, hvordan man slår læsning fra Log4net eller Enterprise Library til.
Der er i første omgang ingen fejlhåndtering i dette minisite, men en ny version med registrering af fejl i event log og tekster fra ressource filer, vil inden længe blive introduceret i en version 0.3.

NDashboard 0.2

NDashboard version 0.2 kan hentes her.

Systemkrav
- Internet Information Server (IIS)
- .Net framework 3.5 eller 4.0
- Adgang til en SQL Server med eksisterende log database tabeller (enten EL eller L4N).

Installation
Opret et website på en IIS og deploy filerne på websitet.

Hent konfigurationsvejledningen her.

SQL Scripts
Det er en forudsætning, man allerede har enten Enterprise eller Log4Net database sat op.
Har man ikke det, kan man finde SQL script herunder:
Log4Net

Enterprise Library scriptet kan efter endt installation fremfindes under '\EntLib4Src\Blocks\Logging\Src\DatabaseTraceListener\Scripts\LoggingDatabase.sql'.
Herunder vil man også finde 'CreateLoggingDb.cmd' batch scriptet, som kan køres direkte fra mappen for oprettelse af databasen på en lokal SQL Server.

Der kommer meget snart et indlæg, der vil handle om opsætning af Enterprise Library Logging block.

Kontakt mig gerne for yderligere hjælp.
Er der interesse for at videreudvikle denne lille webapp, giver jeg med glæde fuld adgang til kildekoden.



Fasil Malik Hayat

Developer
.Net, WCF, Sharepoint, MOSS, Biztalk, JQuery.

linkedin facebook twitter plaxo google+ grooveshark boxee
fasil

Education

fasil

Bachelor of Science (Honours)
De Montfort University, Leicester.

Fag: Java, MVC, Information Strategy, ITIL

Microsoft Certified Technology Specialist

Skills

.net vs2010 - C# sharepoint sql server jquery ubuntu java netbeans


  

Sign in