Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

February 2013 - Posts

Blog em Português

(Portuguese only, sorry!)

Conhecem o meu blog em português?

NHibernate Pitfalls: Identity Identifiers

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

IDENTITY columns are well known and used by SQL Server and MySQL users (where it is called AUTO_INCREMENT), among others. It is very useful, because it generates identifiers for us at the database level, so we don’t need to care about them. It is also probably one of the most used identifier generation patterns in NHibernate.

In the context of NHibernate, using IDENTITY has some drawbacks:

  • For single entity persistence, an IDENTITY value is fetched from the database as soon as the entity is marked for saving, not just upon flushing the session or committing the transaction; because IDENTITY doesn’t care about transactions (units of work), even if we rollback the current transaction, the obtained identifier will be lost, this is by design;
  • We cannot use batching (like in a set with cascading), because after inserting each record, NHibernate needs to issue a SELECT statement in order to find out what the generated identifier was, so that it can hydrate the entity;
  • To make things worse, SCOPE_IDENTITY() fails under some circunstances, and, of course, this may also affect NHibernate.
NHibernate Pitfalls: Schema Auto Action

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

When using NHibernate’s loquacious configuration, you have the change to tell NHibernate what to do with the mappings if the database schema that you are targeting does not exist or does not match the current mappings, when a session factory is created. Like this:

   1: Configuration cfg = new Configuration()
   2: .DataBaseIntegration(db =>
   3: {
   4:     db.BatchSize = 20;
   5:     db.ConnectionStringName = connectionStringName;
   6:     db.Dialect<MsSql2008Dialect>();
   7:     db.Driver<Sql2008ClientDriver>();
   8:     db.HqlToSqlSubstitutions = "true 1, false 0, yes 'Y', no 'N'";
   9:     db.IsolationLevel = IsolationLevel.ReadCommitted;
  10:     db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
  11:     db.LogFormattedSql = true;
  12:     db.LogSqlInConsole = false;
  13:     db.OrderInserts = true;
  14:     db.PrepareCommands = true;
  15:     db.SchemaAction = SchemaAutoAction.Validate;
  16: })

This value is the same as the property hbm2ddl.auto, that you can set either programmatically:

   1: cfg.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlAuto, (createSchema == true) ? SchemaAutoAction.Update.ToString() : SchemaAutoAction.Validate.ToString());

or through XML configuration:

   1: <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
   2:     <session-factory>
   3:         <property name="hbm2ddl.auto">Update</property>

The value you assign to SchemaAction is very important, because it can cause your database to be dropped and created again, losing all data in it!

The possible values are:

  • Recreate (drop-create): unconditionally drops and creates all of the tables for all of the mapped entities; when the session factory is disposed, drop them again;

  • Create (create): unconditionally drops and creates all of the tables for all of the mapped entities;

  • Update (update): check every table against the mappings, and, if there are changes, use DDL commands to update the tables so that they match the schema;

  • Validate (validate): check the existing tables against the mappings and throw an exception if there are differences.

So, it should be clear that you would only use Create or Recreate for demonstration purposes, self contained unit tests or fresh installations, not real-life scenarios.

Some additional notes:

  • If you use Recreate, the tables will only be dropped if the session factory is disposed;
  • For both Create and Recreate, other existing tables are not dropped, only those that are mapped to entities;
  • Create, Recreate and Update will not create the database, only its tables, it is up to you to create the database;
  • Update also creates the tables, if they don’t exist;
  • The default value is Validate.
NHibernate Pitfalls: Manually Assigned Identifiers

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

When you use manually assigned identifiers, you are responsible for assigning a valid id to your entity. By valid, I mean, some key that doesn’t already exist on the database.

Because it’s not NHibernate who is managing it, when time comes for the entity to be saved, NHibernate needs to issue a SELECT statement in order to find out if a row with the given id exists, in which case an UPDATE statement will be issued, otherwise, it will be an INSERT. So, you have to be very careful not no overwrite an existing row unintentionally.

Generally speaking, there’s no need to use manually assigned identifiers, and you should avoid them.

NHibernate Extensibility Index

Updated on April 12th

A list of all my posts on NHibernate extensibility:

ASP.NET Web Forms Extensibility Index

Here you will find a list of all my posts on ASP.NET Web Forms extensibility.

If you have any questions, comments or suggestions, do let me know!

NHibernate Pitfalls: Sets and Hash Codes

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

This is not really an NHibernate-specific problem, but, since a lot of people are having trouble, I decided to mention it.

Sets are probably the most used of all collection types in NHibernate. Up to .NET 4, there was no set implementation in the BCL, so NHibernate historically used Iesi Collections. The set specification says that it doesn’t allow repeated entries, but there are many ways to implement this, and one of the most used ones is Iesi.Collections.Generic.HashedSet<T>, which is also used internally (and automatically) by NHibernate in its NHibernate.Collection.Generic.PersistentGenericSet<T>. Now, this class relies on the hash code of its items and allocates them on an internal structure, where it is assumed that this hash code will never change, which is also consistent with Microsoft’s guidelines. You can see a good description on Eric Lippert’s blog: Guidelines and rules for GetHashCode.

So, what is the problem? The problem is, if changes on your entity cause its hash code to change, the set implementation won’t be able to find the object where it was supposed to be, so it won’t be possible to remove the object from the collection.

Consider this code that illustrates the problem (just for demonstration purposes, of course):

   1: class Test
   2: {
   3:     public Int32 Id
   4:     {
   5:         get;
   6:         set;
   7:     }
   8:  
   9:     private Int32 hashCode = 1;
  10:  
  11:     public override Int32 GetHashCode()
  12:     {
  13:         this.hashCode *= -1;
  14:  
  15:         return (this.hashCode);
  16:     }
  17: }

And some typical usage:

   1: Iesi.Collections.Generic.ISet<Test> col = new Iesi.Collections.Generic.HashedSet<Test>();
   2: col.Add(new Test() { Id = 1 });
   3: col.Add(new Test() { Id = 2 });
   4:  
   5: Test t = col.Last();
   6: Boolean contains = col.Contains(t);
   7: Boolean removed = col.Remove(t);

As you can see for yourself, the Contains and the Remove method may return false, and it will be impossible to remove the item, even though it is there.

There are two ways to go around this:

  • Make sure that your GetHashCode method always returns the same hash, regardless of the entity’s internal state;
  • Make modifications to your entity before adding it to the set, and never again, so that the state-dependent hash code remains the same.

One example of a GetHashCode implementation that never changes could be:

   1: public Int32 Id { get; set; }
   2: public String Name { get; set; }
   3:  
   4: private Int32 hashCode = 0;
   5:  
   6: public override Int32 GetHashCode()
   7: {
   8:     if (this.hashCode != 0)
   9:     {
  10:         return(this.hashCode);
  11:     }
  12:  
  13:     this.hashCode = (result * 397) ^ this.Id.GetHashCode();
  14:     this.hashCode = (result * 397) ^ (this.Name ?? String.Empty).GetHashCode();
  15:  
  16:     return (this.hashCode);
  17: }

As a side note, NHibernate 4.0 will be out in some months, will target .NET 4 and so will make use of the set collections that now exist inside the BCL, namely System.Collections.Generic.ISet<T> and System.Collections.Generic.HashSet<T>, and Iesi Collections 4 have been refactored to use them. The problem, however, remains exactly the same.

ASP.NET Web Forms Extensibility: URL Mapping

A long time before ASP.NET Routing came along, ASP.NET already offered a similar functionality: it was called URL mapping.

URL mapping allows having virtual URLs that redirect to real ones. For example, you can have all requests for “/Product/IPhone” redirected to “/Product.aspx?ID=10”. This allows two simple things:

  • Hiding complexity (the ID parameter, for example);
  • Hiding the technology in use (in this case, the .ASPX extension is never seen);
  • Redirecting from one page (such as Default.aspx) to another transparently.

You can configure URL mapping by just adding entries to the urlMapping section on Web.config:

   1: <urlMappings enabled="true">
   2:     <add url="~/Product/IPhone" mappedUrl="~/Product.aspx?ID=10"/>
   3: </urlMappings>

And that’s it, no modules or additional configuration.

You can keep using the QueryString collection:

   1: Int32 id = Convert.ToInt32(this.Request.QueryString["ID"]);     //10
   2: String path = this.Request.Path;                                //Product.aspx

The Url property also contains the real URL, but the RawUrl contains the requested URL:

   1: String requestedPath = this.Request.RawUrl;    //Product/IPhone
   2: String mappedPath = this.Request.Url;          //http://localhost/Product.aspx?ID=1

So, as you can see, this is much simpler that ASP.NET Routing and should only be used in very simple scenarios: when you are using a version of ASP.NET prior to 3.5, or you want to configure routes only through the Web.config file.

ASP.NET DropDownList With Groups

A long time ago I submitted a request to the ASP.NET team for having the standard DropDownList support HTML’s optgroup tag: http://aspnet.codeplex.com/workitem/10318. For those of you not familiar with this tag – that has been around for quite some time, by the way –, it allows for something like this:

In a nutshell, we can have grouped list items. The issue is still open, but, since it is such a handy feature, and one that I need quite often, I decided to implement support for it on top of the existing DropDownList. Again, my solution uses tag mapping, which you should know from my previous posts, so I can add support for it on all of the already declared DropDownLists.

I wanted to support the two basic scenarios:

  1. Adding ListItem entries on markup or by code;
  2. Having the ListItem entries created dynamically through data binding.

I started by implementing a control that inherits from DropDownList:

   1: public class GroupedDropDownList : DropDownList
   2: {
   3:     public String DataGroupField
   4:     {
   5:         get;
   6:         set;
   7:     }
   8:  
   9:     protected override void PerformDataBinding(IEnumerable dataSource)
  10:     {
  11:         base.PerformDataBinding(dataSource);
  12:  
  13:         if ((String.IsNullOrWhiteSpace(this.DataGroupField) == false) && (dataSource != null))
  14:         {
  15:             ListItemCollection items = this.Items;
  16:             IEnumerable<Object> data = dataSource.OfType<Object>();
  17:             Int32 count = data.Count();
  18:  
  19:             for (Int32 i = 0; i < count; ++i)
  20:             {
  21:                 String group = DataBinder.Eval(data.ElementAt(i), this.DataGroupField) as String ?? String.Empty;
  22:  
  23:                 if (String.IsNullOrWhiteSpace(group) == false)
  24:                 {
  25:                     items[i].Attributes["Group"] = group;
  26:                 }
  27:             }
  28:         }
  29:     }
  30:  
  31:     protected override void RenderContents(HtmlTextWriter writer)
  32:     {
  33:         ListItemCollection items = this.Items;
  34:         Int32 count = items.Count;
  35:         var groupedItems = items.OfType<ListItem>().GroupBy(x => x.Attributes["Group"] ?? String.Empty).Select(x => new { Group = x.Key, Items = x.ToList() });
  36:  
  37:         if (count > 0)
  38:         {
  39:             Boolean flag = false;
  40:  
  41:             foreach (var groupedItem in groupedItems)
  42:             {
  43:                 if (String.IsNullOrWhiteSpace(groupedItem.Group) == false)
  44:                 {
  45:                     writer.WriteBeginTag("optgroup");
  46:                     writer.WriteAttribute("label", groupedItem.Group);
  47:                     writer.Write('>');
  48:                 }
  49:  
  50:                 for (Int32 i = 0; i < groupedItem.Items.Count; ++i)
  51:                 {
  52:                     ListItem item = groupedItem.Items[i];
  53:  
  54:                     if (item.Enabled == true)
  55:                     {
  56:                         writer.WriteBeginTag("option");
  57:  
  58:                         if (item.Selected == true)
  59:                         {
  60:                             if (flag == true)
  61:                             {
  62:                                 this.VerifyMultiSelect();
  63:                             }
  64:  
  65:                             flag = true;
  66:  
  67:                             writer.WriteAttribute("selected", "selected");
  68:                         }
  69:  
  70:                         writer.WriteAttribute("value", item.Value, true);
  71:  
  72:                         if (item.Attributes.Count != 0)
  73:                         {
  74:                             item.Attributes.Render(writer);
  75:                         }
  76:  
  77:                         if (this.Page != null)
  78:                         {
  79:                             this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
  80:                         }
  81:  
  82:                         writer.Write('>');
  83:                         HttpUtility.HtmlEncode(item.Text, writer);
  84:                         writer.WriteEndTag("option");
  85:                         writer.WriteLine();
  86:                     }
  87:                 }
  88:  
  89:                 if (String.IsNullOrWhiteSpace(groupedItem.Group) == false)
  90:                 {
  91:                     writer.WriteEndTag("optgroup");
  92:                 }
  93:             }
  94:         }
  95:     }
  96: }

In case you are wondering, the implementation of RenderContents comes from the standard ListControl, here: http://msdn.microsoft.com/en-us/library/4c9zdz95.aspx. As you can see, I overrode two methods and added an extra property:

  • RenderContents is the method responsible for producing the HTML for each of the ListItem items in the Items collection; I modified it so as to output an optgroup declaration for each of the ListItems that contain references to a group, keeping the order;
  • PerformDataBinding is where the data binding process actually populates the  Items collection; I changed it so as to include a Group attribute, in case the DataGroupField property is set.

The new DataGroupField property, similar to DataTextField and DataValueField, if specified, will contain the name of the property in the data source that holds each item’s group name, when using data binding. If not specified, no grouping will occur. Do note that this only applies to data binding, not manually added ListItems.

Now, for using this control, we can either use it explicitly:

   1: <My:GroupedDropDownList runat="server">
   2:     <asp:ListItem Group="Web" Text="CSS"/>
   3:     <asp:ListItem Group="Web" Text="HTML"/>
   4:     <asp:ListItem Group="Web" Text="JavaScript"/>
   5:     <asp:ListItem Group="Windows" Text="WPF"/>
   6:     <asp:ListItem Group="Windows" Text="Windows Forms"/>
   7: </My:GroupedDropDownList>

Or use a tag mapping:

   1: <tagMapping>
   2:     <add tagType="System.Web.UI.WebControls.DropDownList" mappedTagType="MyNamespace.GroupedDropDownList, MyAssembly"/>
   3: </tagMapping>

In which case, you keep the standard DropDownList declaration but add the Group attribute to the ListItems, which works because ListItem implements IAttributeAccessor:

   1: <asp:DropDownList runat="server">
   2:     <asp:ListItem Group="Web" Text="CSS"/>
   3:     <asp:ListItem Group="Web" Text="HTML"/>
   4:     <asp:ListItem Group="Web" Text="JavaScript"/>
   5:     <asp:ListItem Group="Windows" Text="WPF"/>
   6:     <asp:ListItem Group="Windows" Text="Windows Forms"/>
   7: </asp:DropDownList>

And if you prefer to use data binding, you add an extra attribute for the DataGroupField property (DropDownList also implements IAttributeAccessor):

   1: <asp:DropDownList runat="server" ID="list" DataTextField="Name" DataValueField="Id" DataGroupField="Type"/>

And on code behind (or a data source control):

   1: protected void OnLoad(EventArgs e)
   2: {
   3:     var items = new List<Object>();
   4:     items.Add(new { Text = "CSS", Group = "Web" });
   5:     items.Add(new { Text = "HTML", Group = "Web" });
   6:     items.Add(new { Text = "JavaScript", Group = "Web" });
   7:     items.Add(new { Text = "WPF", Group = "Windows" });
   8:     items.Add(new { Text = "Windows Forms", Group = "Windows" });
   9:  
  10:     this.list.DataSource = items;
  11:     this.list.DataBind();
  12:  
  13:     base.OnLoad(e);
  14: }

And you will get all items nicely grouped!

As you can see, tag mapping is very powerful, and, if used with controls that support extensibility, can be very useful. Hope this helps!

More Posts