LINQ Extension Methods: Intersect

by wolfgang@gehirnwindung.de (Wolfgang) Mai 19, 2010 20:25

Mit der Intersect-Erweiterungsmethode vergleicht man zwei Sequenzen des gleichen Typs miteinander und erhält als Ergebnis eine neue Sequenz, in der die Elemente der ersten Sequenz vorhanden sind, die auch in der 2. Sequenz vorkommen (Gegenteilig zur Except-Erweiterungsmethode). Dabei wird jedes Element nur einmal aufgeführt. Verglichen wird - wenn man nichts anderes angibt - mit EqualityComparer<T>.Default.

Ohne Intersect Extension Method kann man das Verhalten in etwa so nachbilden

private List<T> Intersect<T>( List<T> list1, List<T> list2, EqualityComparer<T> comparer ) {
    var ret = new List<T>( );
    if( comparer == null ) comparer = EqualityComparer<T>.Default;

    for( int i = 0; i < list1.Count; i++ ) {
        if(
            !ret.Contains( list1[i], comparer ) &&
            list2.Contains( list1[i], comparer )
        ) {
            ret.Add( list1[i] );
        }
    }

    return ret;
}

Der Aufruf mit Erweiterungsmethode kann so erfolgen

list1.Intersect( list2 );
list1.Intersect( list2, EqualityComparer<T>.Default );

Tags: , ,

C#

LINQ Extension Methods: Except

by wolfgang@gehirnwindung.de (Wolfgang) Mai 12, 2010 21:00

Mittels Except-Erweiterungsmethode vergleicht man zwei Sequenzen des gleichen Typs miteinander und erhält als Ergebnis eine neue Sequenz, in der die Elemente der ersten Sequenz vorhanden sind, die in der 2. Sequenz nicht vorkommen. Dabei wird jedes Element nur einmal aufgeführt. Verglichen wird - wenn man nichts anderes angibt - mit EqualityComparer<T>.Default.

Ohne Except Extension Method kann man das Verhalten in etwa so nachbilden

private List<T> Except<T>( List<T> list1, List<T> list2, EqualityComparer<T> comparer ) {
    var ret = new List<T>( );
    if( comparer == null ) comparer = EqualityComparer<T>.Default;

    for( int i = 0; i < list1.Count; i++ ) {
        if(
            !ret.Contains( list1[i], comparer ) &&
            !list2.Contains( list1[i], comparer )
        ) {
            ret.Add( list1[i] );
        }
    }

    return ret;
}

Doch zum Glück gibt es die Erweiterungsmethoden, der Aufruf erfolgt in etwa so

list1.Except( list2 );
list1.Except( list2, EqualityComparer<T>.Default );

Tags: , ,

C#

LINQ Extension Methods: Union

by wolfgang@gehirnwindung.de (Wolfgang) Mai 12, 2010 01:00

Die Erweiterungsmethode Union bietet die Möglichkeit, 2 Sequenzen des gleichen Typs zu einer so zusammenzuführen, dass von jedem Element nur noch eine Entsprechung übrig bleibt. Damit verhält sich Union wie der Aufruf von Concat().Distinct().

Die Reihenfolge der Elemente in der Ergebnissequenz ergibt sich aus der Reihenfolge innerhalb der einzelnen Listen, die nacheinander abgearbeitet werden.

Ohne Union - Extension Method sieht das in etwa so aus

private List<T> Union<T>( List<T> list1, List<T> list2, EqualityComparer<T> comparer ) {
    var result = new List<T>( );
    if( comparer == null ) comparer = EqualityComparer<T>.Default;
    for( int i = 0; i < list1.Count; i++ ) {
        if( !result.Contains( list1[1], comparer ) ) {
            result.Add( list1[i] );
        }
    }

    for( int i = 0; i < list2.Count; i++ ) {
        if( !result.Contains( list2[1], comparer ) ) {
            result.Add( list2[i] );
        }
    }

    return result;
}

Der Vergleich wird - wenn nicht anders angegeben - mit dem Default - EqualityComparer<T> vollzogen.

Genauso verhält sich auch die Union-Erweiterungsmethode, die wie folgt aufgerufen wird

list1.Union( list2 );
list1.Union( list2, EqualityComparer<T>.Default );

Tags: , ,

C#

LINQ Extension Methods: Zip

by wolfgang@gehirnwindung.de (Wolfgang) Mai 11, 2010 18:51

Mit der Zip-Erweiterungsmethode können die Elemente zweier Sequenzen 1:1 zusammengeführt werden. Das Ergebnis ist eine neue Sequenz.
Zip ist somit eine spezialisierte Form von Join, wobei der Index als Key verwendet wird. Daher ist es auch egal, wie viele Elemente die jeweiligen Listen haben - zurückgegeben werden nur diejenigen Elemente, die in beiden Listen vorkommen.

Angenommen, man hat die 2 Listen firstNames und lastNamens mit den Vor- bzw. Nachnamen und möchte diese miteinander so kombinieren, dass eine string-Sequenz herauskommt, in der diese Namen durch Komma getrennt zusammengesetzt stehen. Die beiden Listen sind entsprechend sortiert.

Ohne Zip-Methode würde das in etwa so aussehen

var fullNames = new List<string>();
int count = Math.Min( firstNames.Count, lastNames.Count );
for( int i=0; i<count; i++ ){
    fullNames.Add( string.Format( "{1}, {0}", firstNames[i], lastNames[i] ) );
}

wobei die 2. Zeile vereinfacht werden kann, wenn sichergestellt ist, dass die beiden Listen gleich viele Elemente enthalten.

Mit der Zip-Methode geht das wesentlich kürzer (und ist meiner Meinung nach auch etwas leichter lesbar).
Wer noch Probleme mit den Lambda-Ausdrücken hat, der kann sich mit dem Artikel Tanz den => Lambda mit mir... ein wenig einlesen.

var fullNames = firstNames.Zip(
                    lastNames,
                    ( firstName, lastName ) => string.Format( "{1}, {0}", firstName, lastName )
                );

Der Rückgabewert ist nicht auf string-Sequenzen beschränkt. Es kann eine beliebige Klasse (z.B. eine anonymen Klasse) angegeben werden.

Z.B. ist fullNames hier

var fullNames = firstNames.Zip(
                    lastNames,
                    ( firstName, lastName ) => new {
                                                     FirstName = firstName,
                                                     LastName = lastName
                                               }
                );

eine Sequenz von Klassen, in denen die Eigenschaften FirstName und LastName gesetzt sind.

Tags: , ,

C#

SQL Server: Von wiederhergestellten Datenbanken und verwaisten Benutzern

by wolfgang@gehirnwindung.de (Wolfgang) Mai 10, 2010 23:04

Immer wieder - jedoch insgesamt selten und aus dem Grund auch leider immer wieder mit einer Suche verbunden - brauch ich nach der Wiederherstellung einer Datenbank eine Stored Procedure, um die Benutzer aus der wiederhergestellten Datenbank und die Logins des Servers wieder in "Einklang" zu bringen.

Das Problem tritt z.B. auf, wenn die DB in einem anderem Server wiederhergestellt wird. Die Ursache ist, dass zwar evtl. der Login- und der Username gleich, die im Hintergrund wirkenden ID's aber unterschiedlich sind.

Nun. Hierfür gibt es die SP sp_change_users_login. Mit einem Administratorkonto (sysadmin oder db_owner) an der Datenbank anmelden und folgendes Ausführen:

Um eine Übersicht über die Benutzer zu bekommen, die verwaist sind

EXEC sp_change_users_login @action='Report'


Wenn der Login inkl. Passwort bereits eingerichtet ist, der Benutzer aber noch zugeordnet werden muss.

EXEC sp_change_users_login @action='Auto_Fix', @userNamePattern='user'


Wenn der Benutzer zugeordnet und ein Login mit Passwort erstellt werden muss (ist der Login bereits vorhanden, wird @password ignoriert)

EXEC sp_change_users_login @action='Auto_Fix', @userNamePattern='user', @password='password'


Man kann auch einen Benutzer mit einem Login verbinden, wenn die beiden nicht den gleiche Namen haben. Dann sieht es so aus (auch hier kann noch optional ein Passwort angegeben werden)

EXEC sp_change_users_login @action='Update_One', @userNamePattern='user', @loginName='login'

 

So, und beim nächsten Mal weiß ich, wo ich suchen muss ;)

Tags:

Microsoft SQL Server

BizTalk: Maps mit Referenzen zu externen Assemblies debuggen

by wolfgang@gehirnwindung.de (Wolfgang) Mai 05, 2010 14:25

Um BizTalk-Maps zu debuggen, die eine exterene Assembly aufrufen, reichen die Standard-Möglichkeiten nicht mehr aus. Es erscheint die Fehlermeldung

Cannot find the script or external object that implements prefix 'http://schemas.microsoft.com/BizTalk/2003/ScriptNS0'

Um das zu umgehen, müssen die externen Assemblies dem Debugger bekannt gemacht werden. Um allgemein XSLT zu debuggen kann, neben der Möglichkeit dies über das Menü "XML" -> "Debug XSLT" zu erledigen, auch das Objekt XslCompiledTransform verwendet werden. Hierdurch ist man wesentlich flexibler und kann z.B. StartParameter angeben und/oder eben Extension Objects". Und da eine BizTalk-Map letztendlich auch nur ein XSLT-Dokument darstellt, gilt das auch dafür.

Als erstes sollte man ein C#-Projekt zur BizTalk-Solution hinzufügen. Darin wird lediglich unten stehende Methode eingefügt und die "Main"-Methode so angepasst, dass ein Aufruf der Methode "DebugXslt" mit den jeweiligen Parametern erfolgt.

/// <summary>
/// Helper Method to debug Maps with Extension Objects
/// </summary>
/// <param name="mapXslt">Path to xslt-file generated by the MapperCompiler (Validate Map)</param>
/// <param name="inputXml">Path to input-file (Test Map)</param>
/// <param name="extensionObjectsXml">Path to Extension Object XML (Validate Map)</param>
static void DebugXslt( string mapXslt, string inputXml, string extensionObjectsXml ) {
    if( !string.IsNullOrEmpty( mapXslt ) && !string.IsNullOrEmpty( inputXml ) && File.Exists( mapXslt ) && File.Exists( inputXml ) ) {
        XslCompiledTransform xslt = new XslCompiledTransform( true );

        // allow embedded scripts and document()-function
        XsltSettings xsltSettings = new XsltSettings( true, true );

        // Load the style sheet.
        xslt.Load( mapXslt, xsltSettings, null );

        // resolve extension objects
        XsltArgumentList xsltArgs = new XsltArgumentList( );
        if( !string.IsNullOrEmpty(extensionObjectsXml) && File.Exists( extensionObjectsXml ) ) {
            XmlReaderSettings rsettings = new XmlReaderSettings( );
            rsettings.IgnoreComments = true;
            rsettings.IgnoreProcessingInstructions = true;
            rsettings.IgnoreWhitespace = true;

            using( XmlReader reader = XmlReader.Create( extensionObjectsXml, rsettings ) ) {
                reader.ReadStartElement( "ExtensionObjects" );

                do {
                    string ns = "", an = "", cn = "";
                    while( reader.MoveToNextAttribute( ) ) {
                        switch( reader.LocalName ) {
                            case "Namespace":
                                reader.ReadAttributeValue( );
                                ns = reader.Value;
                                break;
                            case "AssemblyName":
                                reader.ReadAttributeValue( );
                                an = reader.Value;
                                break;
                            case "ClassName":
                                reader.ReadAttributeValue( );
                                cn = reader.Value;
                                break;
                        }
                    }

                    // load type and add instance to xslt-Arguments
                    if( !string.IsNullOrEmpty( ns ) && !string.IsNullOrEmpty( an ) && !string.IsNullOrEmpty( cn ) ) {
                        Type t = Type.GetType( Assembly.CreateQualifiedName( an, cn ) );
                        xsltArgs.AddExtensionObject( ns, System.Activator.CreateInstance( t, false ) );
                    }
                } while( reader.ReadToNextSibling( "ExtensionObject" ) );

                reader.Close( );
            }
        }

        // Execute the transformation.
        using( XmlWriter writer = XmlWriter.Create( TextWriter.Null ) ) {
            xslt.Transform( inputXml, xsltArgs, writer );
            writer.Close( );
        }

        xsltArgs.Clear( );
    }
}

Parameter Beschreibung
mapXslt Pfad zur XSLT-Datei
inputXml Pfad zur der Datei, die die Testdaten enthält
extensionObjectsXml

Pfad zur Datei, in der die Erweiterungsobjekte stehen (darf leer sein)

Nun müssen noch die einzelnen Pfade ermittelt werden. Mittels "Validate Map" (rot im Bild) werden die XSLT-Datei und die extensionObjects-Datei erstellt. Im Output-Window erscheinen die jeweiligen Pfade und müssen nur noch übernommen werden.
Hat man kein XML-Dokument, mit dem man Testen kann, sondern ein natives Format, so hilft "Test Map". Auch hier erscheint im Output-Window der Pfad mit dem Hinweis "Test Map used the following file".

 

Sind nun alle 3 Pfade ermittelt und eingetragen, kann es auch endlich losgehen. Damit der Debugger nicht einfach durchläuft, sollte die XSLT-Datei geöffnet und eine Haltepunkt eingefügt werden.

 

Nun muss das Projekt gestartet werden. Dazu mit der rechten Maustaste auf das Projekt zeigen und "Debug" -> "Start new instance" ausführen.
Alternativ das Projekt als StartUp-Project einrichten und F5 drücken...

Tags: , ,

Microsoft BizTalk Server

The Type initializer for '{0}' threw an exception - oder warum die Reihenfolge statischer Felder nicht egal ist...

by wolfgang@gehirnwindung.de (Wolfgang) April 29, 2010 18:06

Heute hab ich in einem Projekt meine Klassen etwas aufgeräumt und dabei auch die Reihenfolge der Methoden, Eigenschaften und Variablen angepasst. Der Build des Projekts war erfolgreich - die Tests danach aber nicht. Es erschien der Fehler:

The Type initializer for '{0}' threw an exception.

Da ich mir aber sicher war, dass ich wirklich nur Elemente auf Klassenebene verschoben hab, hat mich das wirklich sehr verwundert... Aus dem StackTrace konnte man entnehmen, dass es sich um den statische Konstruktor (.cctor()) handelt. Nun, ich schreibe oft statische Konstruktoren, aber in aller Regel ohne jeglichen Inhalt, weswegen der Grad der Verwunderung dementsprechend stieg.

Kurzum, der Fehler liegt wirklich an der Reihenfolge, allerdings geht es hierbei ausschließlich um statische Felder, die in der richtigen Reihenfolge definiert sein müssen. Bei genauerer Betrachtung ist das auch nur logisch. Ich hätte mir an der Stelle eine schöne Kompiler-Fehlermeldung gewünscht.

Ein einfaches Beispiel zeigt, worum es genau geht.

public class Test {
    private static readonly decimal x = 1 / y;
    private static readonly int y = 8;

    static Test() { }
}

Sobald eine Instanz der Klasse erstellt wird, kommt es zu einer Division durch 0 (bzw. zum Versuch desselben) und damit zu besagter Ausnahme.
Zur Veranschaulichung hier noch das Äquivalent. y ist bei der ersten Verwendung noch mit dem default-Wert belegt - und der ist nunmal 0.

public class Test {
    private static readonly decimal x;
    private static readonly int y;

    static Test() {
        x = 1 / y;
        y = 8;
    }
}

Damit ist aber auch klar, dass man die Initialisierung statischer Variablen innerhalb des statischen Konstruktors selbst vornehmen sollte - zumindest wenn diese von anderen Variablen abhängen.

Tags:

C#

BizTalk: Zugriff auf UNB-Segment und AS2-Eigenschaften innerhalb Orchestration

by wolfgang@gehirnwindung.de (Wolfgang) Februar 15, 2010 21:30

In einer Orchestration bekommt man es normalerweise nur noch mit den eigentlichen Daten einer Nachricht zu tun. So sind bei EDIFACT-Nachrichten z.B. die UNA- und UNB-Segmente nicht Teil des Schemas. Weiterhin fehlen auch die AS2-Header und eben alles, was nicht unbedingt benötigt wird. Das hat seine Vorteile (ist aufgeräumt), manchmal wird aber auch der Zugriff auf diese Daten benötigt.

Läuft eine Nachricht mal auf einen Fehler, so kann man in den "Message Details" erkennen, dass verschiedene "Context-Properties" aus eben diesen sonst nicht sichtbaren Segmenten (oder aus dem Protokoll) zu den einzelnen Nachrichten zugewiesen sind.

Auf diese Kontexteigenschaften kann man mittels Message(ContextProperty) innerhalb einer BizTalk Server Expression Shape in Orchestration Expression Shape zugreifen. Im Falle von EDIFACT-Eigenschaften muss aber - damit man die Auswahl der Context Properties überhaupt zu sehen bekommt - noch eine Referenz auf die DLL Microsoft.BizTalk.Edi.BaseArtifacts (oft zu finden unter C:\Program Files\Microsoft BizTalk Server 2006\Microsoft.BizTalk.Edi.BaseArtifacts.dll) zum Projekt hinzugefügt werden.

Mit dieser Referenz sind dann Kontexteigenschaften in den Namespaces EDI (für EDIFACT und X12 - Eigenschaften) und EdiIntAs (für AS2 - Eigenschaften) vorhanden.

Tags: , , ,

Microsoft BizTalk Server

BizTalk: Checkliste für das Senden von AS2-Nachrichten

by wolfgang@gehirnwindung.de (Wolfgang) Januar 20, 2010 21:29

Nach der Checkliste für das Empfangen von Nachrichten über AS2, muss quasi ja auch eine Checkliste für das Senden bereitgestellt werden ;) Diese Liste ist wesentlich kürzer, wenn es auch mehr Möglichkeiten gibt, als hier dargestellt...

  1. Send Port
    - für asynchrone MDN: One-way
    - für synchrone MDN: Solicit-Response
    • Type: HTTP
      • Destionation URL: Die URL, die beim Partner eingerichtet wird (auf beiden Seiten sollte die URL exakt gleich angegeben werden - inkl. Groß-/Kleinschreibung)
      • Enabled chunked encoding deaktivieren
    • Send pipeline
      • AS2Send (für alle möglichen Daten),
      • AS2EDISend (für EDI-Daten) oder
      • eine eigene Send Pipeline, in der die AS2 Encoder-Component verwendet wird
  2. Sender Party
    • General
      • Send Ports (den oben erstellen Port auswählen, alternativ geht die Zuweisung auch über einen Role Link oder wie unten beschrieben über den Alias-Namen)
    • AS2 Properties -> Party as AS2 Message Receiver (nicht verwirren lassen)
      • Einstellungen ab "Outbound AS2 Message" entsprechend der Vereinbarung mit dem Partner einstellen
      • AS2-From und AS2-To erscheinen wie hier angegeben im Head-Bereich der Nachricht und damit beim Partner
  3. Asynchrone MDNs müssen - wie eine beliebig andere AS2-Nachricht - empfangen werden können. An welche Adresse die MDN geschickt wird, kann in den Party-Einstellungen angegeben (Receipt-Delivery-Option) werden. Um die Verarbeitung dieser Nachrichten kümmert sich allerdings der BizTalk-Server selbst.

Die Party Resolution für ausgehende Nachrichten (also das Finden der Party, die die Nachricht empfängt und bei der die entsprechenden Einstellungen vorgenommen wurden) kann auch anders erfolgen. Das zuweisen des Send-Ports zu einer Party wird aber (meiner Erfahrung nach) sehr häufig verwendet. Es sollte allerdings auch ausreichen, einer Party einen EDIINT-AS2-To - Alias und der ausgehenden Nachricht den AS2-To - Header zuzuweisen - getestet hab ich das noch nicht.

Für die Signierung und Verschlüsselung der Nachricht und der Signierung der MDN müssen zusätzlich Zertifikate installiert und an bestimmten Stellen ausgewählt werden, die in dieser kleinen Übersicht beschrieben werden.

Tags: ,

Microsoft BizTalk Server

ASP.NET: Precompilation und die Fehlermeldung Directory '{0}' does not exist. Failed to start monitoring file changes

by wolfgang@gehirnwindung.de (Wolfgang) Januar 18, 2010 00:26

Unter etwas besonderen Umständen kommt es beim Aufruf einer ASP.NET Seite zu dieser Fehlermeldung.

System.Web.HttpException (0x80070002): Directory '{0}' does not exist. Failed to start monitoring file changes.

  • System.Web.FileChangesMonitor.FindDirectoryMonitor(String dir, Boolean addIfNotFound, Boolean throwOnError) +334
  • System.Web.FileChangesMonitor.StartMonitoringPath(String alias, FileChangeEventHandler callback, FileAttributesData& fad) +805
  • System.Web.Caching.CacheDependency.Init(Boolean isPublic, String[] filenamesArg, String[] cachekeysArg, CacheDependency dependency, DateTime utcStart) +2558
  • System.Web.Hosting.MapPathBasedVirtualPathProvider.GetCacheDependency(String virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) +334
  • System.Web.ResponseDependencyList.CreateCacheDependency(CacheDependencyType dependencyType, CacheDependency dependency) +539
  • System.Web.HttpResponse.CreateCacheDependencyForResponse(CacheDependency dependencyVary) +62
  • System.Web.Caching.OutputCacheModule.InsertResponse(HttpResponse response, HttpContext context, String keyRawResponse, HttpCachePolicySettings settings, CachedVary cachedVary, CachedRawResponse memoryRawResponse) +758
  • System.Web.Caching.OutputCacheModule.OnLeave(Object source, EventArgs eventArgs) +8782064
  • System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +68
  • System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

Ursache

Damit es überhaupt dazu kommen kann, müssen folgende Bedingungen gegeben sein:

  • Vorkompilierung / Precompilation
  • Webseite nicht aktualisierbar / Website not updatable
  • WebUserControl und/oder MasterPage in eigenem Verzeichnis
  • Verwendung von OutputCache

Das Problem ist, dass für MasterPages und WebUserControls keine Dummy-Datei vorgehalten werden müssen, da diese über den IIS nicht direkt aufgerufen werden. Dadurch werden aber auch Verzeichnisse, in denen sonst keine anderen Dateien liegen, nicht erstellt. Bei der Verwendung von OutputCache werden nun alle abhängigen Dateien überprüft. Dabei ist es kein Problem, wenn eine Datei fehlt - das Verzeichnis muss allerdings vorhanden sein.

Lösung

Die Lösung ist denkbar einfach. Die Verzeichnisse müssen angelegt werden...

Innerhalb eines Web Deployment Projekts (*.wdproj) kann man dazu folgendes Target verwenden. In leicht abgewandelter Form (die Pfade und Eigenschaften müsste man anpassen) geht es so natürlich auch innerhalb eines beliebig anderen MSBuild-Projekts.

<Target Name="CreateEmptyDirectoriesForOutputCache"
       Condition="'@(EnableUpdateable)' != 'true'">
   <!--
   use this target to create empty directories as a workaround for a bug in
   precompilation in conjunction with @OutputCache and updatable=false

   see http://gehirnwindung.de/post/2010/01/18/ASPNET-Precompilation-und-die-Fehlermeldung-Directory-does-not-exist-Failed-to-start-monitoring-file-changes.aspx for more details
   -->
   <CreateItem Condition="'$(EnableCopyBeforeBuild)' != 'true' and '@(ExcludeFromBuild)' == ''"
          Include="$(SourceWebPhysicalPath)\**\*.master;$(SourceWebPhysicalPath)\**\*.ascx"
          Exclude="$(SourceWebPhysicalPath)\**\.svn\**\*.*">
      <Output ItemName="EmptyFilePaths" TaskParameter="Include" />
   </CreateItem>
   <CreateItem Condition="'$(EnableCopyBeforeBuild)' == 'true' or '@(ExcludeFromBuild)' != ''"
          Include="$(CopyBeforeBuildTargetPath)\**\*.master;$(CopyBeforeBuildTargetPath)\**\*.ascx">
      <Output ItemName="EmptyFilePaths" TaskParameter="Include" />
   </CreateItem>
   <MakeDir Directories="$(WDTargetDir)%(EmptyFilePaths.RecursiveDir)" ContinueOnError="true" />
</Target>

Damit die Verzeichnisse angelegt werden, muss das Target auch aufgerufen werden. Z.B. mittels

<Target Name="AfterBuild">
   <CallTarget Targets="CreateEmptyDirectoriesForOutputCache" />
</Target>

Falls <ExcludeFromBuild>-Tags enthalten sind (oder EnableCopyBeforeBuild=true ist), werden die *.ascx und *.master-Dateien innerhalb der Kopie (Eigenschaft CopyBeforeBuildTargetPath) gesucht. In dem Fall sollte man - falls vorhanden - mit Hilfe von <ExcludeFromBuild> z.B. Subversion-Dateien ausschließen, ansonsten werden diese Pfade auch angelegt.

Sind dagegen keine <ExcludeFromBuild>-Tags vorhanden (und die Eigenschaft EnableCopyBeforeBuild=false), dann wird das physikalische Web-Verzeichnis nach den Dateien durchsucht. Hier muss gegebenenfalls das Exclude-Attribut des ersten <CreateItem>-Tags angepasst werden. Momentan werden damit die Dateien innerhalb der .svn-Ordner ausgeblendet.

Mit Hilfe dieser Targets-Datei kann der Code einfach wiederverwendet werden. Per <Import> wird das Target innerhalb des Web Deployment Projekts geladen. Will man es für alle zukünftigen Projekte anpassen, sollte man die entsprechende Vorlage ändern (zu finden unter C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Packages\WebDeploy.wdproj).
Alternativ dazu kann man diesen Import auch innerhalb der Datei C:\Program Files (x86)\MSBuild\Microsoft\WebDeployment\v9.0\Microsoft.WebDeployment.targets einfügen, dann gilt es für alle Projekte.

Egal wo es zum Schluß nun steht, es sieht dann ungefähr so aus (lediglich der Pfad muss angepasst werden).

<Import Project="./KlugeSoftware.WebDeployment.CreateEmptyDirectoriesForOutputCache.targets" />

Ein expliziter Aufruf des Targets ist hierbei nicht nötig.

Download (1,16 kb)

Tags: , ,

ASP.NET

Powered by BlogEngine.NET 1.6.1.6
Theme by Mads Kristensen | Modified by Mooglegiant and me ;)