Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

July 2012 - Posts

Strongly Typed Delete With NHibernate

To my great satisfaction, LINQ is nowadays present everywhere, from XML processing, to database querying, including SharePoint. The “Q” in it standing for Query, it’s not a surprise that is mainly being used for querying, but it would be interesting to use it for other scenarios, such as updating and deleting.

I would like to be able to do this in NHibernate:

   1: session.Query<Product>().Where(x => x.IsDiscontinued == true).Delete();

The only alternatives I have at the moment is HQL or SQL, but, as we know, it is not strongly-typed, and thus not refactor-friendly. I even suggested it to the NHibernate team, for the new major versions coming on.

It would be easy to achieve this by resorting to LINQ to Objects, I would just have to force materialization of every entity coming from the query, and then issue a ISession.Delete on each them. Not very wise, though. I decided to write my own solution.

Consider this extension method:

   1: public static class QueryableExtensions
   2: {
   3:     #region Private static readonly fields
   4:     private static readonly PropertyInfo sessionProperty = typeof(DefaultQueryProvider).GetProperty("Session", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty);
   5:     private static readonly FieldInfo batcherInterceptorField = typeof(AbstractBatcher).GetField("_interceptor", BindingFlags.NonPublic | BindingFlags.Instance);
   6:     #endregion
   7:  
   8:     #region Public extension methods
   9:     public static void Delete<T>(this IQueryable<T> queryable)
  10:     {
  11:         if (queryable.GetType().GetGenericTypeDefinition() == typeof(NhQueryable<>))
  12:         {
  13:             ISessionImplementor impl = sessionProperty.GetValue(queryable.Provider, null) as ISessionImplementor;
  14:             IInterceptor oldInterceptor = sessionImplInterceptorField.GetValue(impl) as IInterceptor;
  15:             IInterceptor deleteInterceptor = new DeleteInterceptor();
  16:  
  17:             batcherInterceptorField.SetValue(impl.Batcher, deleteInterceptor);
  18:  
  19:             queryable.Any();
  20:  
  21:             batcherInterceptorField.SetValue(impl.Batcher, oldInterceptor);
  22:         }
  23:         else
  24:         {
  25:             throw (new ArgumentException("Invalid type", "queryable"));
  26:         }
  27:     }
  28:     #endregion
  29: }

What it does is:

  1. Checks if the IQueryable<T> object is an NHibernate implementation;
  2. Get access to its underlying ISessionImplementor;
  3. Saves the current IInterceptor (if any) for the current IBatcher instance;
  4. Sets it to a new instance of our new interceptor, DeleteInterceptor, which is where all the fun actually happens;
  5. Executes the query;
  6. Restores the old IInterceptor to the IBatcher.

So, next is the DeleteInterceptor class:

   1: class DeleteInterceptor : EmptyInterceptor
   2: {
   3:     private static readonly Regex regex = new Regex("\\s+from\\s+([^\\s]+)\\s+([^\\s]+)\\s+");
   4:  
   5:     public override SqlString OnPrepareStatement(SqlString sql)
   6:     {
   7:         Match match = regex.Match(sql.ToString());
   8:         String tableName = match.Groups[1].Value;
   9:         String tableAlias = match.Groups[2].Value;
  10:  
  11:         sql = sql.Substring(match.Groups[2].Index);
  12:         sql = sql.Replace(tableAlias, tableName);
  13:         sql = sql.Insert(0, "delete from ");
  14:  
  15:         Int32 orderByIndex = sql.IndexOfCaseInsensitive(" order by ");
  16:  
  17:         if (orderByIndex > 0)
  18:         {
  19:             sql = sql.Substring(0, orderByIndex);
  20:         }
  21:  
  22:         return (sql);
  23:     }
  24: }

Pretty easy, don’t you think? A basic NHibernate interceptor that just replaces the “SELECT” part of the query for a “DELETE” and removes any possible “ORDER BY”.

My solution is based upon replacing the current interceptor – if one exists – for a temporary one, just for the execution of this query, and setting the previous one back afterwards. It should be safe, because ISessions are meant for single-threaded use only. At the current stage, this should be regarded as a hack! There is plenty of room for other solutions and improvements, but it’s fun, though! Let me hear your thoughts and suggestions.

NHibernate Pitfalls: Querying Unmapped Properties With LINQ

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

Imagine you have a class like this:

   1: public class Person
   2: {
   3:     public virtual String FirstName
   4:     {
   5:         get;
   6:         set;
   7:     }
   8:  
   9:     public virtual String LastName
  10:     {
  11:         get;
  12:         set;
  13:     }
  14:  
  15:     public virtual String FullName
  16:     {
  17:         get
  18:         {
  19:             return(String.Concat(this.FirstName, " ", this.LastName));
  20:         }
  21:     }
  22: }

You might be tempted to issue a query such as this:

   1: var people = session.Query<Person>().Where(x => x.FullName == "Ricardo Peres").ToList();

Unfortunately, this will not work, because FullName is not a mapped property, and will result in an exception being thrown.

You have three options:

  1. Change the FullName property so that it is a formula;
  2. Use LINQ to Objects to do a client-side query instead;
  3. Perform a different query.

Regarding option 1, you can define this property using mapping by code as:

   1: mapper.Class<Person>(p =>
   2: {
   3:     p.Property(x => x.FullName, x =>
   4:     {
   5:         x.Formula("FirstName + ' ' + LastName");
   6:         x.Generated(PropertyGeneration.Always);
   7:         x.Insert(false);
   8:         x.Update(false);
   9:     });
  10: }

Whereas for option 2, you have to have all entities materialized and then apply the condition:

   1: var people = session.Query<Person>().ToList().Where(x => x.FullName == "Ricardo Peres");
Be aware that you are first bringing ALL records from the database and only filtering the in-memory data.

As for option 3, it’s pretty straightforward:

   1: var people = session.Query<Person>().Where(x => x.FirstName == "Ricardo" && x.LastName == "Peres").ToList();

There may be far more complex cases, however, when you will not be able to do this so easily, for example, if you have complex logic in your composite property.

Using the ASP.NET Health Monitoring Provider to Secure Your Application in the Case of an Attack

The ASP.NET Health Monitoring is a featured introduced in ASP.NET 2.0. Basically, you have your application raise web events – not to be confused with user interface events – and you configure rules that define, for a given event code or range of event codes, a time interval and a minimum and maximum number of occurrences, the health monitoring provider that the events will be routed to. The ASP.NET infrastructure already raises a number of these events, and you can also define your own events, and take the responsibility to raise them when appropriate. I won’t go into describing how the health monitoring works, there are several web sites that describe it, instead, I will talk about a different use.

The included providers operate in a passive way, they do things such as inserting the raised event into a database, send an email with its data, write an entry to the event log, etc:

 ASP .NET Health Monitoring Provider Class Diagram  

EventLogWebEventProvider

 

Writes events to the Event Log

SimpleMailWebEventProvider

Sends simple, unformatted email

TemplatedMailWebEventProvider

Sends template-formatted email

SqlWebEventProvider

Writes events to SQL Server

IisTraceWebEventProvider

Routes events to IIS 7 logging

TraceWebEventProvider

Writes events to the trace

WmiWebEventProvider

Routes events to WMI

I want to have something different: whenever there are more than 5 failed logins in 1 minute, I assume an attack is under way, and the site is locked down. I understand this is an extreme decision, but there may be cases where this actually makes sense.

A WebAuthenticationFailureAuditEvent event is raised with event code AuditFormsAuthenticationFailure is raised when a failed authentication using Forms Authentication occurs. So, in order to fire a provider whenever there are 5 in a minute, we must add the following to the Web.config file:

   1: <healthMonitoring>
   2:     <rules>
   3:         <add name="Failed Authentication Events" eventName="Failed Authentication Events" provider="Failed Authentication Provider" minInterval="00:01:00" minInstances="5" maxLimit="*" />
   4:     </rules>
   5:     <eventMappings>
   6:         <add name="Failed Authentication Events" startEventCode="4005" endEventCode="4005" type="System.Web.Management.WebAuthenticationFailureAuditEvent, System.Web.Management" />
   7:     </eventMappings>
   8: </healthMonitoring>

As you can see, the start and end event codes are the same, we just want to fire this for the Forms Authentication login failed event.

The next thing is the implementation of the custom provider. What this will do is, when it is fired, will create a App_Offline.htm file on the root of the web site, thus effectively disabling any accesses. It’s easy to do:

   1: public class FailedAuthenticationWebEventProvider : WebEventProvider
   2: {
   3:     public override void Initialize(String name, NameValueCollection config)
   4:     {
   5:         //additional properties may be specified on the Web.config file
   6:         this.SomeAdditionalProperty = config["someAdditionalProperty"];
   7:  
   8:         config.Remove("someAdditionalProperty");
   9:  
  10:         base.Initialize(name, config);
  11:     }
  12:  
  13:     public String SomeAdditionalProperty
  14:     {
  15:         get;
  16:         set;
  17:     }
  18:  
  19:     public override void Flush()
  20:     {
  21:     }
  22:  
  23:     public override void ProcessEvent(WebBaseEvent raisedEvent)
  24:     {
  25:         //copy an existing template file to App_Offline.htm
  26:         String file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Offline.htm.bak");
  27:         File.Copy(file, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Offline.htm"));
  28:     }
  29:  
  30:     public override void Shutdown()
  31:     {       
  32:     }
  33: }

I included an additional property just for demonstrative purposes, it is not being used.

The final step is to register the new provider:

   1: <healthMonitoring>
   2:     <providers>
   3:         <add name="Failed Authentication Provider" type="FailedAuthenticationWebEventProvider" someAdditionalProperty="someValue" />
   4:     </providers>
   5:     ...
   6: </healthMonitoring>

And that’s it!

NHibernate Pitfalls: Eager Loading Multiple Collections

Updated on July 19. Thanks, Hazzik!

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

If you want to load multiple joined collections simultaneously eagerly you will end up with a cartesian product. This is mentioned in the documentation and will occur with both the LINQ, HQL, Criteria or QueryOver providers. The problem is caused by joining multiple records from the root table + the first collection table with each of the records from the second collection table.

There are two ways around it:

  • Either specify a distinct root transformer that will only return a single instance of each of the root entities – keep in mind that this is done client-side, that is, the query still returns a cross join;
  • Issue two future queries and have NHibernate create the objects in memory; this will send two queries, but less data will be returned:
   1: var futureBlogs = session.CreateQuery("from Blog b left join fetch b.Posts where b.Id = :id")
   2: .SetParameter("id", 1)
   3: .Future<Blog>();
   4:  
   5: session.CreateQuery("from Blog b left join fetch b.Users where b.Id = :id1")
   6: .SetParameter("id1", 1)
   7: .Future<Blog>();
   8:  
   9: var blogs = futureBlogs.ToList();

This example I got from Ayende’s blog, where you can find an explanation for this problem.

This works because of the following:

  1. The first query loads records from the Blog table + the related records from the Post table;
  2. The second query loads records from the Blog table + the related records from the User table; because the Blog entities are already constructed in the session (first level cache), the second query will simply attach the loaded Users collections to their related Blog instances; the results are discarded, because everything will be accessible from the Blog collection from the first query;
  3. By calling ToList() we force the execution of the two future queries at the same time and get their results.

Mind you, I believe future queries only work with SQL Server, MySQL and PostgreSQL at the moment - please let me know if others are supported.

Using ASP.NET Resources in JavaScript

If you use resources in your web application to localize your strings on the server side, you may have also felt the need to use the same resources in JavaScript. The thing is, there wasn’t any straightforward way to do it. Until now, that is! Smile

I wrote a simple handler that picks up whatever resources you have on your application and outputs them as JavaScript. Say you have something like this:

image

image

You will be able to use any of the resources in the Sentences like this:

   1: window.alert(Resources.Sentences.Something);

The format is: Resources.<FileName>.<KeyName>. If you have several resource files, each will be located under the Resources namespace.

The code for the handler is:

   1: public sealed class Resources : IHttpHandler
   2: {
   3:     public void ProcessRequest(HttpContext context)
   4:     {
   5:         Assembly asm = null;
   6:         Type appType = HttpContext.Current.ApplicationInstance.GetType();
   7:  
   8:         context.Response.ContentType = "text/javascript";
   9:  
  10:         if (appType.Namespace == "ASP")
  11:         {
  12:             asm = appType.BaseType.Assembly;
  13:         }
  14:         else
  15:         {
  16:             asm = appType.Assembly;
  17:         }
  18:  
  19:         IEnumerable<Type> resources = asm.GetTypes().Where(x => x.Namespace == "Resources").ToList();
  20:  
  21:         var l = context.Request.UserLanguages;
  22:  
  23:         if (context.Request.UserLanguages.Any() == true)
  24:         {
  25:             try
  26:             {
  27:                 Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(context.Request.UserLanguages.First());
  28:             }
  29:             catch
  30:             {
  31:             }
  32:         }
  33:  
  34:         context.Response.Write("var Resources = {};\n");
  35:  
  36:         foreach (Type resource in resources)
  37:         {
  38:             context.Response.Write(String.Format("Resources.{0} = {{}};\n", resource.Name));
  39:             
  40:             IDictionary<String, String> dict = resources.First().GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetProperty).Where(x => x.PropertyType == typeof(String)).ToDictionary(x => x.Name, x => x.GetValue(null, null).ToString());
  41:  
  42:             foreach (String key in dict.Keys)
  43:             {
  44:                 context.Response.Write(String.Format("Resources.{0}.{1} = '{2}';\n", resource.Name, key, dict[key].Replace("'", "\'")));
  45:             }
  46:         }
  47:     }
  48:  
  49:     public Boolean IsReusable
  50:     {
  51:         get
  52:         {
  53:             return (true);
  54:         }
  55:     }
  56: }

You have to install it on the Web.config as an HTTP handler, on the <httpHandlers> section:

   1: <httpHandlers>
   2:   <add path="Resources.axd" verb="GET" type="MyNamespace.Resources, MyAssembly" validate="true" />
   3: </httpHandlers>

Do note that the handler code may be in a different assembly, which is good for reusing purposes.

And finally, you must reference the handler on the page:

   1: <script type="text/javascript" src="Resources.axd"></script>

And that’s it. Your feedback is welcome!

NHibernate Pitfalls: Cascades

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

For entities that have associations – one-to-one, one-to-many, many-to-one or many-to-many –, NHibernate needs to know what to do with their related entities, in three particular moments: when saving, updating or deleting. In particular, there are two possible behaviors: either ignore these related entities or cascade changes to them. NHibernate allows setting the cascade behavior for each association, and the default behavior is not to cascade (ignore).

The possible cascade options are:

None Ignore, this is the default
Save-Update If the entity is being saved or updated, also save any related entities that are either not saved or have been modified and associate these related entities to the root entity. Generally safe
Delete If the entity is being deleted, also delete the related entities. This is only useful for parent-child relations
Delete-Orphan Identical to Delete, with the addition that if once related entity is removed from the association – orphaned –, also delete it. Also only for parent-child
All Combination of Save-Update and Delete, usually that’s what we want (for parent-child relations, of course)
All-Delete-Orphan Same as All plus delete any related entities who lose their relationship

In summary, Save-Update is generally what you want in most cases. As for the Delete variations, they should only be used if the related entities depend on the root entity (parent-child), so that deleting the root entity and not their related entities would result in a constraint violation on the database.

NHibernate Pitfalls: Lazy Scalar Properties Must Be Auto

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

NHibernate supports lazy properties not just for associations (many to one, one to one, one to many, many to many) but also for scalar properties. This allows, for example, only loading a potentially large BLOB or CLOB from the database if and when it is necessary, that is, when the property is actually accessed. In order for this to work, other than having to be declared virtual, the property can’t have an explicitly declared backing field, it must be an auto property:

   1: public virtual String MyLongTextProperty
   2: {
   3:     get;
   4:     set;
   5: }
   6:  
   7: public virtual Byte [] MyLongPictureProperty
   8: {
   9:     get;
  10:     set;
  11: }

All lazy scalar properties are retrieved at the same time, when one of them is accessed.

Vote of Disconfidence to Entity Framework

A friend of mine has found the following problem with Entity Framework 4:

  • Two simple classes and one association between them (one to many):

image

  • One condition to filter out soft-deleted entities (WHERE Deleted = 0):

image

  • 100 records in the database;
  • A simple query:
   1: var l = ctx.Person.Include("Address").Where(x => (x.Address.Name == "317 Oak Blvd." && x.Address.Number == 926) || (x.Address.Name == "891 White Milton Drive" && x.Address.Number == 497) ...);

Will produce the following SQL:

   1: SELECT 
   2: [Extent1].[Id] AS [Id], 
   3: [Extent1].[FullName] AS [FullName], 
   4: [Extent1].[AddressId] AS [AddressId], 
   5: [Extent202].[Id] AS [Id1], 
   6: [Extent202].[Name] AS [Name], 
   7: [Extent202].[Number] AS [Number]
   8: FROM                                                                                                                                                                                                        [dbo].[Person] AS [Extent1]
   9: LEFT OUTER JOIN [dbo].[Address] AS [Extent2] ON ([Extent2].[Deleted] = 0) AND ([Extent1].[AddressId] = [Extent2].[Id])
  10: LEFT OUTER JOIN [dbo].[Address] AS [Extent3] ON ([Extent3].[Deleted] = 0) AND ([Extent1].[AddressId] = [Extent3].[Id])
  11: LEFT OUTER JOIN [dbo].[Address] AS [Extent4] ON ([Extent4].[Deleted] = 0) AND ([Extent1].[AddressId] = [Extent4].[Id])
  12: LEFT OUTER JOIN [dbo].[Address] AS [Extent5] ON ([Extent5].[Deleted] = 0) AND ([Extent1].[AddressId] = [Extent5].[Id])
  13: LEFT OUTER JOIN [dbo].[Address] AS [Extent6] ON ([Extent6].[Deleted] = 0) AND ([Extent1].[AddressId] = [Extent6].[Id])
  14: ...
  15: WHERE ((N'317 Oak Blvd.' = [Extent2].[Name]) AND (926 = [Extent3].[Number])) 
  16: ...

And will result in 680 MB of memory being taken!

Now, Entity Framework has been historically known for producing less than optimal SQL, but 680 MB for 100 entities?!

According to Microsoft, the problem will be addressed in the following version, there is a Connect issue open. There is even a whitepaper, Performance Considerations for Entity Framework 5, which talks about some of the changes and optimizations coming on version 5, but by reading it, I got even more concerned:

“Once the cache contains a set number of entries (800), we start a timer that periodically (once-per-minute) sweeps the cache.”

Say what?! The next version of Entity Framework will spawn timer threads?!

When Code First came along, I thought it was a step in the right direction. Sure, it didn’t include some things that NHibernate did for quite some time – for example, different strategies for Id generation that do not rely on IDENTITY columns, which makes INSERT batching impossible, or support for enumerated types – but I thought these would come with the time. Now, enumerated types have, but so did… timer threads! Sad smile I’m afraid Entity Framework is becoming a monster.

More Posts