Archiv für den Monat Juli 2012

EF4 SQL Abfragen anzeigen für ObjectContext & DbContext


Wer häufig mit dem Entity Framework arbeitet kommt doch hin und wieder mal in die Verlegenheit und möchte gern wissen welche SQL Abfrage hier genau an den Server gesendet wurde. Wenn man hier nicht stolzer Besitzer eines Visual Studios Ultimate mit Intellitrace ist, muss man sich auf andere Weiße weiterhelfen.

Zum einen gibt es den klassischen ObjectContext und den DbContext und für beide Varianten gibt es die unterschiedlichsten Möglichkeiten sich die jeweiligen SQL Abfragen anzuzeigen.

Der ObjectContext

Hier gibt es keine native Möglichkeit sich den SQL Code anzuzeigen. Aber es gibt eine sehr einfache Lösung mit Hilfe einer Extension Methode. Die habe ich im MSDN Forum gefunden:

http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/2a50ffd2-ed73-411d-82bc-c9c564623cb4/

Im Post findet man die Folgenden beiden Funktionen, mit denen man problemlos die jeweiligen SQL Befehle auslesen kann. Wer Probleme hat diese zum Laufen zu bekommen, evtl. mal in den Kommentaren im MSDN Forum weiterlesen, diese haben mir auch geholfen.

 public static string ToTraceString(this IQueryable query)
        {
            System.Reflection.MethodInfo toTraceStringMethod = query.GetType().GetMethod("ToTraceString");

            if (toTraceStringMethod != null)
                return toTraceStringMethod.Invoke(query, null).ToString();
            else
                return "";
        }
        
        public static string ToTraceString(this ObjectContext ctx)
        {
            Assembly entityAssemly = Assembly.Load(entityAssemblyName);

            Type updateTranslatorType = entityAssemly.GetType(
                "System.Data.Mapping.Update.Internal.UpdateTranslator");

            Type functionUpdateCommandType = entityAssemly.GetType(
                "System.Data.Mapping.Update.Internal.FunctionUpdateCommand");

            Type dynamicUpdateCommandType = entityAssemly.GetType(
                "System.Data.Mapping.Update.Internal.DynamicUpdateCommand");

            object[] ctorParams = new object[]
                        {
                            ctx.ObjectStateManager,
                            ((EntityConnection)ctx.Connection).GetMetadataWorkspace(),
                            (EntityConnection)ctx.Connection,
                            ctx.CommandTimeout
                        };

            object updateTranslator = Activator.CreateInstance(updateTranslatorType,
                BindingFlags.NonPublic | BindingFlags.Instance, null, ctorParams, null);

            MethodInfo produceCommandsMethod = updateTranslatorType
                .GetMethod("ProduceCommands", BindingFlags.Instance | BindingFlags.NonPublic);
            object updateCommands = produceCommandsMethod.Invoke(updateTranslator, null);

            List<DbCommand> dbCommands = new List<DbCommand>();

            foreach (object o in (IEnumerable)updateCommands)
            {
                if (functionUpdateCommandType.IsInstanceOfType(o))
                {
                    FieldInfo m_dbCommandField = functionUpdateCommandType.GetField(
                        "m_dbCommand", BindingFlags.Instance | BindingFlags.NonPublic);

                    dbCommands.Add((DbCommand)m_dbCommandField.GetValue(o));
                }
                else if (dynamicUpdateCommandType.IsInstanceOfType(o))
                {
                    MethodInfo createCommandMethod = dynamicUpdateCommandType.GetMethod(
                        "CreateCommand", BindingFlags.Instance | BindingFlags.NonPublic);

                    object[] methodParams = new object[]
                    {
                        updateTranslator,
                        new Dictionary<int, object>()
                    };

                    dbCommands.Add((DbCommand)createCommandMethod.Invoke(o, methodParams));
                }
                else
                {
                    throw new NotSupportedException("Unknown UpdateCommand Kind");
                }
            }


            StringBuilder traceString = new StringBuilder();
            foreach (DbCommand command in dbCommands)
            {
                traceString.AppendLine("=============== BEGIN COMMAND ===============");
                traceString.AppendLine();

                traceString.AppendLine(command.CommandText);
                foreach (DbParameter param in command.Parameters)
                {
                    traceString.AppendFormat("{0} = {1}", param.ParameterName, param.Value);
                    traceString.AppendLine();
                }

                traceString.AppendLine();
                traceString.AppendLine("=============== END COMMAND ===============");
            }

            return traceString.ToString();
        }

Hier ist es nun möglich die SQL Abfragen für Selects zu sehen und auch die Abfragen für Update, Insert und Delete. Das ganze kann dann z.B. folgendermaßen eingesetzt werden:

        public static void ObjectContextTrace()
        {
            //Neues Datenmodell erstellen, Achtung es muss nur ConnectionString in der App.Config "ModelContainer" angepasst werden.
            //die DB wird dann von "allein" angelegt.
            ModelContainer model = new ModelContainer();

            //Das Anlegen eines Objektes auslesen als SQL Statement
            Person person = new Person() { Name = "Müller", Vorname = "Max" };
            model.PersonMenge.AddObject(person);
            Console.Write(model.ToTraceString());
            model.SaveChanges();
            Console.ReadLine();

            //Eine Abfrage auslesen als SQL Statement.
            var namen = from ePerson in model.PersonMenge where ePerson.Name == "Müller" select ePerson;
            Console.Write(namen.ToTraceString());
            Console.ReadLine();
        }

Der DbContext

Um sich die SQL Abfragen für einen DBContext anzusehen, bietet .NET für Select Abfragen bereits eine mitgelieferte Möglichkeit, hier kann einfach “ToString()” auf das passende Query angewendet werden:

//Abfrage erstellen
var houses = from eHouse in modelDbContext.HausMenge where eHouse.Nummer == "31a" select eHouse;
Console.Write(houses.ToString());

Möchte man hier auch Update, Insert und Delete SQL Statements angezeigt bekommen, muss man einen Tracing und Caching Provider fürs das EF Installieren. Dieser steht auf CodePlex zum Download mittels NuGet bereit und muss unserem Projekt hinzugefügt werden. Auf der Homeseite von CodePlex ist in einfachen vier Schritten erläutert was genau gemacht werden muss, damit man die SQL Ausgaben bekommt.

Bei mir sieht das ganze dann z.B: folgendermaßen aus:

 public static void DbContextTrace()
 {
      //Einstellen wo das Log ausgegeben werden soll
      EFTracingProviderConfiguration.LogToConsole = true;

      //http://code.msdn.microsoft.com/EFProviderWrappers
      //Neues Datenmodell erstellen, Achtung es muss nur ConnectionString in der App.Config "ModelContainer" angepasst werden.
      ModelDbContextContainer modelDbContext = new ModelDbContextContainer("ModelDbContextContainer");
      //Beim Ersten ausführen auskommentieren und evtl. mit CreateDB ersetzten, macht probleme mit dem Wrapper
      //modelDbContext.Database.CreateIfNotExists();

      //Neues Objekt erstellen und der DB hinzufügen
      Haus haus = new Haus() { Nummer = "31a", Strasse = "Altenberger Straße" };
      modelDbContext.HausMenge.Add(haus);
       //Wird auf der Konsole ausgegeben, wenn Save Changes aufgerufen wird.
       modelDbContext.SaveChanges();
}

Wenn man jetzt die Ausgabe im Visual Studio beachtet, dann wird dort ausgegeben welche SQL Statement abgesetzt wurden und wie lange die jeweilige Ausführung gedauert hat. Auch Select Anweisungen sind hier zu finden, aber erst wenn diese auch wirklich ausgeführt wurden.

image

Mein Beispielprojekt auf CodePlex

Ich habe auch für dieses Beispiel ein Projekt auf CodePlex angelegt, welches man folgendermaßen finden kann:

https://squadwuschel.codeplex.com/

Dann unter “Source Code” –> “Browse” –> “Testprojekte” –> “EFTraceTest”

Hier könnt Ihr den kompletten Quelltext finden. Wenn jemand noch andere bzw. einfachere Möglichkeiten weiß wie man an die SQL Statements von EF Abfragen kommt, dann nicht zögern einen Kommentar abzugeben.

Minify JavaScript und CSS mit AjaxMin


Wie ich bereits in meinem letzten Post erläutert habe gibt es die unterschiedlichsten Möglichkeiten um seine Webseiten zu “verkleinern”. Hier will ich nur kurz zeigen wie schnell man sein JavaScript Code bzw. seinen CSS Code verkleinern kann.

Ich benutzte dafür AjaxMin von Microsoft, welches auf Codeplex zur Verfügung gestellt wird inklusive des Quelltextes. Wir benötigen allerdings nur die “AjaxMin.dll” welche im msi Package enthalten ist und wenn man das installiert, findet man die passende DLL z.B. unter:

C:\Program Files (x86)\Microsoft\Microsoft Ajax Minifier\AjaxMin.dll

Wenn der Standard Installationspfad gewählt wurde.

Ich habe mir hierfür ein einfaches ASP.NET MVC3 Projekt erstellt. In diesem möchte ich einfach die bereits erstellten CSS und JavaScript Dateien Minimiert direkt auf der Seite mit ausgeben. (Ich bin mir dessen bewusst das es sich dabei nicht um eine optimale Lösung handelt, da diese dann immer mit heruntergeladen werden, besser wäre hier ein Handler in dem man das Caching einstellen kann, ich glaube außerdem gelesen zu haben das dies z.B. bei MVC4 bereits integriert ist. Das ganze lässt sich aber auch simpel in einer Standard ASP.NET Anwendung unterbringen.)

Als erstes muss die AjaxMin.dll als Verweis unserem Projekt hinzugefügt werden und dann können wir schon mal in der _Layout.cshtml Datei einfach die alten Script und Style Einträge mit dem folgenden Code ersetzten:

    @Minify.Min(false).Css("Content\\Site.css")
    @Minify.Min(false).JavaScript("Scripts\\jquery-1.5.1.min.js")
    @Minify.Min(false).JavaScript("Scripts\\modernizr-1.7.min.js")

und der passende Code der Minify Klasse, damit auch die Aufrufe in der _Layout.cshtml Datei funktionieren:

public class Minify
{
    #region Static
    /// <summary>
    /// Neues Minify Objekt zurückgeben, welches dann JavaScript oder Css Minifien kann.
    /// </summary>
    /// <param name="debug">Gibt an ob alles Minified werden soll oder nicht, wenn debuggt werden soll.</param>
    public static Minify Min(bool debug)
    {
        return new Minify(debug);
    }
    #endregion

    #region Konstruktor
    private Minify(bool debug)
    {
    }
    #endregion

    #region Member
    /// <summary>
    /// Gibt an ob die Daten Minified werden sollen oder ob diese zum Debuggen normal ausgegeben werden sollen.
    /// </summary>
    public bool Debug { get; set; }
    #endregion

    #region Public Functions
    /// <summary>
    /// CSS Datei Minimieren
    /// </summary>
    /// <param name="fileName">Relativer Pfad vom Webverzeichnis aus zur Datei die Minimiert werden soll</param>
    /// <returns>Minified Stylesheet in einem Style Tag</returns>
    public MvcHtmlString Css(string fileName)
    {
        //Den Pfad heraussuchen in dem die Style Datei gefunden werden kann.
        string path = Path.Combine(HttpContext.Current.Server.MapPath(@"~/"), fileName);
        if (File.Exists(path))
        {
            //Den Style auslesen aus der Original Datei.
            string style = File.ReadAllText(path);
            if (!Debug)
            {
                //Den AjaxMinifier nutzen um die Daten zu "verkleinern"
                Minifier minifier = new Minifier();
                style = minifier.MinifyStyleSheet(style);
            }

            //Das ganze in Style Tags packen und dann zurückgeben
            return new MvcHtmlString(string.Format("<style>{0}</style>", style));
        }

        return new MvcHtmlString(string.Empty);
    }

    /// <summary>
    /// JavaScript Datei Minimieren.
    /// </summary>
    /// <param name="fileName">Relativer Pfad vom Webverzeichnis aus zur Datei die Minimiert werden soll</param>
    /// <returns>Minified JavaScript in einem Script Tag</returns>
    public MvcHtmlString JavaScript(string fileName)
    {
        //Den Pfad heraussuchen in dem die Style Datei gefunden werden kann.
        string path = Path.Combine(HttpContext.Current.Server.MapPath(@"~/"), fileName);
        if (File.Exists(path))
        {
            //Den Style auslesen aus der Original Datei.
            string script = File.ReadAllText(path);
            if (!Debug)
            {
                //Den AjaxMinifier nutzen um die Daten zu "verkleinern"
                Minifier minifier = new Minifier();
                script = minifier.MinifyJavaScript(script);
            }

            //Das ganze in Style Tags packen und dann zurückgeben
            return new MvcHtmlString(string.Format("<script>{0}</script>", script));
        }

        return new MvcHtmlString(string.Empty);
    }
    #endregion
}

Damit lässt sich der eigene CSS und JavaScript Code sehr schnell Minimieren und bei dieser evtl. auch nicht ganz glücklichen Variante hat man zumindest die Requests auf einen Request für die Seite inklusive JavaScript Dateien und CSS “optimiert”.

Das ganze Projekt könnt Ihr auch unter CodePlex finden.

https://squadwuschel.codeplex.com/

Unter: Source Code –> Browse –> Testprojekte –> MvcMinimizerTest

Leider habe ich das AjaxMin Projekt nicht bei NuGet gefunden, denn damit wäre ein Update einfacher, wenn es da jemand finden sollte, bin ich über einen Kommentar dankbar.