czwartek, 8 października 2009

Conversation (from Seam Framework) in ASP.NET MVC (Part 1)

Recently I faced a problem of implementing an conversation in ASP.NET.
The original I based on is explained here: http://docs.jboss.org/seam/2.2.0.GA/reference/en-US/html/conversations.html . To start with let's explain what conversation is. When users browses some of web pages, the context of all the way he went, an the conditions he chose will be saved in a context - conversation context. This context shouldn't depend on browser window, so i.e. opening web page in new browser window shouldn't break the conversation context.
To explain conversation in real world let's assume that user browses our page in such a way:
1) chooses product catalog
2) narrows product list by specifying some conditions (for example price, color)
3) views single product
4) shows product images.

All the way (this 4 steps) are a conversation. We can then show links in breadcrumb, so that user with one click, can come back from step 4 to step 1.
I assumed that if user goes back (by clicking on breadcrumb) all further steps will be forgotten.
There is my solution:
a) Firstly I created an attribute, that I will decorate all the actions, which I want to be placed In conversation context.
b) Add this attribute to some controller's actions.
c) Create an conversation manager, which will take care of managing conversations.
there can be a lot of conversations, each of it will be identified by parameter cid (conversation id) in requested url. d) Create an breadcrumb, which will take information about browsed pages both from Web.sitemap file and my conversation context.

ad.a)
Listing 1: Conversation Attribute implementation
    /// 
    /// Attribute adding Conversation context to actions
    /// 
    public class Conversation : ActionFilterAttribute, IActionFilter
    {
        HashSet _Types;

        public HashSet Types
        {
            get { return _Types; }
            set { _Types = value; }
        }


        public Conversation(params CType[] types)
        {
            _Types = new HashSet();
            foreach (var item in types)
            {
                _Types.Add(item);
            }
        }

        #region IActionFilter Members

        void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
        {
            
        }

        /// 
        /// Called when [action executing]. Saves conversation context to session.
        /// 
        /// The filter context.
        void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
        {
            ConversationManager cMgr = new ConversationManager(filterContext.HttpContext, Types);
        }

        #endregion
    }

As you can see this attribute will take in it's constructor list of CTypes (conversation types). This types will indicate if page begins conversation or if opening web page with different attributes will erase previous element in conversation context or will add new (if the local path of requested page is the same).
In line 37, when an actions is being executed I create an conversation manager which will take care of managing conversation.
Listing 2: Conversation Types enum
      
      /// Describes type of conversation
      /// 
      public enum CType
      {
          /// 
          /// Begins new conversation context
          /// 
          Begin,
          /// 
          /// Joins conversation for the same Requested action, but with new parameers
          /// 
          Join,       
      }

ad. b)
Listing 3: Example of using Conversation attribute:
[Conversation(CType.Begin, CType.Join)]  
    public ActionResult Index(SearchParams searchParameters)
    {
      // Your action code
      return View();
    }
This is an example of using Conversation attribute.
The rest of implementation will be placed in next post.

środa, 23 września 2009

ASP.NET MVC "Remember me" and FormsAuthentication timeout

Recently I came across a strange behavior of ASP FormsAuthentication class. As it is said in "Pro ASP.NET 3.5 in C# 2008" book forms authentication should create persistent cookie when user marks "Remember me" checkbox in login control. Persistent cookie should avoid logging off user when he closes browser or when default timeout passes (it is configured in forms authentication section in Web.Config file).
Listing 1: Web.Config - Forms authentication configuration
  
         
  

To avoid logging off user even if default timeout goes by I needed to edit SignIn method from FormsAuthenticationService class which is placed in AccountController.cs file.
Listing 2: Updated SignIn method
public void SignIn(string userName, bool createPersistentCookie)
{
    // Remember me was checked - set cookie to remember user for 10 days (or until he logs off)
    if (createPersistentCookie)
    {
        var tenDaysFromNow = DateTime.Now.AddDays(10);
        FormsAuthentication.Initialize();
        HttpCookie cookie = FormsAuthentication.GetAuthCookie(userName, createPersistentCookie);
        cookie.Expires = tenDaysFromNow;
        var cookieVal = FormsAuthentication.Decrypt(cookie.Value);
        FormsAuthenticationTicket at = new FormsAuthenticationTicket(cookieVal.Version, cookieVal.Name, cookieVal.IssueDate, tenDaysFromNow, true, cookieVal.UserData);
        cookie.Value = FormsAuthentication.Encrypt(at);
        HttpContext.Current.Response.Cookies.Add(cookie);                                              
    }            
    else
    {
        FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);                 
    }            
}

The code grabs default authentication cookie (line 8), decrypts its value in line 10 and based on existing value creates new Authentication Ticket with updated ExpirationDate. In the end cookie has been added to response cookies collection.

piątek, 18 września 2009

poniedziałek, 14 września 2009

Regular Expressions in C#

This post is going to explain or memorize my thoughts about regular expressions. I don't use it very often, so that every time I come back to this topic I have to remind myself some of details and that's why I decided to publish this post. The library where you can find a lot of examples is here: http://regexlib.com/ There is also an good freeware program to test your expressions: http://www.radsoftware.com.au/regexdesigner/ The Regex class is placed in
using System.Text.RegularExpressions;
namespace. Our input string looks like this: Jacek Skowron **** Mielec 1984 Address line description description description description description description description description description description description description description description description description description description description description Krystian Kapel *** Szczucin 1983 Addres line description description description description description description description description description description description description description description description description description description description description This string contains set of people. Each person is described like that: First line: Name and rating. Second line: birth place and year, third line contains address and the last line description. My regex expresssion used to parse details from input string is:
   1:  (?<name>.+?)\s(?<rating>\*+).*?\n
   2:  (?<birthPlace>.+?)(?<year>\d{4}?).*?\n
   3:  (?<address>.+?)\n
   4:  (?<description>.+?)\n
Let examine this expression step by step starting from simple versions and exploring it.
First line: It could look like this:
   1:  .+?\s\*+.*?\n
Let's assume there is even no question marks. Explanation then is simple: '.' firstly dot sign stands for any character (except new line \n sing). '+' sign specifies that we want to find one or more characters (here any characters because of dot), '\s' stands for single white-space. '\*+' Next there is an escaped start sign combined with + sign responsible again for finding one or more occurrences of stars. '.*\n' All is left in this line is an hard enter sign - '\n', but I inserted before it '.*' and it is going to find any white-spaces between last star and hard enter. Adding question marks prevents Regular expressions from being greedy, meaning they match as much as they can. (for example it could find name in starting line and stars (rating) in last line of our input string. Having said, first question mark ensures that regex will find all text until first occurance of space fallowed by stars and second ? tells regex to find first hard return. We're almost done with first line, except of brackets, which allow us to divide expression into subexpressions (groups).
   1:  (?<name>.+?)
Referencing to our first line and finding person's name I surrounded expression responsible for finding name with brackets, i.e (?expression) and then added group name in angle bracket. The same was done for rating expression:
(?<rating>\*+)

Second line:
   2:  (?<birthPlace>.+?)(?<year>\d{4}?).*?\n
It introduces only one new aspect - finding digits (specified by '\d') and here where we want to find year (always 4 digits) '\d{4}'
Third and fourth line is just an subset of first line where we were looking for person's name. Regular expression is done. Let's use it in code: Listing - using our expression.
Regex rgx = new Regex(@"(?<name>.+?)\s(?<rating>\*+).*?\n(?<birthPlace>.+?)(?<birthYear>\d{4}?).*?\n(?<address>.+?)\n(?<description>.+?)\n",
 RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace);
                    MatchCollection m = rgx.Matches(inputstring);

                    foreach (Match item in m)
                    {
                        Person p = new Person ();
                        p.Name= item.Groups["name"].Value;
                        p.Rating= item.Groups["rating"].Value.Length;
                        p.BirthPlace = item.Groups["birthPlace"].Value.Trim();
                        p.BirthYear= item.Groups["birthYear"].Value.Trim();
                        p.Address = item.Groups["address"].Value.Trim();
                        p.Description = item.Groups["description"].Value.Trim();
                        // Process our person object
                    }
Variable inputstring contains text with our string to parse. MatchCollection contains all people parsed by regex. Because we've named groups earlier looping over regex result and finding person's properties is easy and straighforward.

piątek, 4 września 2009

Create Fixed div where you can add messages that will fade out automatically.

When you use a lot of ajax requests in your site, especially requests that make some changes on server side and do not return any html content, there is a need to inform user if the request he performed has succeeded. The output will look like this: The plan to achieve it is: 1) Create a hidden div with fixed position in your MasterPage file: 2) Create javaScript function that will show your text for some time. 3) Call this function when your ajax request succeeded. ad. 1) Listing 1: Div html
   1:  <div id="ajax_messages" style="">Operation succeeded</div>
Listing 2: Div style
   1:  #ajax_messages {
   2:  background-color:#FFFF99;
   3:  bottom:0;
   4:  display:block;
   5:  font-family:Arial,Helvetica,sans-serif;
   6:  font-size:110%;
   7:  font-weight:bold;
   8:  height:20px;
   9:  margin:0;
  10:  padding:5px 0 0;
  11:  position:fixed;
  12:  text-align:center;
  13:  width:100%;
  14:  }
ad. 2) In your MasterPage file place javascript source code. This code uses jQuery js library. Listing 3: showMessage function
   1:  <script type="text/javascript">        
   2:          function showMessage(text) {
   3:              var am = $("#ajax_messages");
   4:              am.html(text);
   5:              am.show();
   6:              am.fadeOut(900);
   7:          }
   8:  </script>
The 3rd line grabs the div, 4th places your text in it, 5th shows the div and 6th makes the div disappear slowly (900 is the time of fading out in milliseconds). ad. 3) Listing 4: An example of using this function
   1:  <script type="text/javascript">
   2:      function MakeAjaxRequest(param1, param2) {        
   3:          var url = "/Some/Site?param1=" + param1
   4:          if (param2) {
   5:              url = url + "&param2=" + param2;
   6:          }
   7:          $.ajax({
   8:              method: "POST",
   9:              url: url,
  10:              dataType: "html",
  11:              success: function(result) {                             
  12:                  showMessage("Operation succeeded");
  13:              }
  14:          });
  15:      }
  16:  </script>
Now on your link you can put:
   1:  <a href="javascript:MakeAjaxRequest('param1value', 'param2value')">Make ajax request</a>

Prevent caching javascript or css files

The reason for this post is to make an application prevent user's browser from caching css or js files when you deploy new version of this files. Here is an oryginal post for this. The goal is to make css links or javascript links in your application look like this: Listing 1: Desired css link format.
   1:  <script type="text/javascript" src="/content/js/global.js?33433651"></script>
You just have to create an helper which is going to take your application version number and add it to the parameter of css or js files source. Number with application version should change everytime you deploy your site.

środa, 2 września 2009

Automatically changing web.config files for production and development mode

The reason of this post was forced by a need to automatically change the web.config file of asp application depending on DEBUG or RELEASE version chosen when building application. The code in your web.config file can be different for example in section, where you set up web services. Listing 1 - part of web.config file - Release version
<endpoint address="https://yourdomain.com/services/serviceName.svc" binding="basicHttpBinding" bindingConfiguration="HttpsBinding" contract="ServiceProject.IService">
     <identity>
        <dns value="yourdomain.com" />
     </identity>
</endpoint>
Listing 2 - part of web.config file - development version
<endpoint address="http://localhost:65310/services/ServiceName.svc" binding="basicHttpBinding" contract="ServiceProject.IService">
   <identity>
      <dns value="localhost" />
   </identity>
</endpoint>
When you've got a lot of services in your application it can take a while to change maintain this changes during development and when releasing your application. The solution for this problem is to create 2 files. First will be called Web.DEBUG.config and will contain the same what standard web.config file contains, but with all settings needed when trying out your application on localhost. The second file is Web.RELEASE.config and differs from Web.DEBUG.config file only in this sections where things need to be set for production server. This two files should be placed in the same folder where standard Web.config file is placed. Having that done we can now set our application to change Web.config file depending on Soluction Configuration (debug or release). Right click on your web project and select Properties from context menu. Then go to tab "Build Events" and in "Post-build event command line:" window write this code: Listing 3 - Post-build event command line code
   1:  if $(ConfigurationName) == Debug goto :debug
   2:   
   3:  :release
   4:  xcopy  "$(ProjectDir)Web.RELEASE.config" "$(ProjectDir)Web.config" /Y /R
   5:  goto :exit
   6:   
   7:  :debug
   8:  xcopy  "$(ProjectDir)Web.DEBUG.config" "$(ProjectDir)Web.config" /Y /R
   9:   
  10:  :exit
I think thre's not a lot to explain. Thi code checks your Solution Configuration and replaces Web.config file with appropriate one using xcopy shell function. The paremeters are as fallows: /Y suppresses prompting to confirm you want to overwrite an existing destination file /R overwrites read-only files.

czwartek, 15 stycznia 2009

Binding ASP TreeView to custom DataSource

I tried to bind data from few tables in db to TreeView. Beleow is my solution. ASP TreeView can be bound only to XmlDataSource or SiteMapDataSource, so I had to build XmlDataSource dynamically. Listing 1. Function building XmlDataSource
public XmlDataSource GenerateDataSource(List<products1> custList)
        {
            XmlDataSource returnItems = new XmlDataSource();
            returnItems.EnableCaching = false;
            
            XDocument document = new XDocument();
            using (EntityDataModel edm = new EntityDataModel())
            {
                XElement customersRoot = new XElement("customers");
                foreach (products1 customer in custList)
                {
                    XElement custNode = new XElement("customer", new XAttribute("text", customer.fullname),
                        new XAttribute("value", customer.name));

                    var magazines = edm.prod.Where(p => p.name == customer.name)
                            .SelectMany(p => p.magz);
                    foreach (var magazine in magazines)
                    {
                        // Create Magazine node
                        XElement magazineNode = new XElement("magazine", new XAttribute("text", magazine.name),
                            new XAttribute("value", magazine.id));
                                                
                        var users = edm.prod.Where(p => p.id == mag.id).SelectMany(p => p.users);
                        foreach (var user in users)
                        {
                            // Create User node
                            XElement userNode = new XElement("user", new XAttribute("text", user.fullname),
                            new XAttribute("value", user.id));

                            magazineNode.Add(userNode);
                        }
                        custNode.Add(magazineNode);
                    }
                    customersRoot.Add(custNode);
                }
                document.Add(customersRoot);
                returnItems.Data = document.ToString();                
            }
            returnItems.XPath = "/*/*";
            return returnItems;
        }
Listing 2. Binding to control:
XmlDataSource xmlDataSource = GenerateDataSource(custList);
            TreeView1.DataSource = xmlDataSource;
            TreeView1.DataBind();
Listing 3. Setting control to read text and value properties from XmlDataSource.
<asp:TreeView ID="TreeView1" runat="server" ExpandDepth="1"
    ShowLines="True" >
    <DataBindings>
        <asp:TreeNodeBinding DataMember="customer" TextField="text" ValueField="value" SelectAction="None"/>
        <asp:TreeNodeBinding DataMember="magazine" TextField="text" ValueField="value" SelectAction="None"/>
        <asp:TreeNodeBinding DataMember="user" TextField="text" ValueField="value" SelectAction="None"/>
    </DataBindings>
</asp:TreeView>

środa, 14 stycznia 2009

WHERE IN clause in Linq To Entities

Recently I found nice solution to dynamically build Linq Expression with WHERE IN clause. Listing 1. Function
        static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
                        Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
        {
            if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
            if (null == values) { throw new ArgumentNullException("values"); }
            ParameterExpression p = valueSelector.Parameters.Single();

            // p => valueSelector(p) == values[0] || valueSelector(p) == ...
            if (!values.Any())
            {
                return e => false;
            }
            var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
            var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
            return Expression.Lambda<Func<TElement, bool>>(body, p);
        }
Listing 2. Calling BuildContainsExpression function:
List<string> senderList = new List<string>();
senderList.Add("Jacek");
senderList.Add("Krystian");

using (EntityDataModel edm = new EntityDataModel())
{
       var query = edm.senders.Where(e => e.StartDate > DateTime.Now);
       query = query.Where(BuildContainsExpression<PrListing, string>(e => e.sendernm, sendersList));
}
This builds SQL expression like this: Listing 3. SQL Example
SELECT * FROM senders WHERE (sendernm = 'Jacek' || sendernm = 'Krystian')