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

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