Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

My Friends

My Links

Permanent Posts

Portuguese Communities

Changing a Control’s Output Dynamically

A question that pops up occasionally in ASP.NET forums or sites like Stack Overflow is how to change a control’s output dynamically. Well, since the CSS Friendly Control Adapters came out, I knew how to do it statically, through static registration on a .browser file (it can also be done dynamically, for all controls of a certain type, by adding a fully qualified type name to HttpContext.Current.Request.Browsers.Adapters collection), but I decided to go and try to do it dynamically, without registration. More specifically, I wanted to achieve two things:

  • Apply a XSL transformation to the generated output, provided it is XML-compliant;
  • Change the output through code in an event handler-like fashion.

So, here’s what I came up with:

   1: <%@ Register Assembly="WebApplication1" Namespace="WebApplication1" TagPrefix="web" %>
   2: ...
   3: <web:OutputAdapterControl runat="server" TargetControlID="table" OnOutput="OnOutput" XslPath="~/table.xsl" />
   4:  
   5: <asp:Table runat="server" ID="table"/>

I have two control declarations, one is for a well known asp:Table, and the other is for my OutputAdapterControl. On its declaration, I have a property that references the asp:Table through its id, one property for an external XSL transformation file and the final one for registering an event handler.

The code for the OutputAdapterControl is:

   1: namespace WebApplication1
   2: {
   3:     [NonVisualControl]
   4:     public class OutputAdapterControl : Control
   5:     {
   6:         private static readonly FieldInfo occasionalFieldsField = typeof(Control).GetField("_occasionalFields", BindingFlags.NonPublic | BindingFlags.Instance);
   7:         private static readonly FieldInfo flagsField = typeof(Control).GetField("flags", BindingFlags.NonPublic | BindingFlags.Instance);
   8:  
   9:         public OutputAdapterControl()
  10:         {
  11:             this.Enabled = true;
  12:         }
  13:  
  14:         public String XslPath
  15:         {
  16:             get;
  17:             set;
  18:         }
  19:  
  20:         public String TargetControlID
  21:         {
  22:             get;
  23:             set;
  24:         }
  25:  
  26:         public Boolean Enabled
  27:         {
  28:             get;
  29:             set;
  30:         }
  31:  
  32:         public event EventHandler<OutputEventArgs> Output;
  33:  
  34:         private ControlAdapter getControlAdapter(Control control)
  35:         {
  36:             Object flags = flagsField.GetValue(control);
  37:             MethodInfo setMethod = flags.GetType().GetMethod("Set", BindingFlags.NonPublic | BindingFlags.Instance);
  38:             setMethod.Invoke(flags, new Object[] { 0x8000 });
  39:  
  40:             Object occasionalFields = occasionalFieldsField.GetValue(control);
  41:             FieldInfo rareFieldsField = occasionalFields.GetType().GetField("RareFields");
  42:             Object rareFields = rareFieldsField.GetValue(occasionalFields);
  43:  
  44:             if (rareFields == null)
  45:             {
  46:                 rareFields = FormatterServices.GetUninitializedObject(rareFieldsField.FieldType);
  47:                 rareFieldsField.SetValue(occasionalFields, rareFields);
  48:             }
  49:  
  50:             FieldInfo adapterField = rareFields.GetType().GetField("Adapter");
  51:             ControlAdapter adapter = adapterField.GetValue(rareFields) as ControlAdapter;
  52:  
  53:             return (adapter);
  54:         }
  55:  
  56:         private void setControlAdapter(Control control, ControlAdapter controlAdapter)
  57:         {
  58:             Object occasionalFields = occasionalFieldsField.GetValue(control);
  59:             FieldInfo rareFieldsField = occasionalFields.GetType().GetField("RareFields");
  60:             Object rareFields = rareFieldsField.GetValue(occasionalFields);
  61:             FieldInfo adapterField = rareFields.GetType().GetField("Adapter");
  62:             adapterField.SetValue(rareFields, controlAdapter);
  63:         }
  64:  
  65:         internal void RaiseOutputEvent(OutputEventArgs e)
  66:         {
  67:             if (this.Output != null)
  68:             {
  69:                 this.Output(this, e);
  70:             }
  71:         }
  72:  
  73:         protected override void OnPreRender(EventArgs e)
  74:         {
  75:             if ((this.Enabled == true) && (String.IsNullOrWhiteSpace(this.TargetControlID) == false))
  76:             {
  77:                 Control control = this.FindControl(this.TargetControlID);
  78:                 ControlAdapter controlAdapter = this.getControlAdapter(control);
  79:                 OutputAdapterControlAdapter newAdapter = new OutputAdapterControlAdapter(this, controlAdapter, control, this.XslPath);
  80:  
  81:                 this.setControlAdapter(control, newAdapter);
  82:             }
  83:  
  84:             base.OnPreRender(e);
  85:         }
  86:     }
  87: }

And the code for the control adapter is:

   1: namespace WebApplication1
   2: {
   3:     public class OutputAdapterControlAdapter : ControlAdapter
   4:     {
   5:         private static readonly FieldInfo controlField = typeof(ControlAdapter).GetField("_control", BindingFlags.NonPublic | BindingFlags.Instance);
   6:         private static readonly MethodInfo controlRenderMethod = typeof(Control).GetMethod("Render", BindingFlags.NonPublic | BindingFlags.Instance);
   7:         private static readonly MethodInfo controlAdapterRenderMethod = typeof(ControlAdapter).GetMethod("Render", BindingFlags.NonPublic | BindingFlags.Instance);
   8:  
   9:         public OutputAdapterControlAdapter(OutputAdapterControl outputControl, ControlAdapter original, Control control, String xslPath)
  10:         {
  11:             this.OutputControl = outputControl;
  12:             this.Original = original;
  13:             this.XslPath = xslPath;
  14:             controlField.SetValue(this, control);
  15:         }
  16:  
  17:         protected OutputAdapterControl OutputControl
  18:         {
  19:             get;
  20:             private set;
  21:         }
  22:  
  23:         protected String XslPath
  24:         {
  25:             get;
  26:             private set;
  27:         }
  28:  
  29:         protected ControlAdapter Original
  30:         {
  31:             get;
  32:             private set;
  33:         }
  34:  
  35:         protected override void Render(HtmlTextWriter writer)
  36:         {
  37:             StringBuilder builder = new StringBuilder();
  38:             HtmlTextWriter tempWriter = new HtmlTextWriter(new StringWriter(builder));
  39:  
  40:             if (this.Original != null)
  41:             {
  42:                 controlAdapterRenderMethod.Invoke(this.Original, new Object[] { tempWriter });
  43:             }
  44:             else
  45:             {
  46:                 controlRenderMethod.Invoke(this.Control, new Object[] { tempWriter });
  47:             }
  48:  
  49:             if (String.IsNullOrWhiteSpace(this.XslPath) == false)
  50:             {
  51:                 String path = HttpContext.Current.Server.MapPath(this.XslPath);
  52:  
  53:                 XmlDocument xml = new XmlDocument();
  54:                 xml.LoadXml(builder.ToString());
  55:  
  56:                 builder.Clear();
  57:  
  58:                 XslCompiledTransform xsl = new XslCompiledTransform();
  59:                 xsl.Load(path);
  60:                 xsl.Transform(xml, null, tempWriter);
  61:             }
  62:  
  63:             OutputEventArgs e = new OutputEventArgs() { Html = builder.ToString() };
  64:  
  65:             this.OutputControl.RaiseOutputEvent(e);
  66:  
  67:             if (e.Html != builder.ToString())
  68:             {
  69:                 builder.Clear();
  70:                 builder.Append(e.Html);
  71:             }
  72:  
  73:             writer.Write(builder.ToString());
  74:         }
  75:     }
  76: }

Finally there’s the event argument:

   1: namespace WebApplication1
   2: {
   3:     [Serializable]
   4:     public class OutputEventArgs : EventArgs
   5:     {
   6:         public String Html
   7:         {
   8:             get;
   9:             set;
  10:         }
  11:     }
  12: }

And a sample event handler:

   1: protected void OnOutput(object sender, OutputEventArgs e)
   2: {
   3:     e.Html = e.Html.ToUpper();
   4: }

Please note that this relies heavily on reflection, and, for that matter, will only work with ASP.NET 4.0. It can be made to work with ASP.NET 2/3.5, but you will have to change the code to get to the stored control adapter in the Control class. Thanks to João Angelo for reminding me of that!

If you look at it carefully, you will see that, if an XSL transformation file is specified, the control adapter will use it to transform the output of the target control. Either way, if an event handler is registered, it will raise the event, passing in the event argument the HTML generated by the target control, possibly modified by the XSL transformation. In the event handler, all you have to do is change the Html property of the event argument.

Let me know if this helps in some way, and have fun!

NHibernate Metadata

I know that O/RMs like NHibernate are supposed to shield us from the gory database internals such as tables, views, columns and such; however, at times, I do have the need to know how my domain model translates to the database. NHibernate, of course, provides us with the information we need, but it isn’t always easy to find, so I wrote my own extension methods for ISessionFactory that allow me to get the metadata I need in an easy way:

   1: public static IEnumerable<PropertyInfo> GetComponents(this ISessionFactory sessionFactory, Type entityType)
   2: {
   3:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
   4:     IClassMetadata metadata = sessionFactory.GetClassMetadata(entityName);
   5:  
   6:     for (Int32 i = 0; i < metadata.PropertyTypes.Length; ++i)
   7:     {
   8:         IType property = metadata.PropertyTypes[i];
   9:  
  10:         if (property is ComponentType)
  11:         {
  12:             String propertyName = metadata.PropertyNames[i];
  13:             yield return (entityType.GetProperty(propertyName));
  14:         }
  15:     }
  16: }
  17:  
  18: public static IEnumerable<PropertyInfo> GetUserTypes(this ISessionFactory sessionFactory, Type entityType)
  19: {
  20:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
  21:     IClassMetadata metadata = sessionFactory.GetClassMetadata(entityName);
  22:  
  23:     for (Int32 i = 0; i < metadata.PropertyTypes.Length; ++i)
  24:     {
  25:         IType property = metadata.PropertyTypes[i];
  26:  
  27:         if (property is CustomType)
  28:         {
  29:             String propertyName = metadata.PropertyNames[i];
  30:             yield return (entityType.GetProperty(propertyName));
  31:         }
  32:     }
  33: }
  34:  
  35: public static IEnumerable<PropertyInfo> GetProperties(this ISessionFactory sessionFactory, Type entityType)
  36: {
  37:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
  38:     IClassMetadata metadata = sessionFactory.GetClassMetadata(entityName);
  39:     
  40:     for (Int32 i = 0; i < metadata.PropertyTypes.Length; ++i)
  41:     {
  42:         IType property = metadata.PropertyTypes[i];
  43:  
  44:         if (!(property is OneToOneType) && !(property is ManyToOneType) && !(property is ComponentType))
  45:         {
  46:             String propertyName = metadata.PropertyNames[i];
  47:             yield return (entityType.GetProperty(propertyName));                    
  48:         }
  49:     }
  50: }
  51:  
  52: public static IEnumerable<PropertyInfo> GetAssociations(this ISessionFactory sessionFactory, Type entityType)
  53: {
  54:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
  55:     IClassMetadata metadata = sessionFactory.GetClassMetadata(entityName);
  56:  
  57:     for (Int32 i = 0; i < metadata.PropertyTypes.Length; ++i)
  58:     {
  59:         IType property = metadata.PropertyTypes[i];
  60:  
  61:         if (property is OneToOneType)
  62:         {
  63:             String propertyName = metadata.PropertyNames[i];
  64:             yield return (entityType.GetProperty(propertyName));
  65:         }
  66:         else if (property is ManyToOneType)
  67:         {
  68:             String propertyName = metadata.PropertyNames[i];
  69:             yield return (entityType.GetProperty(propertyName));
  70:         }
  71:     }
  72: }
  73:  
  74: public static PropertyInfo GetIdentifier(this ISessionFactory sessionFactory, Type entityType)
  75: {
  76:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
  77:     AbstractEntityPersister persister = (sessionFactory as ISessionFactoryImplementor).TryGetEntityPersister(entityName) as AbstractEntityPersister;
  78:  
  79:     if (persister != null)
  80:     {
  81:         return (entityType.GetProperty(persister.IdentifierPropertyName));
  82:     }
  83:     else
  84:     {
  85:         return (null);
  86:     }
  87: }
  88:  
  89: public static IEnumerable<String> GetIdentifierColumnNames(this ISessionFactory sessionFactory, Type entityType)
  90: {
  91:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
  92:     AbstractEntityPersister persister = (sessionFactory as ISessionFactoryImplementor).TryGetEntityPersister(entityName) as AbstractEntityPersister;
  93:  
  94:     if (persister != null)
  95:     {
  96:         return (persister.IdentifierColumnNames);
  97:     }
  98:     else
  99:     {
 100:         return (null);
 101:     }
 102: }
 103:  
 104: public static String GetColumnName(this ISessionFactory sessionFactory, Type entityType, String propertyName)
 105: {
 106:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
 107:     AbstractEntityPersister persister = (sessionFactory as ISessionFactoryImplementor).TryGetEntityPersister(entityName) as AbstractEntityPersister;
 108:  
 109:     if (persister != null)
 110:     {
 111:         return (persister.GetPropertyColumnNames(propertyName).SingleOrDefault());
 112:     }
 113:     else
 114:     {
 115:         return (null);
 116:     }
 117: }
 118:  
 119: public static String GetTableName(this ISessionFactory sessionFactory, Type entityType)
 120: {
 121:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
 122:     AbstractEntityPersister persister = (sessionFactory as ISessionFactoryImplementor).TryGetEntityPersister(entityName) as AbstractEntityPersister;
 123:  
 124:     if (persister != null)
 125:     {
 126:         return (persister.TableName);
 127:     }
 128:     else
 129:     {
 130:         return (null);
 131:     }
 132: }
 133:  
 134: public static IEnumerable<PropertyInfo> GetCollections(this ISessionFactory sessionFactory, Type entityType)
 135: {
 136:     String entityName = (sessionFactory as ISessionFactoryImplementor).TryGetGuessEntityName(entityType);
 137:     IClassMetadata metadata = sessionFactory.GetClassMetadata(entityName);
 138:     String prefix = String.Concat(entityName, ".");
 139:     IDictionary<String, ICollectionMetadata> collectionMetadata = sessionFactory.GetAllCollectionMetadata().Where(x => x.Key.StartsWith(prefix)).ToDictionary(x => x.Key, x => x.Value);
 140:  
 141:     foreach (String key in collectionMetadata.Keys)
 142:     {
 143:         String propertyName = key.Replace(prefix, String.Empty);
 144:         yield return(entityType.GetProperty(propertyName));
 145:     }
 146: }

A brief explanation:

  • GetProperties: all the mapped properties for a given entity;
  • GetComponents: given an entity type, returns all the properties that are mapped as components;
  • GetUserTypes: all the properties that are mapped as custom user types;
  • GetIdentifier: the property that is mapped as the identifier;
  • GetAssociations: properties mapped as one to one or many to one, that is, that point to another entity;
  • GetCollections: properties mapped as one to many, that is, collections, of any type;
  • GetTableName: the table or view name where the entity is stored;
  • GetIdentifierColumnNames: the names of the column(s) that compose the primary key for the table;
  • GetColumnName: the table or view column that store a given property.
Remove View State from UpdatePanel Call

You may not be aware that view state (and control state) is included on each UpdatePanel call, even if the controls placed inside it have it disabled. As view state can get quite large, it may be useful to disable posting it during UpdatePanel calls, as long as you know what you are doing!

Here’s one possible solution, using Microsoft’s AJAX Library to intercept the asynchronous call before it is actually sent to the server:

   1: function removeViewState(body)
   2: {
   3:     var builder = new Sys.StringBuilder();
   4:     var parts = body.split('&');
   5:  
   6:     for (var i = 0; i < parts.length; ++i)
   7:     {
   8:         if (parts[i].indexOf('__VIEWSTATE=') < 0)
   9:         {
  10:             builder.append(parts[i]);
  11:         }
  12:  
  13:         if (i != parts.length - 1)
  14:         {
  15:             builder.append('&');
  16:         }
  17:     }
  18:  
  19:     return (builder.toString());
  20: }
  21:  
  22: Sys.Net.WebRequestManager.add_invokingRequest
  23: (
  24:     function (s, e)
  25:     {
  26:         var webRequest = e.get_webRequest();
  27:         var body = webRequest.get_body();
  28:  
  29:         body = removeViewState(body);
  30:  
  31:         webRequest.set_body(body);
  32:     }
  33: );

As you can see, I iterate through all of the parameters being sent along with the request, and I skip the view state.

Hope this helps!

Twitter

Follow me at https://twitter.com/#!/RJPeres75

Posted: Jan 27 2012, 03:15 PM by Ricardo Peres | with no comments
Filed under:
Detecting Authentication Expiration in ASP.NET

I recently had to solve a problem: when issuing an asynchronous call by using an UpdatePanel, if the user is no longer authenticated – due possibly to long inactivity – this situation is silently ignored. What happens is, when an unauthenticated user tries to access a protected resource, ASP.NET redirects the request to the login page, and still returns a 302 HTTP status code (Found).

Here’s what I did: first, I hooked an event listener to the completedRequest event of the Sys.Net.WebRequestManager JavaScript object, part of Microsoft’s AJAX Library. This event fires whenever an asynchronous call, made typically by an UpdatePanel, terminates.

Inside it, I look at the response text, directly at the XMLHttpRequest instance. If it contains a text that I know only my login page contains (a comment inside <!-- -->, such as <!—this is the login page -->), then I know I am no longer authenticated, since my async requests are getting redirected.

All I can do at this point is give the user the option to authenticate again, by redirecting to the login page (yes, I know about application services, but that is not the point here).

So, here’s the code:

   1: Sys.Application.add_init
   2:     (
   3:         function ()
   4:         {
   5:             Sys.Net.WebRequestManager.add_completedRequest
   6:             (
   7:                 function (s, e)
   8:                 {
   9:                     if (s._xmlHttpRequest.responseText.indexOf('this is the login page') >= 0)
  10:                     {
  11:                         if (window.confirm('You are no longer authenticated. Do you wish to return to the login page?'))
  12:                         {   
  13:                             window.location.href = '/Login.aspx?ReturnURL=' + window.location.pathname;
  14:                         }
  15:                     }
  16:                 }
  17:             );
  18:         }
  19:     );

And that’s it. Just place this in your master page or on a control that goes on every page.

Querying an Uninitialized Collection with NHibernate

I have talked before about the problem of accessing an uninitialized collection and presented a solution that allows us to see if the collection contains elements on the DB, and how many there are, without actually loading them. Now I have a general purpose solution for querying the collection on the DB.

Here it is:

   1: public static IQueryable<T> Query<T>(this IEnumerable<T> collection)
   2: {
   3:     if (collection is AbstractPersistentCollection)
   4:     {
   5:         IPersistentCollection col = collection as IPersistentCollection;
   6:  
   7:         if (col.WasInitialized == false)
   8:         {
   9:             String role = col.Role;
  10:             Object owner = col.Owner;
  11:             Object key = col.Key;
  12:             ISessionImplementor sessionImpl = typeof(AbstractPersistentCollection).GetField("session", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(collection) as ISessionImplementor;
  13:             ISession session = sessionImpl as ISession;
  14:             ISessionFactory sessionFactory = session.SessionFactory;
  15:             String ownerEntityName = sessionImpl.BestGuessEntityName(owner);
  16:             Type ownerType = Type.GetType(ownerEntityName);
  17:             IClassMetadata metadata = sessionFactory.GetClassMetadata(ownerEntityName);
  18:             String idPropertyName = metadata.IdentifierPropertyName;
  19:             MethodInfo ownerIdGetMethod = ownerType.GetProperty(idPropertyName).GetGetMethod();
  20:             String childPropertyName = role.Split('.').Last();
  21:             MethodInfo ownerChildGetMethod = ownerType.GetProperty(childPropertyName).GetGetMethod();
  22:             Type childType = typeof(T);
  23:             ParameterExpression a = null;
  24:             IQueryable<T> details = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "SelectMany" && x.GetParameters().Length == 2).First().MakeGenericMethod(ownerType, childType).Invoke
  25:             (
  26:                 null,
  27:                 new Object[]
  28:                     {                    
  29:                         typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "Where").First().MakeGenericMethod(ownerType).Invoke
  30:                         (
  31:                             null,
  32:                             new Object[]
  33:                             {
  34:                                 typeof(LinqExtensionMethods).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "Query" && x.GetParameters()[0].ParameterType == typeof(ISession)).Single().MakeGenericMethod(ownerType).Invoke
  35:                                 (
  36:                                     null,
  37:                                     new Object [] { session }
  38:                                 ),
  39:                                 Expression.Lambda
  40:                                 (
  41:                                     typeof(Func<,>).MakeGenericType(ownerType, typeof(Boolean)),
  42:                                     Expression.Equal(Expression.Property(a = Expression.Parameter(ownerType, "x"), ownerIdGetMethod), Expression.Constant(key, ownerIdGetMethod.ReturnType)), new ParameterExpression[] { a }
  43:                                 )
  44:                             }
  45:                         ),
  46:                         Expression.Lambda
  47:                         (
  48:                             typeof(Func<,>).MakeGenericType(ownerType, typeof(IEnumerable<>).MakeGenericType(childType)),
  49:                             Expression.Property(a = Expression.Parameter(ownerType, "x"), ownerChildGetMethod), new ParameterExpression[] { a }
  50:                         )
  51:                     }
  52:             ) as IQueryable<T>;
  53:  
  54:             return (details);
  55:         }
  56:     }
  57:  
  58:     return (collection.AsQueryable());
  59: }

As you can see, I first check to see the collection is an NHibernate one, if not, I fall back to LINQ to Objects; also, if the collection is already initialized, I also use LINQ to Objects.

Hope this helps!

NHibernate Pitfalls: SQL Queries and Parameter Prefix

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

When you execute an SQL query with NHibernate with named parameters, you would perhaps expect them to be prefixed with a symbol specific to the current DB you are accessing, for example, @ for SQL Server and : for Oracle; that is not the case. NHibernate requires that you always use it’s DB-neutral parameter prefix, :, the same that you use for HQL queries:

   1: //for SQL queries
   2: session.CreateSQLQuery("SELECT * FROM [Order] WHERE Id = :id").SetParameter("id", 1).List();
   3: //for HQL queries
   4: session.CreateQuery("from Order where id = :id").SetParameter("id", 1).List();
Inline Image in ASP.NET

Inline images is a technique that, instead of referring to an external URL, includes all of the image’s content in the HTML itself, in the form of a Base64-encoded string.

It avoids a second browser request, at the cost of making the HTML page slightly heavier and not using cache.

Not all browsers support it, but current versions of IE, Firefox and Chrome do.

In order to use inline images, you must write the img element’s src attribute like this:

   1: <img src="data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub/
   2: /ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcpp
   3: V0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7" 
   4: width="16" height="14" alt="embedded folder icon"/>

The syntax is: data:[<mediatype>][;base64],<data>

I developed a simple control that allows you to use inline images in your ASP.NET pages. Here it is:

   1: public class InnerImage: Image
   2: {
   3:     protected override void OnInit(EventArgs e)
   4:     {
   5:         String imagePath = this.Context.Server.MapPath(this.ImageUrl);
   6:         String extension = Path.GetExtension(imagePath).Substring(1);
   7:         Byte[] imageData = File.ReadAllBytes(imagePath);
   8:  
   9:         this.ImageUrl = String.Format("data:image/{0};base64,{1}", extension, Convert.ToBase64String(imageData));
  10:  
  11:         base.OnInit(e);
  12:     }
  13: }
Simple, don’t you think? Winking smile
Posted: Nov 30 2011, 12:08 PM by Ricardo Peres | with no comments
Filed under: , ,
Checking if an Unloaded Collection Contains Elements with NHibernate

If you want to know if an unloaded collection in an entity contains elements, or count them, without actually loading them, you need to use a custom query; that is because the Count property (if the collection is not mapped with lazy=”extra”) and the LINQ Count() and Any() methods force the whole collection to be loaded.

You can use something like these two methods, one for checking if there are any values, the other for actually counting them:

   1: public static Boolean Exists(this ISession session, IEnumerable collection)
   2: {
   3:     if (collection is IPersistentCollection)
   4:     {
   5:         IPersistentCollection col = collection as IPersistentCollection;
   6:  
   7:         if (col.WasInitialized == false)
   8:         {
   9:                 String[] roleParts = col.Role.Split('.');
  10:                 String ownerTypeName = String.Join(".", roleParts, 0, roleParts.Length - 1);
  11:                 String ownerCollectionName = roleParts.Last();
  12:                 String hql = "select 1 from " + ownerTypeName + " it where it.id = :id and exists elements(it." + ownerCollectionName + ")";
  13:                 Boolean exists = session.CreateQuery(hql).SetParameter("id", col.Key).List().Count == 1;
  14:  
  15:                 return (exists);
  16:         }
  17:     }
  18:  
  19:     return ((collection as IEnumerable).OfType<Object>().Any());
  20: }
  21:  
  22: public static Int64 Count(this ISession session, IEnumerable collection)
  23: {
  24:     if (collection is IPersistentCollection)
  25:     {
  26:         IPersistentCollection col = collection as IPersistentCollection;
  27:  
  28:         if (col.WasInitialized == false)
  29:         {
  30:             String[] roleParts = col.Role.Split('.');
  31:             String ownerTypeName = String.Join(".", roleParts, 0, roleParts.Length - 1);
  32:             String ownerCollectionName = roleParts.Last();
  33:             String hql = "select count(elements(it." + ownerCollectionName + ")) from " + ownerTypeName + " it where it.id = :id";
  34:             Int64 count = session.CreateQuery(hql).SetParameter("id", col.Key).UniqueResult<Int64>();
  35:  
  36:             return (count);
  37:         }
  38:     }
  39:  
  40:     return ((collection as IEnumerable).OfType<Object>().Count());
  41: }

Here’s how:

   1: MyEntity entity = session.Load(100);
   2:  
   3: if (session.Exists(entity.SomeCollection))
   4: {
   5:     Int32 count = session.Count(entity.SomeCollection);
   6:     //...
   7: }
NHibernate Pitfalls: Querying a Collection Count

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

Beware when querying a one-to-many collection (bag, set, map, list, etc) with the LINQ standard operators Any() or Count() (or even with the collection’s own Count property: if the collection is mapped as lazy = true, it will be loaded entirely from the DB (if it is not already loaded). That is because the Any() and Count() methods are from LINQ to Objects, not LINQ to NHibernate:

   1: Int32 ordersByProduct = product.OrderDetails.Count();    //causes loading of all order details

There are two possible solutions:

  • Map the collection with lazy = extra and use the collection's Count property;
   1: mapper.Class<Product>(ca =>
   2: {
   3:     //...
   4:     ca.Set(x => x.OrderDetails, x =>
   5:     {
   6:         x.Key(c => c.Column("`PRODUCTID`"));
   7:         x.Inverse(true);
   8:         x.Lazy(CollectionLazy.Extra);
   9:         //...
  10:     }, c => c.OneToMany());
  11: }
  • Perform an independent query (not on the collection) to find if the association has any elements:
   1: var existOrderForProduct = session.Query<OrderDetail>().Where(x => x.Product.Id == 100).Any();
More Posts Next page »