Archive for August, 2010

Problem z HttpCookie.Expires i jQuery

Dziś na temat korzystania z jQuery i ciasteczek. Jako scenariusz przyjmijmy proste zliczenie kliknięć, gdzie licznik przechowujemy jako wartość ciastka.

W tym celu tworzymy aplikację ASP.NET MVC wraz z testowym kontrolerem i dwoma widokami – Indeks, który jest widokiem bazowym i Modify, który zwraca kawałek html’a z zawartością ciastka.

Kotroler inicjujemy w następujący sposób:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Web;
  5 using System.Web.Mvc;
  6
  7 namespace CookieAndJQuery.Controllers
  8 {
  9     [HandleError]
10     public class TestsController : Controller
11     {
12         private static string CookieName = “TestCookie”;
13
14         public ActionResult Index()
15         {
16             return View();
17         }
18
19         public ActionResult Modify()
20         {
21             HttpCookie cookie = Request.Cookies[CookieName];
22             if (cookie == null)
23             {
24                 cookie = new HttpCookie(CookieName, “0”);
25                 cookie.Expires = DateTime.MaxValue;
26             }
27
28             int v = int.Parse(cookie.Value);
29             cookie.Value = (++v).ToString();
30             Response.Cookies.Add(cookie);
31             ViewData[“itemValue”]= cookie.Value;
32             return View();
33         }
34     }
35 }

Indeks ma za zadanie po kliknięciu przycisku wywołać akcję Modify w kontrolerze TestsController a następnie wstrzyknąć jej wynik do diva o id=”resultContainer”

  1 <%@ Page Language=“C#” Inherits=“System.Web.Mvc.ViewPage” %>
  2 <html>
  3 <head runat=“server”>
  4     <title>Index</title>
  5     <script src=“Scripts/jquery-1.4.1.min.js” type=“text/javascript”></script>
  6 </head>
  7 <body>
  8     <div>
  9         <input type=“button” id=“btnTestCookies” value=“ClickMe” />
10         <div id=“resultContainer”></div>
11     </div>
12     <script type=“text/javascript”>
13         $(“#btnTestCookies”).click(function () {
14             $.ajax(
15                 {
16                     url: “/Tests/Modify”,
17                     success: function (data) {
18                         $(“#resultContainer”).html(data);
19                     },
20                     cache: false,
21                     dataType: “html”
22                 }
23             );
24         });
25     </script>
26 </body>
27 </html>

 

Dla pełnego obrazu poniżej widnieje zawartość widoku Modify:

  1 <%@ Page Language=“C#” Inherits=“System.Web.Mvc.ViewPage” Buffer=“false” %>
  2 <div>
  3     <%= ViewData[“itemValue”] %>
  4 </div>

Skoro mamy już bazę do doświadczeń pora przejść do sedna problemu, a mianowiscie do obsługi ciastek. Jak zapewne zauważyliście kontroler w linii 25 ustawia czas wygaśnięcia ciastka na DateTime.MaxValue, czyli ciastko nigdy nie wygaśnie. Na pierwszy rzut oka nie widać problemu: wywołujemy akcję, akcja zmienia wartość ciastka, ciastko wraca do przeglądarki, klikamy ponownie, wartość wzrasta – super! Jednakże spójżmy jak faktycznie wygląda ciastko, które otrzymujemy:

Pierwsze wywołanie strony – tworzymy ciastko

Kolejne wywołanie strony – niestety jak widać poniżej czas wygaśnięcia ciastka przypadnie na koniec sesji:

Przez to po zamknięciu i ponownym uruchomieniu przeglądarki ciastko zostanie usuniete a tym samym licznik zostanie zresetowany…

Przyjrzyjmy się jak wygląda request i response. Korzystając z Fiddlera można podejrzeć treść nagłówka Cookies, w którym widać wszystko jak na dłoni:

Odpowiedź po pierwszym wywołaniu (ustawiamy ciastko)

Request przy kojelnych kliknięciach przycisku – jak widać brak przy żądaniu informacji o ekspiracji ciastka, co skuktuje opisanym zachowaniem.

Jak to ominąć? W przypadku, który miałem jako zadanie postanowiłem przy każdym takim wywołaniu ustawiać czas życia ciastka na DateTime.MaxValue, dzięki  czemu po ponownym uruchomieniu przeglądarki ciastko nadal tam jest…

  1 public ActionResult Modify()
  2 {
  3     HttpCookie cookie = Request.Cookies[CookieName];
  4     if (cookie == null)
  5     {
  6         cookie = new HttpCookie(CookieName, “0”);
  7         cookie.Expires = DateTime.MaxValue;
  8     }
  9
10     cookie.Expires = DateTime.MaxValue;
11     int v = int.Parse(cookie.Value);
12     cookie.Value = (++v).ToString();
13     Response.Cookies.Add(cookie);
14     ViewData[“itemValue”] = cookie.Value;
15     return View();
16 }

W wyniku tej modyfikacji informacja o ciastkach znajdująca się w odpowiedzi wygląda następująco

dzięki czemu przy kolejnym uruchomieniu przeglądarki ciastko nadal tam będzie gotowe do użycia :)


Przeklikaj swój świat! – kontrola zdarzeń myszki

Myszka – z pozoru urządzenie, które bez ludzkiej ręki nie jest w stanie nic zrobić. Błąd! Istnieje możliwość nadania jej “życia” poprzez wywoływanie odpowiednich zdarzeń z kodu aplikacji. Aby to osiągnąć przydatne może być wykorzystanie poniższego helpera, a właściwie szablonu helpera, który można w łatwy sposób rozszerzyć o kolejne metody:

  1 using System.Drawing;
  2 using System.Runtime.InteropServices;
  3 using System.Windows.Forms;
  4
  5 namespace ConsAppClicker
  6 {
  7     public static class MouseHelper
  8     {
  9         #region Internal
10         [DllImport(“user32.dll”, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
11         private static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
12
13         private const int MOUSEEVENTF_LEFTDOWN = 0x02;
14         private const int MOUSEEVENTF_LEFTUP = 0x04;
15         private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
16         private const int MOUSEEVENTF_RIGHTUP = 0x10;
17         #endregion
18
19         #region LMB
20         public static void RaiseDoubleClick()
21         {
22             int x = Cursor.Position.X;
23             int y = Cursor.Position.Y;
24             RaiseLeftClick(x, y);
25             RaiseLeftClick(x, y);
26         }
27
28         public static void RaiseLeftClick()
29         {
30             int x = Cursor.Position.X;
31             int y = Cursor.Position.Y;
32             RaiseLeftClick(x, y);
33         }
34
35 private static void RaiseLeftClick(int x, int y)
36 {
37     mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, x, y, 0, 0);
38 }
39         #endregion
40
41         #region RMB
42         public static void RaiseRightClick()
43         {
44             int x = Cursor.Position.X;
45             int y = Cursor.Position.Y;
46             RaiseRightClick(x, y);
47         }
48
49         public static void RaiseRightClick(int x, int y)
50         {
51             mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, x, y, 0, 0);
52         }
53         #endregion
54
55         #region Position
56         public static void SetPosition(int x, int y)
57         {
58             Cursor.Position = new Point(x, y);
59         }
60         #endregion
61     }
62 }
63

Oferuje on możliwość ustawienia odpowiedniej pozycji kursora myszy oraz wywołania najważniejszych jej zdarzeń dla aktualnej pozycji. Jak widać przy pomocy PInvoke możemy wykorzystać wywołanie zdarzenia znajdujące się w bibliotece user32.dll, która przyjmuje flagę zdarzenia (bitowa flaga, lub ich suma) oraz współrzędne zdarzenia. Eksperymentując z tym helperem w
pewnym momencie zapewne zauważycie, że zdarzenie jest uruchamiane dla aktualnej pozycji kursora niezależnie od tego co jest podane w linii 51 (wywołanie mouse_event)… Dziwne zachowanie, być może zależy od czynników, do których jeszcze nie dotarłem podczas drążenia tematu. W związku z tym można by korzystać tylko z metody SetPosition oraz ze zmienionej postaci Raise*Click, na przykład:

  1 public static void RaiseRightClick(int x, int y)
  2 {
  3     mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
  4 }
 

Oto przykład wykorzystania, który przy domyślnych ustawieniach pulpitu i ikon (“Mój” komputer” w lewym gornym rogu) po dwóch sekundach od uruchomienia otworzy okno eksploratora plików.

  1 using System;
  2
  3 namespace ConsAppClicker
  4 {
  5     class Program
  6     {
  7         static void Main(string[] args)
  8         {
  9             Console.WriteLine(“Console mouse click demo”);
10             System.Threading.Thread.Sleep(2000);
11             MouseHelper.SetPosition(20, 20);
12             MouseHelper.RaiseDoubleClick();
13             Console.WriteLine(“Done!”);
14             Console.ReadLine();
15         }
16     }
17 }
 

Teraz ożywienie poczciwego “szczura” jest już w zasięgu ręki. Miłej zabawy!


Stopwatch – zmierz się z czasem!

Podczas naszej pracy często zdarza się nam zastanawiać jak szybko działa dany fragment kodu. Aby to sprawdzić wystarczy skorzystać z klasy Stopwatch znajdującej się w przestrzeni nazw System.Diagnostics.

Najczęściej będziemy korzystać z oferowanych przez obiekt tego typu metod Start() i Stop() oraz właściwości Elapsed. Sprawdźmy zatem jak długo potrwa pusta pętla przez cały zakres Int32:

  1 private static void StopwatchDemo()
  2 {
  3     Console.WriteLine(“For loop from 1 to {0} time test”, Int32.MaxValue);
  4     Stopwatch sw = new Stopwatch();
  5     sw.Start();
  6     for (int i = 0; i < Int32.MaxValue; i++) ;
  7     sw.Stop();
  8     Console.WriteLine(“Elapsed time {0}”, sw.Elapsed);
  9     Console.WriteLine(“Press [Enter] to close”);
10     Console.ReadLine();
11 }

A oto wynik działania kodu:

Jest też możliwość wielokrotnego wykorzystania obiektu Stopwatch używając metody Reset(), która zeruje jego wartość:

sw.Reset();
 

Jeżeli nie zresetujemy obiektu, wówczas wykonanie metody Start() spowoduje naliczanie czasu począwszy od jego poprzedniej wartości.

  1 private static void StopwatchNoResetDemo()
  2 {
  3     Console.WriteLine(“Stopwatch without Reset() test”);
  4     Stopwatch sw = new Stopwatch();
  5     for (int i = 0; i < 5; i++)
  6     {
  7         sw.Start();
  8         for (int j = 0; j < 10000000; j++) ;
  9         sw.Stop();
10         Console.WriteLine(“Elapsed time for i={0} loop: {1}”, i, sw.Elapsed);
11     }
12     Console.WriteLine(“Press [Enter] to close”);
13     Console.ReadLine();
14 }

 

A oto wynik:

 

Jak widać ta prosta do wykorzystania klasa pozwala dokładnie znaleźć miejsca, które są najwolniejsze i być może uda się je zoptymalizować…


MTS – przygotuj swój harmonogram!

Dziś o godzinie 00:00 na stronie MTS została udostępniona opcja przygotowania harmonogramu na nadchodzącą konferencję. Dwa dni wcześniej została otworzona rejestracja na sesje dla osób, które jako pierwsze zarejestrowały się na konferencję (lub wygrały na nią wstęp) zgodnie z informacją o pierwszeństwie widniejącą na stronie. Co wybrałem?

Po sesji generalnej posłucham o “Tworzenie wydajnych usług w technologii WCF”, jako że pasjonuje mnie ta technologia. Po przerwie obiadowej przyjdzie czas na “VS 2010 IDE Tips and Tricks” – takich informacji nigdy za wiele :) , w następnej kolejności  “Wiele Ciekawych Funkcji 4.0, czyli leniwym żyje się łatwiej” – lenistwo to jedna z wymaganych cech programistów :). Na koniec dnia “Lap around new things in .NET Framework 4.0”.

Środę rozpocznę od “Wprowadzenie do F#” – jakoś nie mogę się zabrać do autowprowadzenia do F# :), dalej Bartek Pampuch opowie o “Visual Studio 2010 – Deep Dive” – na Bartka zawsze można liczyć!. Po obiedzie “Budowanie aplikacji biznesowych w Silverlight 4 i WCF RIA Services” – ku poszerzaniu horyzontów a na sam koniec “Asynchroniczny model programowania w .Net.”.


  • O mnie

    Maciej Grabek

    Moje profile na:

    MVP

    Codeguru.pl GoldenLine
    Twitter CodeProject

  • english version
  • Polecam

  • Copyright © Maciej Grabek. All rights reserved.
    Powered by WordPress
    %d bloggers like this: