Today I had to add a delete function to an old ASP.NET WebForms website. It was missing because there are a lot of relational data. So the steps to delete a record are:
- Click delete button
- Proof of existing relational data
- Show the result to the customer (and ask what to do)
- Delete all or cancel the delete action
Because I'm used to work with ASP.NET MVC, I thought on jQuery to solve the problem. Async calls to an Action on a Controller class and showing the result in a modal popup are easy tasks to do in ASP.NET MVC. This because of the MVC architecture, specially the Routing Engine used in MVC.
But in this case, I had to deal with ASP.NET WebForms and no routing to a Controller class. So how to solve the problem? I saw four different ways to do it:
1) Build a WCF Service and call it with jQuery
2) Create a ashx handler and call it with jQuery
3) Set the [WebMethod] Attribute to a method in the aspx page and call it with jQuery
4) Use the ScriptManager, the [WebMethod] and [ScriptMethod] Attribute
The first option (1), building a WCF Service is to much work for this small task.
The secound option (2) is quite good. The only thing I don't like is, I have to deal with two files what makes maintainance harder. Let's take a look at this solution anyway.
The jQuery code to call the handler looks like:
$(document).ready(function () {
$("#deleteButton").click(function () {
$.ajax({
type: "POST",
url: "Handler1.ashx",
data: "{'siteId':'" + id + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
// What you like to do on success
},
fail: function (msg) {
// What you like to do on error
}
});
});
});
The Handler (ashx-File) can be added easily with Visual Studio. Just select the Generic Handler template. The code looks like:
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Check for relational data and generate the message
var message = "Generated message for the user";
context.Response.ContentType = "text/plain";
context.Response.Write(message);
}
...
}
The third option (3) is in this case my favorite. Simple and easy. The jQuery call is the same, just the URL changes to something like
...
url: "SiteName.aspx/IsDeletable",
...
where the parameter after the slash is the name of the method.
In the ASPX-File we have to add a static method and decorate it with the [WebMethod] attribute. That's it! The code looks like:
[WebMethod]
public static string IsDeletable(Guid siteId)
{
// Check for relational data and generate the message
var message = "Generated message for the user";
return message;
}
IMPORTANT: The method has to be static.
The last option is ASP.NET WebForms specific and uses the ScriptManager. In my opinion do we have a better solution with the jQuery version. Anyway, here is what you need:
Add a ScriptManager as follow:
... ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" ...
Add JavaScript code to make the call:
...
PageMethods.IsDeletable(id);
...
Notice that this is just the call. Get and handle the return value has to be added as well.
And on the method in our aspx-File we do need an additional attribute:
[System.Web.Script.Services.ScriptMethod()]
Now it's up to you to choose your right solution.
Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts
Friday, January 21, 2011
Friday, January 14, 2011
Everything just random?
Today I had to fix some code of a colleague. To test the class I've fixed, there was a mock class generating random testdata. But this testdata were always the same. So I looked also at this problem and found the following code:
for (int i = foo; i < bar; i++)
{
return new Random().Next();
}
Looks good at first glance, but this will always return the same number (at least in the same second). This is because the Random class uses in the default constructor a time-dependent default seed value.
An easy way to fix the problem would be to use the overloaded constructor and provide a unique seed value. For example just the int value i used for the for loop. This could look like:
for (int i = foo; i < bar; i++)
{
return new Random(i).Next();
}
Another way to get random numbers would be in using the Next method in a correct way. This meens on the same instance of the Random class. In this case the initialization of the Random class has to be outside the loop. The code would look like this:
var r = new Random();
for (int i = foo; i < bar; i++)
{
return new r.Next();
}
This way would definitely be better, then we need just one instance of the Random class.
Further information on the Random class can be found here http://msdn.microsoft.com/en-us/library/system.random.aspx.
for (int i = foo; i < bar; i++)
{
return new Random().Next();
}
Looks good at first glance, but this will always return the same number (at least in the same second). This is because the Random class uses in the default constructor a time-dependent default seed value.
An easy way to fix the problem would be to use the overloaded constructor and provide a unique seed value. For example just the int value i used for the for loop. This could look like:
for (int i = foo; i < bar; i++)
{
return new Random(i).Next();
}
Another way to get random numbers would be in using the Next method in a correct way. This meens on the same instance of the Random class. In this case the initialization of the Random class has to be outside the loop. The code would look like this:
var r = new Random();
for (int i = foo; i < bar; i++)
{
return new r.Next();
}
This way would definitely be better, then we need just one instance of the Random class.
Further information on the Random class can be found here http://msdn.microsoft.com/en-us/library/system.random.aspx.
Tuesday, March 11, 2008
Virtual, new and override - the differences
Auf die Frage was ist der Unterschied zwischen new und override, im konkreten Fall für ein Property verwendet, bekommt man stehts die korrekte Antwort. New versteckt das Property in der Basisklasse, override überschreibt dieses. Gut, aber was heisst das genau? Nun werden die sinnvollen Antworten doch eher rare.
Folgendes Beispiel zeigt die Funktionsweisen auf. Parent ist die Basisklasse, die zwei Properties Foo und Bar anbietet. Child ist eine Klasse die von Parent ableitet und die beiden Properties einmal mit new und einmal mit override überschreibt. In der Klasse MyClass werden zwei Instanzen von Child erzeugt, wobei im ersten Fall die Deklaration auf die Basisklasse Parent lautet. Die beiden Properties der beiden Instancen werden abgefragt und ausgedruckt.
public class MyClass
{
public static void Main()
{
Parent p = new Child();
Child c = new Child();
Console.WriteLine(p.Foo);
Console.WriteLine(p.Bar);
Console.WriteLine(c.Foo);
Console.WriteLine(c.Bar);
Console.ReadLine();
}
}
public class Parent
{
public string Foo {get{return "ParentFoo";}}
public virtual string Bar {get{return "ParentBar";}}
}
public class Child : Parent
{
public new string Foo {get{return "ChildFoo";}}
public override string Bar {get{return "ChildBar";}}
}
Das aufschlussreiche Ergebniss sieht so aus:
ParentFoo
ChildBar
ChildFoo
ChildBar
Da zwei Instancen von Child erzeugt wurden ist der Ausdruck ParentFoo doch für den einen oder anderen erstaunlich. Wie kommt das? Die Antwort liegt im Early- und Late Binding.
Im Fall von Foo wurde das Property auf der Parent Klasse nicht mit virtual deklariert. Somit wird das Early Binding verwendet. Das heisst, der Compiler legt zur Kompilierzeit fest welches Property aufgerufen wird. Da im ersten Fall die Variable als Parent deklariert ist, wird in jedem Fall (egal was für eine Instanz effektiv vorhanden ist) das Property Foo der Klasse Parent aufgerufen und im zweiten Fall das Property der Klasse Child.
Im Fall von Bar wurde das Property auf der Basisklasse mit virtual deklariert und somit das Early Binding verhindert. Das heisst, das zur Laufzeit die Instance analysiert wird und entsprechend das Property der effektiv vorhandenen Klasse (in unserem Fall Child) aufgerufen wird.
Und zum Schluss noch einen Blick auf den IL Code:
.method public hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] class Parent parent,
[1] class Child child)
L_0000: newobj instance void Child::.ctor()
L_0005: stloc.0
L_0006: newobj instance void Child::.ctor()
L_000b: stloc.1
L_000c: ldloc.0
L_000d: callvirt instance string Parent::get_Foo()
L_0012: call void [mscorlib]System.Console::WriteLine(string)
L_0017: ldloc.0
L_0018: callvirt instance string Parent::get_Bar()
L_001d: call void [mscorlib]System.Console::WriteLine(string)
L_0022: ldloc.1
L_0023: callvirt instance string Child::get_Foo()
L_0028: call void [mscorlib]System.Console::WriteLine(string)
L_002d: ldloc.1
L_002e: callvirt instance string Parent::get_Bar()
L_0033: call void [mscorlib]System.Console::WriteLine(string)
L_0038: call string [mscorlib]System.Console::ReadLine()
L_003d: pop
L_003e: ret
}
Würde man nicht erwarten, dass beim Early Binding ein call und nicht ein callvirt aufgerufen wird? Eigentlich schon. Das callvirt wird generiert, damit die Klasse auf null geprüft werden kann. Der JIT Compiler wird aber nach der null Prüfung diesen Aufruf effektiv in einen non-virtual Aufruf umsetzen.
Folgendes Beispiel zeigt die Funktionsweisen auf. Parent ist die Basisklasse, die zwei Properties Foo und Bar anbietet. Child ist eine Klasse die von Parent ableitet und die beiden Properties einmal mit new und einmal mit override überschreibt. In der Klasse MyClass werden zwei Instanzen von Child erzeugt, wobei im ersten Fall die Deklaration auf die Basisklasse Parent lautet. Die beiden Properties der beiden Instancen werden abgefragt und ausgedruckt.
public class MyClass
{
public static void Main()
{
Parent p = new Child();
Child c = new Child();
Console.WriteLine(p.Foo);
Console.WriteLine(p.Bar);
Console.WriteLine(c.Foo);
Console.WriteLine(c.Bar);
Console.ReadLine();
}
}
public class Parent
{
public string Foo {get{return "ParentFoo";}}
public virtual string Bar {get{return "ParentBar";}}
}
public class Child : Parent
{
public new string Foo {get{return "ChildFoo";}}
public override string Bar {get{return "ChildBar";}}
}
Das aufschlussreiche Ergebniss sieht so aus:
ParentFoo
ChildBar
ChildFoo
ChildBar
Da zwei Instancen von Child erzeugt wurden ist der Ausdruck ParentFoo doch für den einen oder anderen erstaunlich. Wie kommt das? Die Antwort liegt im Early- und Late Binding.
Im Fall von Foo wurde das Property auf der Parent Klasse nicht mit virtual deklariert. Somit wird das Early Binding verwendet. Das heisst, der Compiler legt zur Kompilierzeit fest welches Property aufgerufen wird. Da im ersten Fall die Variable als Parent deklariert ist, wird in jedem Fall (egal was für eine Instanz effektiv vorhanden ist) das Property Foo der Klasse Parent aufgerufen und im zweiten Fall das Property der Klasse Child.
Im Fall von Bar wurde das Property auf der Basisklasse mit virtual deklariert und somit das Early Binding verhindert. Das heisst, das zur Laufzeit die Instance analysiert wird und entsprechend das Property der effektiv vorhandenen Klasse (in unserem Fall Child) aufgerufen wird.
Und zum Schluss noch einen Blick auf den IL Code:
.method public hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] class Parent parent,
[1] class Child child)
L_0000: newobj instance void Child::.ctor()
L_0005: stloc.0
L_0006: newobj instance void Child::.ctor()
L_000b: stloc.1
L_000c: ldloc.0
L_000d: callvirt instance string Parent::get_Foo()
L_0012: call void [mscorlib]System.Console::WriteLine(string)
L_0017: ldloc.0
L_0018: callvirt instance string Parent::get_Bar()
L_001d: call void [mscorlib]System.Console::WriteLine(string)
L_0022: ldloc.1
L_0023: callvirt instance string Child::get_Foo()
L_0028: call void [mscorlib]System.Console::WriteLine(string)
L_002d: ldloc.1
L_002e: callvirt instance string Parent::get_Bar()
L_0033: call void [mscorlib]System.Console::WriteLine(string)
L_0038: call string [mscorlib]System.Console::ReadLine()
L_003d: pop
L_003e: ret
}
Würde man nicht erwarten, dass beim Early Binding ein call und nicht ein callvirt aufgerufen wird? Eigentlich schon. Das callvirt wird generiert, damit die Klasse auf null geprüft werden kann. Der JIT Compiler wird aber nach der null Prüfung diesen Aufruf effektiv in einen non-virtual Aufruf umsetzen.
Wednesday, December 19, 2007
Vom Namen zur Instanz mit DynamicMethod
Ein Kunde legt beim Logging unterschiedliche LogMessages (unterschiedliche Klassen) in einer Datenbank ab. Dabei werden der Typ der Message (der LogMessage Klassenname), die Message selber sowie weitere Felder in der Tabelle gespeichert.
Nun sollten diese Einträge gelesen und die ursprünglichen Objekte wieder hergestellt werden.
Für die erste Lösung wurde der offensichtliche Weg mit Reflection gewählt:
string s = "Namespace.Name.FromDB";
Assembly a = Assembly.GetAssembly(typeof(ThisClass));
Type t = a.GetType(s);
BaseType b = (BaseType)Activator.CreateInstance(t);
Da dies nicht sehr performant ist, wurde der Code wie folgt verbessert:
string s = "Namespace.Name.FromDB";
Assembly a = Assembly.GetAssembly(typeof(ThisClass));
Type t = a.GetType(s);
ConstructorInfo i = t.GetConstructor(Type.EmptyTypes);
BaseType b = (BaseType)i.Invoke(null);
Wobei der Klassenname und die zugehörige ConstructorInfo in einem statischen Dictionaire abgelegt werden, damit diese nicht bei jedem Aufruf neu evaluiert werden müssen. So haben wir vom zweiten bis x-ten Aufruf den gewünschten Performancegewinn. Der Code dazu sieht folgendermassen aus.
ConstructorInfo c = null;
if (_cinfo.TryGetValue(s, out c))
{
c = t.GetConstructor(Type.EmptyTypes);
lock(_cinfo)
{
if (_cinfo.ContainsKey(s))
{
_cinfo.Add(s, c);
}
}
}
Noch eleganter und viel performanter geht's mit Dynamic Methods. Der von der dynamischen Methode zurückgegebene Delegate wird wie zuvor die ConstructorInfo in einem Dictionaire abgelegt und für die Folgeaufrufe wieder verwendet:
public delegate BaseType CtorDelegate();
DynamicMethod dm = new DynamicMethod
("MyCtor", typeof(BaseType), Type.EmptyTypes, typeof(BaseType).Module);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Ret);
CtorDelegate d = (CtorDelegate)dm.CreateDelegate(typeof(CtorDelegate));
Der Aufruf ist dann genau so kurz (d wird zuvor aus dem Dictionaire gelesen) und unheimlich schnell:
t message = (t)d();
Nun muss nur noch alles in einer Factory Klasse schön verpackt werden und fertig. Mehr zu diesem Thema inklusive Performance Messungen findet man wie immer bei Google ;-)
Nun sollten diese Einträge gelesen und die ursprünglichen Objekte wieder hergestellt werden.
Für die erste Lösung wurde der offensichtliche Weg mit Reflection gewählt:
string s = "Namespace.Name.FromDB";
Assembly a = Assembly.GetAssembly(typeof(ThisClass));
Type t = a.GetType(s);
BaseType b = (BaseType)Activator.CreateInstance(t);
Da dies nicht sehr performant ist, wurde der Code wie folgt verbessert:
string s = "Namespace.Name.FromDB";
Assembly a = Assembly.GetAssembly(typeof(ThisClass));
Type t = a.GetType(s);
ConstructorInfo i = t.GetConstructor(Type.EmptyTypes);
BaseType b = (BaseType)i.Invoke(null);
Wobei der Klassenname und die zugehörige ConstructorInfo in einem statischen Dictionaire abgelegt werden, damit diese nicht bei jedem Aufruf neu evaluiert werden müssen. So haben wir vom zweiten bis x-ten Aufruf den gewünschten Performancegewinn. Der Code dazu sieht folgendermassen aus.
ConstructorInfo c = null;
if (_cinfo.TryGetValue(s, out c))
{
c = t.GetConstructor(Type.EmptyTypes);
lock(_cinfo)
{
if (_cinfo.ContainsKey(s))
{
_cinfo.Add(s, c);
}
}
}
Noch eleganter und viel performanter geht's mit Dynamic Methods. Der von der dynamischen Methode zurückgegebene Delegate wird wie zuvor die ConstructorInfo in einem Dictionaire abgelegt und für die Folgeaufrufe wieder verwendet:
public delegate BaseType CtorDelegate();
DynamicMethod dm = new DynamicMethod
("MyCtor", typeof(BaseType), Type.EmptyTypes, typeof(BaseType).Module);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Ret);
CtorDelegate d = (CtorDelegate)dm.CreateDelegate(typeof(CtorDelegate));
Der Aufruf ist dann genau so kurz (d wird zuvor aus dem Dictionaire gelesen) und unheimlich schnell:
t message = (t)d();
Nun muss nur noch alles in einer Factory Klasse schön verpackt werden und fertig. Mehr zu diesem Thema inklusive Performance Messungen findet man wie immer bei Google ;-)
Monday, November 05, 2007
Sprachfeatures C# 3.0 und wie sie die Welt verändern
Mit dem .NET Framework 3.5 kommen erneut etliche Features zur Sprache C# 3.0 hinzu, die dem Entwickler das Leben bedeutend erleichtern (ausser man ist auf der Wartungsseite).
Automatic Properties, Extension Methods, Lambda Expressions, Anonymous Types, usw. bieten dem Entwickler vollkommen neue Möglichkeiten, was zu sehr schickem Code führen kann. Bei exzessivem Anwenden nur des Features oder der Eleganz wegen kann das aber zu schwer wartbarem und unübersichtlichen Code- und Sprachauswüchsen führen.
Trotzdem, ganz hübsche ist folgendes (auch wenn nicht sehr sinnvoll ;-):
7.TimesPrint("MeinText");
Wesentlich eleganter als:
for (int i = 0; i < 7; i++)
{
Console.WriteLine("MeinText");
}
Doch damit es funktioniert braucht's noch eine Extension Method:
Public static void TimesPrint(this int no, string s)
{
for (int i = 0; i < no; i++)
{
Console.WriteLine(s);
}
}
Etwas allgemeiner formuliert könnte der Aufruf so aussehen und so auch wirklich Sinn machen (die Extension Method muss natürlich entsprechend angepasst werden):
7.Times(i => Console.WriteLine("MeinText"));
Ob die Wartbarkeit durch die schwerer erkennbare Funktionalität komplexer oder durch die elegante Syntax doch eher erleichtert wird, wird spätestens die Praxis zeigen.
So oder so, die neuen Features machen viel Spass!
PS: Eine gute Übersicht zu den neuen Features gibt's hier.
Automatic Properties, Extension Methods, Lambda Expressions, Anonymous Types, usw. bieten dem Entwickler vollkommen neue Möglichkeiten, was zu sehr schickem Code führen kann. Bei exzessivem Anwenden nur des Features oder der Eleganz wegen kann das aber zu schwer wartbarem und unübersichtlichen Code- und Sprachauswüchsen führen.
Trotzdem, ganz hübsche ist folgendes (auch wenn nicht sehr sinnvoll ;-):
7.TimesPrint("MeinText");
Wesentlich eleganter als:
for (int i = 0; i < 7; i++)
{
Console.WriteLine("MeinText");
}
Doch damit es funktioniert braucht's noch eine Extension Method:
Public static void TimesPrint(this int no, string s)
{
for (int i = 0; i < no; i++)
{
Console.WriteLine(s);
}
}
Etwas allgemeiner formuliert könnte der Aufruf so aussehen und so auch wirklich Sinn machen (die Extension Method muss natürlich entsprechend angepasst werden):
7.Times(i => Console.WriteLine("MeinText"));
Ob die Wartbarkeit durch die schwerer erkennbare Funktionalität komplexer oder durch die elegante Syntax doch eher erleichtert wird, wird spätestens die Praxis zeigen.
So oder so, die neuen Features machen viel Spass!
PS: Eine gute Übersicht zu den neuen Features gibt's hier.
Friday, October 05, 2007
Dynamic Method Performance
In einem interessanten Artikel im MSDN Magazine wird auf unterschiedliche Performance Probleme, im Speziellen im Zusammenhang mit Reflection, eingegangen. Mich interessierte vor allem die gelobte Performance der dynamischen Methoden, die seit der .NET Framework Version 2.0 zur Verfügung stehen. Eine einfache Testanwendung hat gezeigt, dass der Artikel recht behält.
In der Testanwendung wird einem Objekt ein String eine Million mal zugewiesen. Dies zuerst direkt verdrahtet, danach mit dynamischer Methode und zuletzt via Reflection. Das Ergebnis ist eindeutig. Die direkt verdrahtete Methode ist natürlich die Schnellste, die dynamische Methode braucht dafür doppelt so lange (was ich immer noch sehr gut finde) und mittels Reflection dauert es 100mal länger. What a gain!
Übrigens, die Assembly habe ich mit Lutz's Reflector untersucht, um sicher zu stellen, dass die Schlaufe auch wirklich bei allen drei Varianten ausgeführt wird.
Und hier noch der Code:
public class Program
{
private delegate void DemoDelegate(Foo arg);
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
Foo f = new Foo();
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
f.Text = "Hello World!";
}
stopwatch.Stop();
Console.WriteLine("Direct: {0}", stopwatch.Elapsed);
stopwatch.Reset();
DemoDelegate del = createMethod();
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
del(f);
}
stopwatch.Stop();
Console.WriteLine("Dynamic method: {0}", stopwatch.Elapsed);
stopwatch.Reset();
PropertyInfo property = f.GetType().GetProperty("Text");
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
property.SetValue(f, "Hello World!", null);
}
stopwatch.Stop();
Console.WriteLine("Reflection: {0}", stopwatch.Elapsed);
Console.ReadKey();
}
private static DemoDelegate createMethod()
{
Type[] parameterTypes = new Type[] { typeof(Foo) };
DynamicMethod method = new DynamicMethod("Demo", null, parameterTypes, typeof(Program));
ILGenerator iLGenerator = method.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldstr, "Hello World!");
MethodInfo setMethod = typeof(Foo).GetProperty("Text").GetSetMethod();
iLGenerator.EmitCall(OpCodes.Call, setMethod, null);
iLGenerator.Emit(OpCodes.Ret);
return (DemoDelegate)method.CreateDelegate(typeof(DemoDelegate));
}
}
public class Foo
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; }
}
}
In der Testanwendung wird einem Objekt ein String eine Million mal zugewiesen. Dies zuerst direkt verdrahtet, danach mit dynamischer Methode und zuletzt via Reflection. Das Ergebnis ist eindeutig. Die direkt verdrahtete Methode ist natürlich die Schnellste, die dynamische Methode braucht dafür doppelt so lange (was ich immer noch sehr gut finde) und mittels Reflection dauert es 100mal länger. What a gain!
Übrigens, die Assembly habe ich mit Lutz's Reflector untersucht, um sicher zu stellen, dass die Schlaufe auch wirklich bei allen drei Varianten ausgeführt wird.
Und hier noch der Code:
public class Program
{
private delegate void DemoDelegate(Foo arg);
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
Foo f = new Foo();
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
f.Text = "Hello World!";
}
stopwatch.Stop();
Console.WriteLine("Direct: {0}", stopwatch.Elapsed);
stopwatch.Reset();
DemoDelegate del = createMethod();
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
del(f);
}
stopwatch.Stop();
Console.WriteLine("Dynamic method: {0}", stopwatch.Elapsed);
stopwatch.Reset();
PropertyInfo property = f.GetType().GetProperty("Text");
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
property.SetValue(f, "Hello World!", null);
}
stopwatch.Stop();
Console.WriteLine("Reflection: {0}", stopwatch.Elapsed);
Console.ReadKey();
}
private static DemoDelegate createMethod()
{
Type[] parameterTypes = new Type[] { typeof(Foo) };
DynamicMethod method = new DynamicMethod("Demo", null, parameterTypes, typeof(Program));
ILGenerator iLGenerator = method.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldstr, "Hello World!");
MethodInfo setMethod = typeof(Foo).GetProperty("Text").GetSetMethod();
iLGenerator.EmitCall(OpCodes.Call, setMethod, null);
iLGenerator.Emit(OpCodes.Ret);
return (DemoDelegate)method.CreateDelegate(typeof(DemoDelegate));
}
}
public class Foo
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; }
}
}
Friday, September 14, 2007
Remote Debbuging
Wenn man eine Applikation remote debuggen soll, wird man feststellen, dass verschiedene Punkte beachtet werden müssen, damit das Ziel erreicht wird. Einige hoffentlich nützliche Hinweise möchte ich hiermit weitergeben:
1. Remotedebugging heisst, mit dem lokalen Visual Studio eine laufende Applikation auf einer anderen Maschine, typischerweise einem Server, zu debuggen.
2. Der Applikationscode (dll und pdb Files) muss auf beiden Systemen identisch sein.
3. Auf der Remotemaschine muss der Remote Debugger, im Speziellen das File msvsmon.exe vorhanden sein. Auf der lokalen Maschine muss Visual Studio vorhanden sein.
4. Der lokale Account (unter dem Visual Studio läuft) muss auf dem Remotesystem Administratorrechte haben, damit sich Visual Studio attachen kann.
5. Auf dem Remotesystem muss der Remotedebugger (msvsmon.exe) unter dem lokalen Account (unter demselben wie Visual Studio läuft), gestartet werden (im Contextmenü Run as ... ausführen).
6. In Visual Studio muss unter Tools, Options, Debugging, General die Option ‚Enable just my Code’ inaktiv sein, damit die Symbols geladen werden und Breakepoints gesetzt werden können.
7. In Visual Studio kann nun unter Debug, Attach to Process der Remoteprozess angebunden werden. Dazu Transport: Default und Qualifier: SERVERNAME wählen. Nun den gewünschten Prozess, z. Bsp. W3wp.exe auswählen. Ist der gewünschte Prozess nicht aufgelistet, muss sichergestellt werden, dass die Applikation wirklich läuft. Dazu z. Bsp. Die Webseite aufrufen.
8. Nun wie gewohnt debuggen.
Viel Spass.
1. Remotedebugging heisst, mit dem lokalen Visual Studio eine laufende Applikation auf einer anderen Maschine, typischerweise einem Server, zu debuggen.
2. Der Applikationscode (dll und pdb Files) muss auf beiden Systemen identisch sein.
3. Auf der Remotemaschine muss der Remote Debugger, im Speziellen das File msvsmon.exe vorhanden sein. Auf der lokalen Maschine muss Visual Studio vorhanden sein.
4. Der lokale Account (unter dem Visual Studio läuft) muss auf dem Remotesystem Administratorrechte haben, damit sich Visual Studio attachen kann.
5. Auf dem Remotesystem muss der Remotedebugger (msvsmon.exe) unter dem lokalen Account (unter demselben wie Visual Studio läuft), gestartet werden (im Contextmenü Run as ... ausführen).
6. In Visual Studio muss unter Tools, Options, Debugging, General die Option ‚Enable just my Code’ inaktiv sein, damit die Symbols geladen werden und Breakepoints gesetzt werden können.
7. In Visual Studio kann nun unter Debug, Attach to Process der Remoteprozess angebunden werden. Dazu Transport: Default und Qualifier: SERVERNAME wählen. Nun den gewünschten Prozess, z. Bsp. W3wp.exe auswählen. Ist der gewünschte Prozess nicht aufgelistet, muss sichergestellt werden, dass die Applikation wirklich läuft. Dazu z. Bsp. Die Webseite aufrufen.
8. Nun wie gewohnt debuggen.
Viel Spass.
http und Textfile
Für einen Kunden sollte ich von einer Webseite (http://.../mytext.php) ein File downloaden und den Text auf einem Server Share speichern. Und so einfach ist das mit .NET 2.0.
// Get stream from any webserver over HTTP response object
WebRequest request = (WebRequest)WebRequest.Create(new Uri("http://.../mytext.php"));
WebResponse response = request.GetResponse();
// create reader to read the stream and save it in a string
StreamReader reader = new StreamReader(response.GetResponseStream());
// write string to a file
File.WriteAllText(@"c:/test.txt", reader.ReadToEnd());
// close objects
reader.Close();
response.Close();
// Get stream from any webserver over HTTP response object
WebRequest request = (WebRequest)WebRequest.Create(new Uri("http://.../mytext.php"));
WebResponse response = request.GetResponse();
// create reader to read the stream and save it in a string
StreamReader reader = new StreamReader(response.GetResponseStream());
// write string to a file
File.WriteAllText(@"c:/test.txt", reader.ReadToEnd());
// close objects
reader.Close();
response.Close();
Tuesday, October 24, 2006
C# 3.0 Extensions
In einer Diskussion über Mittag zum Thema C# 3.0 wurde nach den Verbesserungen der neuen Version gefragt. Aus diesem Grund hier eine kurze Übersicht und ein Dokument zum Thema von Microsoft.
· Implicitly typed local variables, which permit the type of local variables to be inferred from the expressions used to initialize them.
· Extension methods, which make it possible to extend existing types and constructed types with additional methods.
· Lambda expressions, an evolution of anonymous methods that provides improved type inference and conversions to both delegate types and expression trees.
· Object initializers, which ease construction and initialization of objects.
· Anonymous types, which are tuple types automatically inferred and created from object initializers.
· Implicitly typed arrays, a form of array creation and initialization that infers the element type of the array from an array initializer.
· Query expressions, which provide a language integrated syntax for queries that is similar to relational and hierarchical query languages such as SQL and XQuery.
· Expression trees, which permit lambda expressions to be represented as data (expression trees) instead of as code (delegates).
Und hier das vollständige Dokument.
· Implicitly typed local variables, which permit the type of local variables to be inferred from the expressions used to initialize them.
· Extension methods, which make it possible to extend existing types and constructed types with additional methods.
· Lambda expressions, an evolution of anonymous methods that provides improved type inference and conversions to both delegate types and expression trees.
· Object initializers, which ease construction and initialization of objects.
· Anonymous types, which are tuple types automatically inferred and created from object initializers.
· Implicitly typed arrays, a form of array creation and initialization that infers the element type of the array from an array initializer.
· Query expressions, which provide a language integrated syntax for queries that is similar to relational and hierarchical query languages such as SQL and XQuery.
· Expression trees, which permit lambda expressions to be represented as data (expression trees) instead of as code (delegates).
Und hier das vollständige Dokument.
Monday, May 29, 2006
C# Snippets - Einsatz oder Missbrauch
Wieder ein Referat, wieder Demos, wieder viel Copy/Paste. Falsch. Dieses Mal habe ich alle vorbereiteten Codeteile als Snippets hinterlegt und mir so nicht nur viel Copy/Past Arbeit erspart, sondern das Publikum zum Staunen gebracht. Und so geht's.
Den Snippet Manager, der in der Professional Edition unter Tools liegt suchte ich vergeblich in meiner Team Edition. Da hilft der Shortcut Ctrl+K, Ctrl+B oder den Eintrag über Customize dem Menü wieder hinzufügen.
Danach ein bestehendes Snippet (Name.snippet im XML-Format) in VS öffnen, anpassen und speichern.
Die neuen Snippets über den Manager einbinden.
Fertig zum Einsatz!
Übrigens gibt es die für VB Entwickler im VS integrierten Snippets hier auch für C# Entwickler zum downloaden.
Den Snippet Manager, der in der Professional Edition unter Tools liegt suchte ich vergeblich in meiner Team Edition. Da hilft der Shortcut Ctrl+K, Ctrl+B oder den Eintrag über Customize dem Menü wieder hinzufügen.
Danach ein bestehendes Snippet (Name.snippet im XML-Format) in VS öffnen, anpassen und speichern.
Die neuen Snippets über den Manager einbinden.
Fertig zum Einsatz!
Übrigens gibt es die für VB Entwickler im VS integrierten Snippets hier auch für C# Entwickler zum downloaden.
Subscribe to:
Posts (Atom)