Development With A Dot

Blog on development in general, and specifically on .NET

  • ASP.NET Web Forms Extensibility: Page Parser Filters

    Introduction

    ASP.NET includes a valuable yet not well known extension point called the page parser filter. I once briefly talked about it in the context of SharePoint, but I feel a more detailed explanation is in order.

    A page parser filter is a class with a public parameterless constructor that inherits from PageParserFilter and is registered on the Web.config file, in the pages section, pageParserFilterType attribute. It is called when ASP.NET is compiling a page, the first time it is called, for every page. There can be only one page parser filter per web application.

    Parser

    Why is it a parser? Well, it parses – or, better, receives a notification for - every control declared on the markup of a page (those with runat=”server” or contained inside of), as well as all of the page’s directives (<%@ … %>). The control declarations include all of its attributes and properties, the recognized control type and any complex properties that the markup contains. This allows us to do all kinds of crazy stuff:

    • Inspect, add and remove page directives;
    • Setting the page’s compilation mode;
    • Insert or remove controls or text literals dynamically at specific places;
    • Add/change/remove a control’s properties or attributes;
    • Even (with some reflection magic) change a control’s type or tag.


    So, how do we all this? First, the parser part. We can inspect all page directives by overriding the PreprocessDirective method. This is called for all page directives:

       1: public override void PreprocessDirective(String directiveName, IDictionary attributes)
       2: {
       3:     if (directiveName == "page")
       4:     {
       5:         //make a page asynchronous
       6:         attributes["async"] = "true";
       7:     }
       8:  
       9:     base.PreprocessDirective(directiveName, attributes);
      10: }

    The page’s compilation mode is controlled by GetCompilationMode:

       1: public override CompilationMode GetCompilationMode(CompilationMode current)
       2: {
       3:     return (base.GetCompilationMode(current));
       4: }

    As for adding controls dynamically, we make use of the ParseComplete method:

       1: public override void ParseComplete(ControlBuilder rootBuilder)
       2: {
       3:     if (rootBuilder is FileLevelPageControlBuilder)
       4:     {
       5:         this.ProcessControlBuilder(rootBuilder);
       6:     }
       7:  
       8:     base.ParseComplete(rootBuilder);
       9: }
      10:  
      11: private void ProcessControlBuilder(ControlBuilder builder)
      12: {
      13:     this.ProcessControlBuilderChildren(builder);
      14:  
      15:     if (builder.ControlType == typeof(HtmlForm))
      16:     {
      17:         //add a Literal control inside the form tag
      18:         var literal = ControlBuilder.CreateBuilderFromType(null, builder, typeof(Literal), "asp:Literal", "literal", new Hashtable { { "Text", "Inserted dynamically I" } }, -1, null);
      19:         builder.AppendSubBuilder(literal);
      20:  
      21:         //add an HTML snippet inside the form tag
      22:         builder.AppendLiteralString("<div>Inserted dynamically II</div>");
      23:     }
      24: }
      25:  
      26: private void ProcessControlBuilderChildren(ControlBuilder parentBuilder)
      27: {
      28:     foreach (var builder in parentBuilder.SubBuilders.OfType<ControlBuilder>())
      29:     {
      30:         this.ProcessControlBuilderChildren(builder);
      31:     }
      32: }

    Same for changing a control’s properties:

       1: private static readonly FieldInfo simplePropertyEntriesField = typeof(ControlBuilder).GetField("_simplePropertyEntries", BindingFlags.Instance | BindingFlags.NonPublic);
       2:  
       3: private void SetControlProperty(ControlBuilder builder, String propertyName, String propertyValue)
       4: {
       5:     var properties = (simplePropertyEntriesField.GetValue(builder) as IEnumerable);
       6:  
       7:     if (properties == null)
       8:     {
       9:         properties = new ArrayList();
      10:         simplePropertyEntriesField.SetValue(builder, properties);
      11:     }
      12:  
      13:     var entry = properties.OfType<SimplePropertyEntry>().SingleOrDefault(x => x.Name == propertyName) ?? simplePropertyEntryConstructor.Invoke(null) as SimplePropertyEntry;
      14:     entry.Name = propertyName;
      15:     entry.UseSetAttribute = (builder.ControlType != null && builder.ControlType.GetProperties().Any(x => x.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)) == false);
      16:     entry.PersistedValue = propertyValue;
      17:     entry.Value = entry.PersistedValue;
      18:     entry.Filter = String.Empty;
      19:  
      20:     if (properties.OfType<SimplePropertyEntry>().Any(x => x.Name == propertyName) == false)
      21:     {
      22:         (properties as ArrayList).Add(entry);
      23:     }
      24: }
      25:  
      26: private void ProcessControlBuilder(ControlBuilder builder)
      27: {
      28:     if (typeof(IEditableTextControl).IsAssignableFrom(builder.ControlType) == true)
      29:     {
      30:         this.SetControlProperty(builder, "Text", "Injected dynamically!");
      31:     }
      32:  
      33:     this.ProcessControlBuilderChildren(builder);
      34: }

    And even changing the control’s output tag or instance type:

       1: private static readonly FieldInfo controlTypeField = typeof(ControlBuilder).GetField("_controlType", BindingFlags.Instance | BindingFlags.NonPublic);
       2: private static readonly FieldInfo tagNameField = typeof(ControlBuilder).GetField("_tagName", BindingFlags.Instance | BindingFlags.NonPublic);
       3:  
       4: private void SetControlType(ControlBuilder builder, Type controlType)
       5: {
       6:     controlTypeField.SetValue(builder, controlType);
       7: }
       8:  
       9: private void SetTagName(ControlBuilder controlBuilder, String tagName)
      10: {
      11:     tagNameField.SetValue(controlBuilder, tagName);
      12: }
      13:  
      14: private void ProcessControlBuilder(ControlBuilder builder)
      15: {
      16:     if (builder.TagName != null)
      17:     {
      18:         this.SetTagName(builder, builder.TagName.ToUpper());
      19:     }
      20:  
      21:     if (builder.ControlType == typeof(MyControl))
      22:     {
      23:         this.SetControlType(builder, typeof(MyDerivedControl));
      24:     }
      25:  
      26:     this.ProcessControlBuilderChildren(builder);
      27: }

    Why would we want to change a control’s type? Well, thing about generics, for once.

    Filter

    And now the filtering part: why is it a filter? Because it allows us to filter and control a number of things:

    • The allowed master page, base page class and source file;
    • The allowed controls;
    • The total number of controls allowed on a page;
    • The total number of direct and otherwise references on a page;
    • Allow or disallow code and event handler declarations;
    • Allow or disallow code blocks (<%= … %>, <%: … %>, <% … %>);
    • Allow or disallow server-side script tags (<script runat=”server”>…</script>);
    • Allow, disallow and change data binding expressions (<%# … %>);
    • Add, change or remove event handler declarations.


    All of the filtering methods and properties described below return a Boolean flag and its base implementation may or may not be called, depending on the logic that we want to impose.

    Allowing or disallowing a base page class is controlled by the AllowBaseType method (the default is to accept):

       1: public override Boolean AllowBaseType(Type baseType)
       2: {
       3:     return (baseType == typeof(MyBasePage));
       4: }

    For master pages, user controls or source files we have the AllowVirtualReference virtual method (again, the default is true):

       1: public override Boolean AllowVirtualReference(String referenceVirtualPath, VirtualReferenceType referenceType)
       2: {
       3:     if (referenceType == VirtualReferenceType.Master)
       4:     {
       5:         return (referenceVirtualPath == "AllowedMaster.Master");
       6:     }
       7:     else if (referenceType == VirtualReferenceType.UserControl)
       8:     {
       9:         return (referenceVirtualPath != "ForbiddenControl.ascx");
      10:     }
      11:  
      12:     return (base.AllowVirtualReference(referenceVirtualPath, referenceType));
      13: }

    Controls are controlled (pun intended) by AllowControl, which also defaults to accept:

       1: public override Boolean AllowControl(Type controlType, ControlBuilder builder)
       2: {
       3:     return (typeof(IForbiddenInterface).IsAssignableFrom(controlType) == false);
       4: }

    This may come in handy to disallow the usage of controls in ASP.NET MVC ASPX views!

    The number of controls and dependencies on a page is defined by NumberOfControlsAllowed, NumberOfDirectDependenciesAllowed and TotalNumberOfDependenciesAllowed. Interesting, the default for all these properties is 0, so we have to return –1:

       1: public override Int32 NumberOfControlsAllowed
       2: {
       3:     get
       4:     {
       5:         return (-1);
       6:     }
       7: }
       8:  
       9: public override Int32 NumberOfDirectDependenciesAllowed
      10: {
      11:     get
      12:     {
      13:         return (-1);
      14:     }
      15: }
      16:  
      17: public override Int32 TotalNumberOfDependenciesAllowed
      18: {
      19:     get
      20:     {
      21:         return (-1);
      22:     }
      23: }

    Direct dependencies are user controls directly declared in the page and indirect ones are those declared inside other user controls.

    Code itself, including event handler declarations, are controlled by AllowCode (default is true):

       1: public override Boolean AllowCode
       2: {
       3:     get
       4:     {
       5:         return (true);
       6:     }
       7: }

    If we want to change a data binding expression, we resort to ProcessDataBindingAttribute, which also returns true by default:

       1: public override Boolean ProcessDataBindingAttribute(String controlId, String name, String value)
       2: {
       3:     if (name == "Text")
       4:     {
       5:         //Do not allow binding the Text property
       6:         return (false);
       7:     }
       8:  
       9:     return (base.ProcessDataBindingAttribute(controlId, name, value));
      10: }

    For intercepting event handlers, there’s the ProcessEventHook, which likewise returns true by default:

       1: public override Boolean ProcessEventHookup(String controlId, String eventName, String handlerName)
       2: {
       3:     if (eventName == "SelectedIndexChanged")
       4:     {
       5:         //Remove event handlers for the SelectedIndexChanged event
       6:         return (false);
       7:     }
       8:  
       9:     return (base.ProcessEventHookup(controlId, eventName, handlerName));
      10: }

    And finally, for code blocks, server-side scripts and data binding expressions, there’s the ProcessCodeConstruct method, which likewise also allows everything by default:

    Conclusion

    This was in no means an in-depth description of page parser filters, I just meant to give you an idea of its (high) potential. It is very useful to restrict what end users can place on their pages (SharePoint style) as well as for adding dynamic control programmatically in specific locations of the page, before it is actually built.

    As usual, let me hear your thoughts! Winking smile

    Read more...

  • NHibernate Succinctly

    It’s with great pleasure that I see the release of NHibernate Succinctly, a book that I wrote for Syncfusion’s Succinctly series! It’s my second title, more to come! Winking smile

    Like Entity Framework Code First Succinctly, this book has something for the beginner as well as for more advanced users. Unlike EFCFS, it covers the current version of NHibernate.

    My good friend Zoran Maksimovic (@zoranmax) was the technical reviewer and besides him, I also want to thank everyone at Syncfusion: Hillary Bowling, Darren West, Tres Watkins and Graham High for their feedback and support, and for the opportunity they gave me. Of course, my coleagues at Critical, Pedro Gomes (@pedromvgomes) and Marco Carreira, and also my old friend Tiago Andrade, who also conducted reviews, cannot be forgotten as well!

    Go ahead, grab your copy and let me know what you think!

    By the way, it might be interesting to write something about my experience as an author… Any of you Succinctly authors out there would like to share your experience? Write me a line or comment here! Thanks!

    Read more...

  • New Features in SharePoint Designer 2013 Workflows

    SharePoint Designer 2013 workflows brought along new and quite welcome features. I will list my favorite ones.

    First, the ability to define stages (blocks of instructions) and to jump (yes, goto-style!) between them. This permits a better organization of our code and because of conditional jumps, even includes the ability to reuse code and to loop between stages.

    image

    Next one is stage-level loops. We have two flavors: one where the loop count is predefined from an Integer variable and the other where a condition is used to determine its end.

    image

    Then we have a new type of variables, Dictionary, and three associated actions: build a dictionary with values, get an item from a dictionary and count the number of items in it. Each Dictionary item can be of a different type, and it is even possible to have items of type Dictionary. A Dictionary can be indexed by its key or by position, which is very useful to use inside loops.

    image

    It is now possible to start list and site workflows, but only SharePoint 2010 are supported. Workflows can be started synchronously, in which case, the originating workflow will wait for the result, or asynchronously, aka, fire and forget. Depending on the workflow selected, it may be necessary to specify values for its parameters.

    Also new is the ability to assign tasks and start approval processes.

    image


    image

    A perhaps not so used one is the capability to start a document translation process. The name is misleading, since it can also be used to translate list fields. The result translation is stored in another list.

    Finally, we have the ability to call an HTTP web service. This will mostly used to call REST-style web services, but nothing prevents us from calling SOAP, since we can build a SOAP request using the string utility actions that the Designer offers. We can specify both request contents and headers, and retrieve the result, headers and HTTP status code. The problem is, SharePoint Designer workflows can only process results coming as JSON, not XML, but there are a number of translation web services that can be used to turn XML into JSON.

    image

    Finally,

    All in all, very useful additions! Smile

    Read more...

  • Looping Through List Items in SharePoint 2013 Designer Workflows

    SharePoint 2013 Designer workflows now has two new interesting options: the ability to call HTTP web services and the option to loop over some code a number of times. This, together with the new REST API, which supports querying lists and returning data in JSON, allows iterating through list items in a workflow, something that was not possible before.

    In order to demonstrate this, let’s create a new Site Workflow in SharePoint Designer, that will iterate through the Tasks list:

    image

    Call it Process Tasks, for example, and make sure you select SharePoint 2013 as the platform type.

    In the workflow designer, let’s start by creating a new stage, call it Retrieve Tasks:

    image

    In it, we add a new Set Workflow Variable action which creates a new String variable called url with the value “http://sp2013/_api/web/lists/getbytitle('Tasks')/items”. This uses the new REST API, and you can pass in additional options, such as for ordering by the DueDate field in descending order:

    http://sp2013/_api/web/lists/getbytitle('Tasks')/items?$orderby=DueDate desc

    or filtering:

    http://sp2013/_api/web/lists/getbytitle('Tasks')/items?$filter=DueDate gt DateTime’2014-07-31T00:00:00’

    Next, we add a Dictionary variable (Build a Dictionary action), call it requestHeaders, and initialize it as this:

    image

    Both “Accept” and “Content-Type” entries are of the String type and they both contain the value “application/json;odata=verbose”, SharePoint REST API understands this and sets the response content type appropriately as JSON. If we don’t pass these values, the output would come as XML.

    Following, add an Call an HTTP Web Service action and set its properties. The request will be the url variable:

    image

    Response content will go to a new variable called responseContent:

    image

    Response headers will go to a new variable called responseHeaders:

    image

    And the same goes for the response code (variable responseCode):

    image

    Then we set the request headers to be the requestHeaders variable we created just now, by clicking on the properties for the Call an HTTP Web Service action:

    image

    Now, create a new stage, call it Process Tasks, and, at the end of the initial stage, add a Go to Process Tasks action.

    On the Process Tasks stage, add a Get an Item from a Dictionary action, set the item as d/results, the source variable reponseContent and the output to a new variable of type Dictionary called list. Then count items from this list variable using a Count Items in Dictionary action and store the result in a new Integer variable called count. This variable will tell us how many times we have to loop. Finally, create a new Integer variable called index and set it to 0 (Set Workflow Variable), this will be the loop index.

    Next, add a loop (Loop n Times), call it Loop Task Items, and set the loop variable to count. Inside the loop, get value d/results([%Variable: index%]) using a Get an Item from a Dictionary action from responseContent and store it in a new Dictionary variable called item. Get some fields (Get an Item from a Dictionary) from the item variable, such as Title and DueDate  (mind you, these will be task item fields) and store them in appropriate variables and do whatever you want with them, like logging its contents (Log). Time to increment the loop counter: add a Do Calculation action and in it increment the index variable into a new Integer variable called indexPlusOne. Then set the index variable to be the indexPlusOne (Set Workflow Variable). Finally, exit the loop and set the workflow status (Set Workflow Status action) to Finished. At the end of the stage, select Go to End of Workflow.

    That’s it. Your workflow should look like this:

    image

    The new functionality makes SharePoint Designer workflows much more powerful than before. I will continue to talk about it in the following posts.

    Read more...

  • NHibernate Pitfalls: XML Mappings

    This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

    If you are still using XML mappings – .hbm.xml files –, there’s nothing wrong with that, but you may run into problems.

    First, you will need to add these files as embedded resources in your project, if you are calling Configuration.AddAssembly(), Configuration.AddClass() or you are specifying the name of the assembly in the .config file like this:

       1: <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
       2:     <session-factory>
       3:         <property name="connection.driver_class">NHibernate.Driver.Sql2008ClientDriver</property>
       4:         <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
       5:         <property name="connection.connection_string_name">MyConnection</property>
       6:         <mapping assembly="MyAssembly.MyModel" />
       7:     </session-factory>
       8: </hibernate-configuration>

    These methods will look for .hbm.xml files as embedded resources in that assembly:

    image

    Alternatively, you can have the files on the filesystem, but you will have to call Configuration.AddDirectory(), Configuration.AddFile() or Configuration.AddInputStream(), passing the appropriate parameters.

    In either case, the name of each .hbm.xml must end with .hbm.xml (of course!) and must be composed of the name of the associated class including its namespace.

    If you don’t do this, NHibernate will not find your mappings, which will result in runtime errors.

    Read more...

  • NHibernate Pitfalls: Loading Foreign Key Properties

    This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

    When saving a new entity that has references to other entities (one to one, many to one), one has two options for setting their values:

    • Load each of these references by calling ISession.Get and passing the foreign key;
    • Load a proxy instead, by calling ISession.Load with the foreign key.

    So, what is the difference? Well, ISession.Get goes to the database and tries to retrieve the record with the given key, returning null if no record is found. ISession.Load, on the other hand, just returns a proxy to that record, without going to the database. This turns out to be a better option, because we really don’t need to retrieve the record – and all of its non-lazy properties and collections -, we just need its key.

    An example:

       1: //going to the database
       2: OrderDetail od = new OrderDetail();
       3: od.Product = session.Get<Product>(1);    //a product is retrieved from the database
       4: od.Order = session.Get<Order>(2);        //an order is retrieved from the database
       5:  
       6: session.Save(od);
       7:  
       8: //creating in-memory proxies
       9: OrderDetail od = new OrderDetail();
      10: od.Product = session.Load<Product>(1);    //a proxy to a product is created
      11: od.Order = session.Load<Order>(2);        //a proxy to an order is created
      12:  
      13: session.Save(od);

    So, if you just need to set a foreign key, use ISession.Load instead of ISession.Get.

    Read more...

  • NHibernate Pitfalls: Fetch and Paging

    This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

    NHibernate allows you to force loading additional references (many to one, one to one) or collections (one to many, many to many) in a query. You must know, however, that this is incompatible with paging. It’s easy to see why.

    Let’s say you want to get 5 products starting on the fifth, you can issue the following LINQ query:

       1: session.Query<Product>().Take(5).Skip(5).ToList();

    Will product this SQL in SQL Server:

       1: SELECT
       2:     TOP (@p0) product1_4_,
       3:     name4_,
       4:     price4_
       5: FROM
       6:     (select
       7:         product0_.product_id as product1_4_,
       8:         product0_.name as name4_,
       9:         product0_.price as price4_,        
      10:         ROW_NUMBER() OVER(
      11:     ORDER BY
      12:         CURRENT_TIMESTAMP) as __hibernate_sort_row
      13:     from
      14:         product product0_) as query
      15:     WHERE
      16:         query.__hibernate_sort_row > @p1
      17:     ORDER BY

    If, however, you wanted to bring as well the associated order details, you might be tempted to try this:

       1: session.Query<Product>().Fetch(x => x.OrderDetails).Take(5).Skip(5).ToList();

    Which, in turn, will produce this SQL:

       1: SELECT
       2:     TOP (@p0) product1_4_0_,
       3:     order1_3_1_,
       4:     name4_0_,
       5:     price4_0_,
       6:     order2_3_1_,
       7:     product3_3_1_,
       8:     quantity3_1_,
       9:     product3_0__,
      10:     order1_0__
      11: FROM
      12:     (select
      13:         product0_.product_id as product1_4_0_,
      14:         orderdetai1_.order_detail_id as order1_3_1_,
      15:         product0_.name as name4_0_,
      16:         product0_.price as price4_0_,
      17:         orderdetai1_.order_id as order2_3_1_,
      18:         orderdetai1_.product_id as product3_3_1_,
      19:         orderdetai1_.quantity as quantity3_1_,
      20:         orderdetai1_.product_id as product3_0__,
      21:         orderdetai1_.order_detail_id as order1_0__,
      22:         ROW_NUMBER() OVER(
      23:     ORDER BY
      24:         CURRENT_TIMESTAMP) as __hibernate_sort_row
      25:     from
      26:         product product0_
      27:     left outer join
      28:         order_detail orderdetai1_
      29:             on product0_.product_id=orderdetai1_.product_id
      30:         ) as query
      31: WHERE
      32:     query.__hibernate_sort_row > @p1
      33: ORDER BY
      34:     query.__hibernate_sort_row;

    However, because of the JOIN, what happens is that, if your products have more than one order details, you will get several records – one per order detail – per product, which means that pagination will be broken.

    There is an workaround, which forces you to write your LINQ query in another way:

       1: session.Query<OrderDetail>().Where(x => session.Query<Product>().Select(y => y.ProductId).Take(5).Skip(5).Contains(x.Product.ProductId)).Select(x => x.Product).ToList()

    Or, using HQL:

       1: session.CreateQuery("select od.Product from OrderDetail od where od.Product.ProductId in (select p.ProductId from Product p skip 5 take 5)").List<Product>();

    The generated SQL will then be:

       1: select
       2:     product1_.product_id as product1_4_,
       3:     product1_.name as name4_,
       4:     product1_.price as price4_
       5: from
       6:     order_detail orderdetai0_
       7: left outer join
       8:     product product1_
       9:         on orderdetai0_.product_id=product1_.product_id
      10: where
      11:     orderdetai0_.product_id in (
      12:         SELECT
      13:             TOP (@p0) product_id
      14:         FROM
      15:             (select
      16:                 product2_.product_id,
      17:                 ROW_NUMBER() OVER(
      18:             ORDER BY
      19:                 CURRENT_TIMESTAMP) as __hibernate_sort_row
      20:             from
      21:                 product product2_) as query
      22:         WHERE
      23:             query.__hibernate_sort_row > @p1
      24:         ORDER BY
      25:             query.__hibernate_sort_row);

    Which will get you what you want: for 5 products, all of their order details.

    Read more...