Contents tagged with NHibernate

  • Lesser-Known NHibernate Features: Validation and Lifecycle Interfaces

    NHibernate offers two interfaces that can be used to validate an entity before it is saved or updated or to cancel its saving, updating and deleting: IValidatable and ILifecycle. They are an alternative to events, and don’t need anything else to work other than be implemented by some entity.

    Here’s what a possible implementation of IValidatable looks like:

       1: public class Product : IValidatable
       2: {
       3:     public String Name { get; set; }
       4:     public Decimal Price { get; set; }
       5:  
       6:     void IValidatable.Validate()
       7:     {   
       8:         if (String.IsNullOrWhitespace(this.Name) == true)
       9:         {
      10:             throw new InvalidOperationException("Name must be set");
      11:         }
      12:  
      13:         if (this.Price <= 0)
      14:         {
      15:             throw new InvalidOperationException("Price needs to be greater than 0");
      16:         }
      17:     }
      18: }

    As you can see, you can cancel the current operation - save or update - by throwing an exception.

    As for ILifecycle, it not only allows us to specifically cancel a save, update and also delete operation, but also to be notified as soon as the entity is loaded:

       1: public class Customer : ILifecycle
       2: {
       3:     public IList<Order> Orders { get; set; }
       4:     public Int32 Id { get; set; }
       5:     public String Name { get; set; }
       6:     
       7:     LifecycleVeto ILifecycle.OnDelete(ISession s)
       8:     {
       9:         return ((this.Orders.Any() == true) ? LifecycleVeto.Veto : LifecycleVeto.NoVeto);
      10:     }
      11:     void ILifecycle.OnLoad(ISession s, Object id)
      12:     {
      13:         Trace.WriteLine(String.Format("A customer with id {0} was loaded", id));
      14:     }
      15:     LifecycleVeto ILifecycle.OnSave(ISession s)
      16:     {
      17:         return ((String.IsNullOrWhitespace(this.Name) == true) ? LifecycleVeto.Veto : LifecycleVeto.NoVeto);
      18:     }
      19:     LifecycleVeto ILifecycle.OnUpdate(ISession s)
      20:     {
      21:         using (var childSession = s.GetSession(s.ActiveEntityMode))
      22:         {
      23:             var any = childSession.CreateQuery("select 1 from Customer where Id != :id and Name = :name").SetParameter("id", this.Id).SetParameter("name", this.Name).List<Int32>();
      24:             return ((any.Count != 0) ? LifecycleVeto.Veto : LifecycleVeto.NoVeto);
      25:         }
      26:     }

    You even have access to the current session.

    This is a classic feature of  NHibernate, but some people don’t like it because it “pollutes” our POCO entities with NHibernate-specific features, which makes our entities less reusable, and forces us to reference the NHibernate DLL. It may, however, come in handy sometimes as a "poor-man's" event system.

    Read more...

  • Entity Developer Review, Part 2

    Here’s my second post on Entity Developer, this time, focused on NHibernate. You can find the first part here.

    I created a new model item of type NHibernate and chose the option to generate from the database:

    image

    Mind you, you need to have the database providers installed for the databases you want to access, Entity Developer does not include them.I was then presented with a list of database objects:

    image

    After I selected a few, Entity Developer shows the class and properties naming rules dialog:

    image

    Next, some more options, pretty complete. Very useful is the option to detect many-to-many associations and table per type inheritances. These will be the default settings, but we can override them on a class by class basis:

    image

    Interesting, the next dialog gives an opportunity to split entities among different diagrams according to the database object’s schema:

    image

    Finally, we get to choose the template that will be used to generate the classes (the only included one is for HBM.XML) and set some of its properties, like, generate a single file or one file per entity, implement standard methods such as Equals or GetHashCode, standard interfaces like NHibernate’s IValidatable, ICloneable and INotifyPropertyChanged, add data contract serialization attributes for WCF and add support for a validation framework (NHibernate Validator, Data Annotations or Enterprise Library Validation Application Block):

    image

    There are several templates available, in both C# and Visual Basic, which we can browse and select (others can be added manually):

    The code is immediately generated, unless there are validation errors in the model, like, a table without primary key:

    image

    In the Model window, selecting a class or a property of a class, we can set its properties, I think all of NHibernate’s features are there, collection types, cascading options, id generators, etc, plus validation settings, which will be translated to the validation framework of choice.

    The HBM.XML files are set as embedded resources, which is also nice, and the Web.config file is updated with the connection string to the selected database. Weird, the dialect was set to MsSql2005Dialect, which doesn’t make much sense since I was connecting to SQL Server 2014. It is possible to select another dialect through the user interface, but dialects and drivers appear as text boxes, not much of a help.

    All in all, it’s pretty impressive, it seems that all NHibernate features are present – did I mention that it is even possible to configure filters? – making Entity Developer a very interesting product. Another strong point is the T4 template system, which, with some effort, allows us to generate code mappings or whatever we want. The only problem I see is that no template for NHibernate's built-in mapping by code is supplied, although one exists for Fluent NHibernate.

    Next, Entity Framework.

    Read more...

  • Entity Developer Review, Part 1

    Introduction

    I recently started to use Entity Developer, by Devart, to generate entities from my databases. It is a commercial product, mind you, and I am in no way affiliated with them, I just happen to like it, so I am going to talk a bit about it.

    A very strong point is that it supports out of the box NHibernate, Entity Framework and the now half-forgotten LINQ to SQL, which makes it stand out from other products. This is not going to be a thorough review, but I will post more as soon as I explore things in more depth.

    Look and Feel

    In general, the UI has a classic Windows appearance, with multiple floating windows, and there are a couple of skins available, but I didn’t pay much attention to it.

    image

    There’s a Database Explorer window which allows us to browse our databases and to see what’s in there (tables, views, functions and stored procedures), plus we can drag objects from it into the Diagram window, which will add this objects to the model.

    The Diagram window allows automatic layout of the objects in it, having each in different colors, adding notes (nice!) and new model items (classes, inheritances, associations, enumerations. A diagram can be “stamped” with the author’s details (name, company, version, date, etc), which is good if we are to print it or generate a bitmap from it (supported natively). It can be zoomed and there’s a Diagram Overview window that shows the full model at once, so that we can easily spot objects outside of the visible area.

    There may be several diagrams for an entity model, the core of an Entity Developer project, and this model has it’s own window, on which we can explore it and see all the mapped database objects and corresponding model items, including inheritances, associations, components (complex/value types), etc. We also have there the list of templates available for the current project type.

    There’s the possibility to generate a SQL script from the model, but in NHibernate projects, only databases SQL Server, SQL Server Compact Edition, Oracle, MySQL and PostgreSQL, this requires that the proper .NET providers are present. For Entity Framework projects, I could only find the option to generate the script for SQL Server, and, of course, LINQ to SQL only supports SQL Server as well. There’s an option for updating the model from the database as well, in case we chose to generate it from the database.

    It is of course possible to validate the model, and the Error List window will present all outcomes of this validation, which can either be errors or warnings. A nice thing: double clicking on a notification, we jump directly to the model item that caused it. It would be interesting as well to have the option to quick fix the problem, but I reckon it’s not always possible or easy to do in an automated fashion.

    When it comes to actually generating the code, there are an endless number of options, from capitalization of properties, lazy loading of associations, inclusion of foreign keys, etc. These settings are of course dependent on the project (NHibernate, Entity Framework or LINQ to SQL), because not all libraries have the same features. The generation is based on T4 templates, which is nice, because they are a standard in .NET. Only one template for each API is include, but it is very easy to write our own, by copying from an existing one and modifying it to our liking. I have yet to find a T4 debugger, Entity Developer, unsurprisingly, does not offer one.

    Conclusion

    OK, so far I have talked in general about Entity Developer, in my next posts, I will talk about NHibernate and Entity Framework specifics and also about advanced functionality.

    Read more...

  • NHibernate Pitfalls: HQL Queries With Joins

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

    Normally, in an HQL query, you can omit the select clause, that is, the two queries are identical:

       1: var q1 = session.CreateQuery("from Product"); //both return IList<Product>
       2: var q2 = session.CreateQuery("select p from Product p");

    However, if you add joins, then it’s a whole different matter:

       1: var q3 = session.CreateQuery("from Product p left join p.OrderDetails");             //returns IList<Object[]>
       2: var q4 = session.CreateQuery("select p from Product p left join p.OrderDetails");    //returns IList<Product>
       3:  

    Worse, queries q3 and q4 will not filter distinct root entities, meaning, you will get the cartesian product of Product x OrderDetail.

    So, you will need to select the root entity (select p), plus add a distinct entity result transformer (Transformers.DistinctRootEntity) to get what you want:

       1: var q5 = session.CreateQuery("select p from Product p left join p.OrderDetails").SetResultTransformer(Transformers.DistinctRootEntity);    //IList<Product>

    This doesn’t happen with LINQ, it always performs a distinct root entity selection, but it also happens with Criteria and QueryOver.

    Thanks to Alexander Zaytsev (@hazzik) who reminded me of this!







    Read more...

  • Lesser-Known NHibernate Features: Proxy Generation

    Did you know that you can leverage NHibernate’s built-in proxy generator to inject custom behavior in your classes? It is called NHibernate.Proxy.DynamicProxy.ProxyFactory and there’s an interface, NHibernate.Proxy.DynamicProxy.IInterceptor, that you can use to extend it, by intercepting method and property calls.

    First, you inject an NHibernate interceptor, inheriting from EmptyInterceptor, in the configuration instance, before building the session factory:

       1: public sealed class NotifyPropertyChangedInterceptor : EmptyInterceptor
       2: {
       3:     private static readonly ProxyFactory factory = new ProxyFactory();
       4:  
       5:     private ISession session = null;
       6:  
       7:     public override void SetSession(ISession session)
       8:     {
       9:         this.session = session;
      10:         base.SetSession(session);
      11:     }
      12:  
      13:     public override Object Instantiate(String clazz, EntityMode entityMode, Object id)
      14:     {
      15:         var entityType = this.session.SessionFactory.GetClassMetadata(clazz).GetMappedClass(entityMode);
      16:         var target = this.session.SessionFactory.GetClassMetadata(entityType).Instantiate(id, entityMode);
      17:         var proxy = factory.CreateProxy(entityType, new _NotifyPropertyChangedInterceptor(target), typeof(INotifyPropertyChanged));
      18:  
      19:         this.session.SessionFactory.GetClassMetadata(entityType).SetIdentifier(proxy, id, entityMode);
      20:  
      21:         return (proxy);
      22:     }
      23: }

    The code in the Instantiate method will add the INotifyPropertyChanged interface to the generated proxy instance, a common interface that is used for detecting property changes.

    But the real fun is in the NHibernate.Proxy.DynamicProxy.IInterceptor implementation:

       1: sealed class _NotifyPropertyChangedInterceptor : NHibernate.Proxy.DynamicProxy.IInterceptor
       2: {
       3:     private PropertyChangedEventHandler changed = delegate { };
       4:     private readonly Object target = null;
       5:  
       6:     public _NotifyPropertyChangedInterceptor(Object target)
       7:     {
       8:         this.target = target;
       9:     }
      10:  
      11:     #region IInterceptor Members
      12:  
      13:     public Object Intercept(InvocationInfo info)
      14:     {
      15:         Object result = null;
      16:  
      17:         if (info.TargetMethod.Name == "add_PropertyChanged")
      18:         {
      19:             var propertyChangedEventHandler = info.Arguments[0] as PropertyChangedEventHandler;
      20:             this.changed += propertyChangedEventHandler;
      21:         }
      22:         else if (info.TargetMethod.Name == "remove_PropertyChanged")
      23:         {
      24:             var propertyChangedEventHandler = info.Arguments[0] as PropertyChangedEventHandler;
      25:             this.changed -= propertyChangedEventHandler;
      26:         }
      27:         else
      28:         {
      29:             result = info.TargetMethod.Invoke(this.target, info.Arguments);
      30:         }
      31:  
      32:         if (info.TargetMethod.Name.StartsWith("set_") == true)
      33:         {
      34:             var propertyName = info.TargetMethod.Name.Substring("set_".Length);
      35:             this.changed(info.Target, new PropertyChangedEventArgs(propertyName));
      36:         }
      37:  
      38:         return (result);
      39:     }
      40:  
      41:     #endregion
      42: }

    This will detect calls to properties (starting with set_) and, after they complete, will raise the PropertyChanged event. This will save you lots of lines in your code!

    You just need to register the NHibernate interceptor, and your done:

       1: cfg.SetInterceptor(new NotifyPropertyChangedInterceptor());

    And from now one, all your entities automagically implement INotifyPropertyChanged:

       1: var product = session.Get<Product>(1);
       2: var npc = product as INotifyProductChanged;
       3: npc.PropertyChanged += (s, e) => 
       4: {
       5:     if (e.PropertyName == "Price")
       6:     {
       7:         Console.WriteLine("Someone changed the price of the product!");
       8:     }
       9: };





    Read more...

  • Lesser-Known NHibernate Features: Serializable Values

    If you want to store anything in a property of an entity, you can declare it as Object or dynamic; as long as you set it to a value of a serializable type, as supported by BinaryFormatter, you’re good! You can even change the value afterwards to a different type, and it will be saved.

    You map it like this, in mapping by code:

       1: ca.Property(x => x.Payload, x =>
       2: {
       3:     x.Column("`payload`");
       4:     x.NotNullable(false);
       5:     x.Type(NHibernateUtil.Serializable);
       6: });

    Where the class should have a property of type Object or dynamic:

       1: //public virtual Object Payload
       2: public virtual dynamic Payload { get; set; }

    NHibernate will return the same object type and all of its contents anytime you retrieve it.

    Try that in other persistence frameworks! Winking smile


    Read more...

  • ASP.NET NHibernateDataSource

    A long, long time ago, I wrote a NHibernateDataSource control. Back then, it was based in the first LINQ provider for NHibernate, and a long has happened since. Now, I decided to give it another go! Smile

    Historically, in ASP.NET, a data control should inherit from DataSourceControl, like ObjectDataSource, LinqDataSource, EntityDataSource, SqlDataSource, etc, and should expose collections for parameters for each of the supported operations (select, update, delete and insert). Since ASP.NET 4, however, a new base class came along: QueryableDataSource. This class is an implementation of IQueryableDataSource, which allows using QueryExtender, also introduced in version 4, to filter and sort the results of a data source that uses LINQ.

    I wanted my control to be able to use QueryExtender, but I also wanted to be able to give it an HQL query. It should also be capable of inserting, updating and deleting entities.

    So, here’s what I came up with, first, the NHibernateDataSource class:

       1: [ParseChildren(true)]
       2: public class NHibernateDataSource : QueryableDataSource
       3: {
       4:     public NHibernateDataSource()
       5:     {
       6:         this.SelectParameters = new ParameterCollection();
       7:         this.InsertParameters = new ParameterCollection();
       8:         this.UpdateParameters = new ParameterCollection();
       9:         this.DeleteParameters = new ParameterCollection();
      10:     }
      11:  
      12:     [Description("Raised when a session factory is built")]
      13:     public event EventHandler<BuildSessionFactoryEventArgs> BuildSessionFactory;
      14:     [Description("Raised when a configuration instance is created")]
      15:     public event EventHandler<ConfigureEventArgs> Configure;
      16:     [Description("Raised when an entity is created for inserts or updates")]
      17:     public event EventHandler<CreateInstanceEventArgs> CreateInstance;
      18:  
      19:     [Description("Raised after an entity is inserted")]
      20:     public event EventHandler<EntityEventArgs> EntityInserted;
      21:     [Description("Raised after an entity is deleted")]
      22:     public event EventHandler<EntityEventArgs> EntityDeleted;
      23:     [Description("Raised after an entity is updated")]
      24:     public event EventHandler<EntityEventArgs> EntityUpdated;
      25:     [Description("Raised after a query is executed")]
      26:     public event EventHandler<EntitiesSelectedEventArgs> EntitiesSelected;
      27:     [Description("Raised when an operation completes (select, insert, update or delete)")]
      28:     public event EventHandler<OperationCompletedEventArgs> OperationCompleted;
      29:  
      30:     [Description("Raised before a select is made")]
      31:     public event EventHandler<EntitiesSelectingEventArgs> EntitiesSelecting;
      32:     [Description("Raised before an entity is inserted")]
      33:     public event EventHandler<EntityEventArgs> EntityInserting;
      34:     [Description("Raised before an entity is deleted")]
      35:     public event EventHandler<EntityEventArgs> EntityDeleting;
      36:     [Description("Raised before an entity is updated")]
      37:     public event EventHandler<EntityEventArgs> EntityUpdating;
      38:  
      39:     [Description("The entity name to update, delete or insert")]
      40:     [DefaultValue("")]
      41:     public String EntityName
      42:     {
      43:         get;
      44:         set;
      45:     }
      46:  
      47:     [Description("The HQL to use for selecting records, when mode Hql is selected")]
      48:     [DefaultValue("")]
      49:     public String Hql
      50:     {
      51:         get;
      52:         set;
      53:     }
      54:  
      55:     [Description("The maximum number of records to retrieve, if paging is not used")]
      56:     [DefaultValue(0)]
      57:     public Int32 MaximumRecords
      58:     {
      59:         get;
      60:         set;
      61:     }
      62:  
      63:     [Description("The page size to retrieve")]
      64:     [DefaultValue(0)]
      65:     public Int32 PageSize
      66:     {
      67:         get;
      68:         set;
      69:     }
      70:  
      71:     [Description("The page index to retrieve")]
      72:     [DefaultValue(0)]
      73:     public Int32 PageIndex
      74:     {
      75:         get;
      76:         set;
      77:     }
      78:  
      79:     [Description("Use HQL or EntityName for selecting")]
      80:     [DefaultValue(NHibernateDataSourceMode.Linq)]
      81:     public NHibernateDataSourceMode Mode
      82:     {
      83:         get;
      84:         set;
      85:     }
      86:  
      87:     [Description("Whether to merge the entity before updating or deleting or not")]
      88:     [DefaultValue(false)]
      89:     public Boolean RefreshBeforeUpdate
      90:     {
      91:         get;
      92:         set;
      93:     }
      94:  
      95:     [Description("Parameters that will be used for the HQL query")]
      96:     public ParameterCollection SelectParameters
      97:     {
      98:         get;
      99:         private set;
     100:     }
     101:  
     102:     [Description("Parameters that will be used for inserting a new entity")]
     103:     public ParameterCollection InsertParameters
     104:     {
     105:         get;
     106:         private set;
     107:     }
     108:  
     109:     [Description("Parameters that will be used for updating an existing entity")]
     110:     public ParameterCollection UpdateParameters
     111:     {
     112:         get;
     113:         private set;
     114:     }
     115:  
     116:     [Description("Parameters that will be used for deleting an existing entity")]
     117:     public ParameterCollection DeleteParameters
     118:     {
     119:         get;
     120:         private set;
     121:     }
     122:  
     123:     [Browsable(false)]
     124:     public ISessionFactory SessionFactory
     125:     {
     126:         get;
     127:         set;
     128:     }
     129:  
     130:     internal static ISessionFactory InternalSessionFactory
     131:     {
     132:         get;
     133:         set;
     134:     }
     135:  
     136:     internal ISessionFactory EffectiveSessionFactory
     137:     {
     138:         get
     139:         {
     140:             var sessionFactory = this.SessionFactory ?? InternalSessionFactory;
     141:             var sfArgs = new BuildSessionFactoryEventArgs() { SessionFactory = sessionFactory };
     142:  
     143:             this.OnBuildSessionFactory(sfArgs);
     144:  
     145:             if (sfArgs.SessionFactory == null)
     146:             {
     147:                 var cfg = new Configuration().Configure();
     148:  
     149:                 var cfgArgs = new ConfigureEventArgs() { Configuration =  cfg };
     150:  
     151:                 this.OnConfigure(cfgArgs);
     152:  
     153:                 cfg = cfgArgs.Configuration;
     154:  
     155:                 sessionFactory = cfg.BuildSessionFactory();
     156:  
     157:                 if (InternalSessionFactory == null)
     158:                 {
     159:                     InternalSessionFactory = sessionFactory;
     160:                 }
     161:             }
     162:             else
     163:             {
     164:                 sessionFactory = sfArgs.SessionFactory;
     165:             }
     166:  
     167:             return (sessionFactory);
     168:         }
     169:     }
     170:  
     171:     protected virtual void OnBuildSessionFactory(BuildSessionFactoryEventArgs e)
     172:     {
     173:         var handler = this.BuildSessionFactory;
     174:  
     175:         if (handler != null)
     176:         {
     177:             handler(this, e);
     178:         }
     179:     }
     180:  
     181:     protected virtual void OnConfigure(ConfigureEventArgs e)
     182:     {
     183:         var handler = this.Configure;
     184:  
     185:         if (handler != null)
     186:         {
     187:             handler(this, e);
     188:         }
     189:     }
     190:  
     191:     protected virtual void OnCreateInstance(CreateInstanceEventArgs e)
     192:     {
     193:         var handler = this.CreateInstance;
     194:  
     195:         if (handler != null)
     196:         {
     197:             handler(this, e);
     198:         }
     199:     }
     200:  
     201:     protected virtual void OnEntitiesSelecting(EntitiesSelectingEventArgs e)
     202:     {
     203:         var handler = this.EntitiesSelecting;
     204:  
     205:         if (handler != null)
     206:         {
     207:             handler(this, e);
     208:         }
     209:     }
     210:  
     211:     protected virtual void OnEntityInserted(EntityEventArgs e)
     212:     {
     213:         var handler = this.EntityInserted;
     214:  
     215:         if (handler != null)
     216:         {
     217:             handler(this, e);
     218:         }
     219:     }
     220:  
     221:     protected virtual void OnEntityDeleted(EntityEventArgs e)
     222:     {
     223:         var handler = this.EntityDeleted;
     224:  
     225:         if (handler != null)
     226:         {
     227:             handler(this, e);
     228:         }
     229:     }
     230:  
     231:     protected virtual void OnEntityUpdated(EntityEventArgs e)
     232:     {
     233:         var handler = this.EntityUpdated;
     234:  
     235:         if (handler != null)
     236:         {
     237:             handler(this, e);
     238:         }
     239:     }
     240:  
     241:     protected virtual void OnEntityInserting(EntityEventArgs e)
     242:     {
     243:         var handler = this.EntityInserting;
     244:  
     245:         if (handler != null)
     246:         {
     247:             handler(this, e);
     248:         }
     249:     }
     250:  
     251:     protected virtual void OnEntityDeleting(EntityEventArgs e)
     252:     {
     253:         var handler = this.EntityDeleting;
     254:  
     255:         if (handler != null)
     256:         {
     257:             handler(this, e);
     258:         }
     259:     }
     260:  
     261:     protected virtual void OnEntityUpdating(EntityEventArgs e)
     262:     {
     263:         var handler = this.EntityUpdating;
     264:  
     265:         if (handler != null)
     266:         {
     267:             handler(this, e);
     268:         }
     269:     }
     270:  
     271:     public virtual void OnEntitiesSelected(EntitiesSelectedEventArgs e)
     272:     {
     273:         var handler = this.EntitiesSelected;
     274:  
     275:         if (handler != null)
     276:         {
     277:             handler(this, e);
     278:         }
     279:     }
     280:  
     281:     public virtual void OnOperationCompleted(OperationCompletedEventArgs e)
     282:     {
     283:         var handler = this.OperationCompleted;
     284:  
     285:         if (handler != null)
     286:         {
     287:             handler(this, e);
     288:         }
     289:     }
     290:  
     291:     public Int32 Insert()
     292:     {
     293:         if (String.IsNullOrWhiteSpace(this.EntityName) == true)
     294:         {
     295:             throw (new InvalidOperationException("The EntityName property cannot be empty."));
     296:         }
     297:  
     298:         if (this.InsertParameters.Count == 0)
     299:         {
     300:             throw (new InvalidOperationException("Missing InsertParameters."));
     301:         }
     302:  
     303:         using (var session = this.EffectiveSessionFactory.OpenStatelessSession())
     304:         using (session.BeginTransaction())
     305:         {
     306:             var args = new EntityEventArgs(this.CreateInstanceAndSetParameters(this.InsertParameters));
     307:  
     308:             this.OnEntityInserting(args);
     309:  
     310:             if (args.Cancel == true)
     311:             {
     312:                 return (0);
     313:             }
     314:  
     315:             session.Insert(args.Entity);
     316:             session.Transaction.Commit();
     317:  
     318:             this.OnEntityInserted(args);
     319:             this.OnOperationCompleted(new OperationCompletedEventArgs(DataSourceOperation.Insert, args.Entity));
     320:         }
     321:  
     322:         return (1);
     323:     }
     324:  
     325:     public Int32 Update()
     326:     {
     327:         if (String.IsNullOrWhiteSpace(this.EntityName) == true)
     328:         {
     329:             throw (new InvalidOperationException("The EntityName property cannot be empty."));
     330:         }
     331:  
     332:         if (this.UpdateParameters.Count == 0)
     333:         {
     334:             throw (new InvalidOperationException("Missing UpdateParameters."));
     335:         }
     336:  
     337:         using (var session = this.EffectiveSessionFactory.OpenStatelessSession())
     338:         using (session.BeginTransaction())
     339:         {
     340:             var args = new EntityEventArgs(this.CreateInstanceAndSetParameters(this.UpdateParameters));
     341:  
     342:             this.OnEntityUpdating(args);
     343:  
     344:             if (args.Cancel == true)
     345:             {
     346:                 return (0);
     347:             }
     348:  
     349:             if (this.RefreshBeforeUpdate == true)
     350:             {
     351:                 this.Refresh(args.Entity);
     352:             }
     353:  
     354:             session.Update(args.Entity);
     355:             session.Transaction.Commit();
     356:  
     357:             this.OnEntityUpdated(args);
     358:             this.OnOperationCompleted(new OperationCompletedEventArgs(DataSourceOperation.Update, args.Entity));
     359:  
     360:             return (1);
     361:         }
     362:     }
     363:  
     364:     public Int32 Delete()
     365:     {
     366:         if (String.IsNullOrWhiteSpace(this.EntityName) == true)
     367:         {
     368:             throw (new InvalidOperationException("The EntityName property cannot be empty."));
     369:         }
     370:  
     371:         using (var session = this.EffectiveSessionFactory.OpenStatelessSession())
     372:         using (session.BeginTransaction())
     373:         {
     374:             var args = new EntityEventArgs(this.CreateInstanceAndSetParameters(this.DeleteParameters));
     375:  
     376:             this.OnEntityDeleting(args);
     377:  
     378:             if (args.Cancel == true)
     379:             {
     380:                 return (0);
     381:             }
     382:  
     383:             if (this.RefreshBeforeUpdate == true)
     384:             {
     385:                 this.Refresh(args.Entity);
     386:             }
     387:  
     388:             session.Delete(args.Entity);
     389:             session.Transaction.Commit();
     390:  
     391:             this.OnEntityDeleted(args);
     392:             this.OnOperationCompleted(new OperationCompletedEventArgs(DataSourceOperation.Delete, args.Entity));
     393:  
     394:             return (1);
     395:         }
     396:     }
     397:  
     398:     protected void Refresh(Object entity)
     399:     {
     400:         using (var session = this.EffectiveSessionFactory.OpenSession())
     401:         {
     402:             session.DefaultReadOnly = true;
     403:             session.FlushMode = FlushMode.Never;
     404:  
     405:             var metadata = this.GetMetadata(this.EntityName);
     406:             var propertiesToLoad = new List<String>();
     407:  
     408:             for (var i = 0; i < metadata.PropertyNames.Length; ++i)
     409:             {
     410:                 if (metadata.GetPropertyValue(entity, metadata.PropertyNames[i], EntityMode.Poco) == null)
     411:                 {
     412:                     if (metadata.PropertyTypes[i].IsEntityType == false)
     413:                     {
     414:                         propertiesToLoad.Add(metadata.PropertyNames[i]);
     415:                     }
     416:                     else
     417:                     {
     418:                         propertiesToLoad.Add(String.Concat(metadata.PropertyNames[i], ".id"));
     419:                     }
     420:                 }
     421:             }
     422:  
     423:             var hql = new StringBuilder();
     424:             hql.Append("select ");
     425:             hql.Append(String.Join(", ", propertiesToLoad));
     426:             hql.AppendFormat(" from {0} where id = :id", entity.GetType().FullName);
     427:  
     428:             var query = session.CreateQuery(hql.ToString());
     429:             query.SetParameter("id", metadata.GetIdentifier(entity, EntityMode.Poco));
     430:  
     431:             var result = query.UniqueResult();
     432:             var values = (result as Object[]) ?? new Object[] { result };
     433:  
     434:             for (var i = 0; i < propertiesToLoad.Count; ++i)
     435:             {
     436:                 var parts = propertiesToLoad[i].Split('.');
     437:                 var value = values[i];
     438:                 var propertyName = parts.First();
     439:  
     440:                 if (parts.Length > 1)
     441:                 {
     442:                     var propertyIndex = Array.IndexOf(metadata.PropertyNames, propertyName);
     443:                     var propertyType = metadata.PropertyTypes[propertyIndex].ReturnedClass;
     444:  
     445:                     value = session.Load(propertyType, values[i]);
     446:                 }
     447:  
     448:                 metadata.SetPropertyValue(entity, propertyName, value, EntityMode.Poco);
     449:             }
     450:         }
     451:     }
     452:  
     453:     protected internal IDictionary<String, Object> GetParameters(ParameterCollection parameters)
     454:     {
     455:         return (parameters.GetValues(this.Context, this).OfType<DictionaryEntry>().ToDictionary(x => x.Key.ToString(), x => x.Value));
     456:     }
     457:  
     458:     protected void SetParameterValues(Object instance, IClassMetadata metadata, IDictionary<String, Object> parameters)
     459:     {
     460:         foreach (var parameter in parameters)
     461:         {
     462:             if (metadata.PropertyNames.Contains(parameter.Key) == true)
     463:             {
     464:                 metadata.SetPropertyValue(instance, parameter.Key, parameter.Value, EntityMode.Poco);
     465:             }
     466:             else if (metadata.IdentifierPropertyName == parameter.Key)
     467:             {
     468:                 metadata.SetIdentifier(instance, parameter.Value, EntityMode.Poco);
     469:             }
     470:         }
     471:     }
     472:  
     473:     protected Object CreateInstanceAndSetParameters(ParameterCollection parameters)
     474:     {
     475:         var metadata = this.GetMetadata(this.EntityName);
     476:  
     477:         if (metadata == null)
     478:         {
     479:             throw (new InvalidOperationException("Entity could not be found."));
     480:         }
     481:  
     482:         var entityType = metadata.GetMappedClass(EntityMode.Poco);
     483:  
     484:         var ciArgs = new CreateInstanceEventArgs(entityType, null);
     485:  
     486:         this.OnCreateInstance(ciArgs);
     487:  
     488:         if (ciArgs.Instance == null)
     489:         {
     490:             ciArgs.Instance = Activator.CreateInstance(entityType);
     491:         }
     492:  
     493:         this.SetParameterValues(ciArgs.Instance, metadata, this.GetParameters(parameters));
     494:  
     495:         return (ciArgs.Instance);
     496:     }
     497:  
     498:     protected internal IClassMetadata GetMetadata(String entityName)
     499:     {
     500:         var metadata = this.EffectiveSessionFactory.GetAllClassMetadata().Where(x => x.Key.EndsWith(entityName)).Select(x => x.Value).SingleOrDefault();
     501:  
     502:         return (metadata);
     503:     }
     504:  
     505:     protected internal void ProcessEntitiesSelecting(EntitiesSelectingEventArgs e)
     506:     {
     507:         this.OnEntitiesSelecting(e);
     508:     }
     509:  
     510:     protected internal void ProcessEntitiesSelected(EntitiesSelectedEventArgs e)
     511:     {
     512:         this.OnEntitiesSelected(e);
     513:         this.OnOperationCompleted(new OperationCompletedEventArgs(DataSourceOperation.Select, e.Results));
     514:     }
     515:  
     516:     protected override QueryableDataSourceView CreateQueryableView()
     517:     {
     518:         return (new NHibernateDataSourceView(this, "DefaultView", this.Context) as QueryableDataSourceView);
     519:     }
     520: }

    You can see that it exposes some events:

    • Configure: gives developers a chance to build (or return an existing) Configuration instance, that will be used for building the session factory;
    • BuildSessionFactory: allows setting parameters on the default session factory or returning an existing one;
    • CreateInstance: raised before NHibernate creates a default instance, to allow developers to return one;
    • EntityInserting: raised before an entity is inserted, allowing developers to cancel the operations or to set entity parameter;
    • EntityUpdating: raised before an entity is updated, allowing developers to cancel the operations or to set entity parameter;
    • EntityDeleting: raised before an entity is deleting, allowing its cancellation;
    • EntitiesSelecting: raised before a select operation is performed;
    • EntityInserted: raised after an entity was inserted;
    • EntityUpdated: raised after an entity was updated;
    • EntityDeleted: raised after an entity was deleted;
    • EntitiesSelected: raised after a select operation was performed;
    • OperationCompleted: raised after an operation completes (select, insert, update or delete).


    If no handler for CreateInstance is supplied, NHibernateDataSource will try to create an entity using Activator.CreateInstance.

    EntitySelecting is raised regardless of the Mode (Hql or Linq), but it will have different values in its argument: a query string plus parameters in the case of Hql and an IQueryable instance for Linq.

    EntityInserting, EntityUpdating and EntityDeleting allow the modification of properties of the entity in the parameter, but not the replacing of the entity itself.

    OperationCompleted is always called, except in the event of an exception.

    It also exposes a couple of properties:

    • Mode: one of the two operation modes, Hql or Linq. If Hql is used, then the Hql property must be set; otherwise, it’s EntityName that is required;
    • Hql: an NHibernate HQL query string;
    • EntityName: the name of an entity that the control will work with; only required for Mode Linq or for inserts, updates or deletes;
    • RefreshBeforeUpdate: whether NHibernate should refresh the properties of an entity before updating or deleting it;
    • MaximumRecords: the optional maximum number of records to retrieve, if paging is not used (PageSize and PageIndex);
    • PageIndex: the page index to retrieve;
    • PageSize: the page size to retrieve;
    • SessionFactory: a session factory that will be used instead of a default created one;
    • SelectParameters: a collection of parameters to be applied to the Hql string;
    • InsertParameters: a collection of parameters for the insert operation;
    • UpdateParameters: a collection of parameters for the update operation;
    • DeleteParameters: a collection of parameters for the delete operation.


    And, of course, exposes the basic operations: select is the default, but Insert, Update and Delete methods are available.

    NHibernateDataSource will check if the SessionFactory property is set, otherwise, it will build its own Configuration instance and raise the Configure and BuildSessionFactory events. The generated session factory is then stored in the InternalSessionFactory static property for caching.

    Then, the NHibernateDataSourceView, which is the responsible for the actual querying, inheriting from QueryableDataSourceView:

       1: public class NHibernateDataSourceView : QueryableDataSourceView
       2: {
       3:     private static readonly MethodInfo queryMethod = typeof (LinqExtensionMethods).GetMethod("Query", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(IStatelessSession) }, null );
       4:     private static readonly MethodInfo toListMethod = typeof(Enumerable).GetMethod("ToList", BindingFlags.Public | BindingFlags.Static);
       5:  
       6:     public NHibernateDataSourceView(NHibernateDataSource dataSource, String viewName, HttpContext context) : base(dataSource, viewName, context)
       7:     {
       8:         this.DataSource = dataSource;
       9:     }
      10:  
      11:     protected NHibernateDataSource DataSource
      12:     {
      13:         get;
      14:         private set;
      15:     }
      16:  
      17:     protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
      18:     {
      19:         using (var session = this.DataSource.EffectiveSessionFactory.OpenStatelessSession())
      20:         {
      21:             var results = null as IList;
      22:  
      23:             switch (this.DataSource.Mode)
      24:             {
      25:                 case NHibernateDataSourceMode.Hql:
      26:                 {
      27:                     if (String.IsNullOrWhiteSpace(this.DataSource.Hql) == true)
      28:                     {
      29:                         throw (new InvalidOperationException("The Hql property cannot be empty."));
      30:                     }
      31:  
      32:                     var hql = this.DataSource.Hql;
      33:                     var parameters = this.DataSource.GetParameters(this.DataSource.SelectParameters);
      34:                     var args = new EntitiesSelectingEventArgs(hql, parameters, this.DataSource.PageSize, this.DataSource.PageIndex, this.DataSource.MaximumRecords);
      35:  
      36:                     this.DataSource.ProcessEntitiesSelecting(args);
      37:  
      38:                     var query = session.CreateQuery(args.Hql);
      39:  
      40:                     foreach (var param in args.SelectParameters)
      41:                     {
      42:                         if (!(param.Value is IEnumerable) || (param.Value is String) || (param.Value is Byte[]))
      43:                         {
      44:                             query.SetParameter(param.Key, param.Value);
      45:                         }
      46:                         else
      47:                         {
      48:                             query.SetParameterList(param.Key, param.Value as IEnumerable);
      49:                         }
      50:                     }
      51:  
      52:                     if (args.PageSize != 0)
      53:                     {
      54:                         query.SetMaxResults(args.PageSize);
      55:                         query.SetFirstResult(Math.Max((args.PageIndex * args.PageSize) - 1, 0));
      56:                         arguments.MaximumRows = args.PageSize;
      57:                     }
      58:  
      59:                     if (args.MaximumRecords != 0)
      60:                     {
      61:                         query.SetMaxResults(args.MaximumRecords);
      62:                         arguments.MaximumRows = args.MaximumRecords;
      63:                     }
      64:  
      65:                     results = query.List();
      66:  
      67:                     arguments.AddSupportedCapabilities(DataSourceCapabilities.Page);
      68:  
      69:                     if (args.PageSize != 0)
      70:                     {
      71:                         arguments.StartRowIndex = Math.Max((args.PageIndex * args.PageSize) - 1, 0);
      72:                     }
      73:  
      74:                     break;
      75:                 }
      76:  
      77:                 case NHibernateDataSourceMode.Linq:
      78:                 {
      79:                     if (String.IsNullOrWhiteSpace(this.DataSource.EntityName) == true)
      80:                     {
      81:                         throw (new InvalidOperationException("The EntityName property cannot be empty."));
      82:                     }
      83:  
      84:                     var query = queryMethod.MakeGenericMethod(this.EntityType).Invoke(null, new Object[] { session }) as IQueryable;
      85:  
      86:                     var qcea = new QueryCreatedEventArgs(query);
      87:  
      88:                     this.OnQueryCreated(qcea);
      89:  
      90:                     var esaea = new EntitiesSelectingEventArgs(qcea.Query);
      91:  
      92:                     this.DataSource.ProcessEntitiesSelecting(esaea);
      93:  
      94:                     results = toListMethod.MakeGenericMethod(this.EntityType).Invoke(null, new Object[] { esaea.Query }) as IList;
      95:  
      96:                     arguments.AddSupportedCapabilities(DataSourceCapabilities.Page | DataSourceCapabilities.Sort);
      97:  
      98:                     break;
      99:                 }
     100:             }
     101:  
     102:             var entitiesSelectedArgs = new EntitiesSelectedEventArgs(results);
     103:  
     104:             this.DataSource.ProcessEntitiesSelected(entitiesSelectedArgs);
     105:  
     106:             return (entitiesSelectedArgs.Results);
     107:         }
     108:     }
     109:  
     110:     protected override Type EntityType
     111:     {
     112:         get
     113:         {
     114:             return (this.DataSource.GetMetadata(this.DataSource.EntityName).GetMappedClass(EntityMode.Poco));
     115:         }
     116:     }
     117:  
     118:     protected override Object GetSource(QueryContext context)
     119:     {
     120:         throw new NotImplementedException();
     121:     }
     122:  
     123:     protected override void HandleValidationErrors(IDictionary<String, Exception> errors, DataSourceOperation operation)
     124:     {
     125:     }
     126: }

    And the NHibernateDataSourceMode:

       1: public enum NHibernateDataSourceMode
       2: {
       3:     Linq,
       4:     Hql
       5: }

    Finally, all of the event arguments:

       1: [Serializable]
       2: public sealed class BuildSessionFactoryEventArgs : EventArgs
       3: {
       4:     public ISessionFactory SessionFactory
       5:     {
       6:         get;
       7:         set;
       8:     }
       9: }
      10:  
      11: [Serializable]
      12: public sealed class ConfigureEventArgs : EventArgs
      13: {
      14:     public Configuration Configuration
      15:     {
      16:         get;
      17:         set;
      18:     }
      19: }
      20:  
      21: [Serializable]
      22: public sealed class CreateInstanceEventArgs : EventArgs
      23: {
      24:     public CreateInstanceEventArgs(Type type, Object instance)
      25:     {
      26:         this.Type = type;
      27:         this.Instance = instance;
      28:     }
      29:  
      30:     public Type Type
      31:     {
      32:         get;
      33:         private set;
      34:     }
      35:  
      36:     public Object Instance
      37:     {
      38:         get;
      39:         set;
      40:     }
      41: }
      42:  
      43: [Serializable]
      44: public sealed class EntitiesSelectedEventArgs : EventArgs
      45: {
      46:     public EntitiesSelectedEventArgs(IList results)
      47:     {
      48:         this.Results = results;
      49:     }
      50:  
      51:     public IList Results
      52:     {
      53:         get;
      54:         set;
      55:     }
      56: }
      57:  
      58: [Serializable]
      59: public sealed class EntitiesSelectingEventArgs : EventArgs
      60: {
      61:     public EntitiesSelectingEventArgs(IQueryable query)
      62:     {
      63:         this.Query = query;
      64:     }
      65:  
      66:     public EntitiesSelectingEventArgs(String hql, IDictionary<String, Object> selectParameters, Int32 pageSize, Int32 pageIndex, Int32 maximumRecords)
      67:     {
      68:         this.Hql = hql;
      69:         this.SelectParameters = selectParameters;
      70:         this.PageSize = pageSize;
      71:         this.PageIndex = pageIndex;
      72:         this.MaximumRecords = maximumRecords;
      73:     }
      74:  
      75:     public IQueryable Query
      76:     {
      77:         get;
      78:         set;
      79:     }
      80:  
      81:     public String Hql
      82:     {
      83:         get;
      84:         set;
      85:     }
      86:  
      87:     public IDictionary<String, Object> SelectParameters
      88:     {
      89:         get;
      90:         private set;
      91:     }
      92:  
      93:     public Int32 PageSize
      94:     {
      95:         get;
      96:         set;
      97:     }
      98:  
      99:     public Int32 PageIndex
     100:     {
     101:         get;
     102:         set;
     103:     }
     104:  
     105:     public Int32 MaximumRecords
     106:     {
     107:         get;
     108:         set;
     109:     }
     110: }
     111:  
     112: [Serializable]
     113: public sealed class EntityEventArgs : CancelEventArgs
     114: {
     115:     public EntityEventArgs(Object entity)
     116:     {
     117:         this.Entity = entity;
     118:     }
     119:  
     120:     public Object Entity
     121:     {
     122:         get; 
     123:         set;
     124:     }
     125: }
     126:  
     127: [Serializable]
     128: public sealed class OperationCompletedEventArgs : EventArgs
     129: {
     130:     public OperationCompletedEventArgs(DataSourceOperation operation, Object entity)
     131:     {
     132:         this.Entity = entity;
     133:         this.Operation = operation;
     134:     }
     135:  
     136:     public OperationCompletedEventArgs(DataSourceOperation operation, IList results)
     137:     {
     138:         this.Results = results;
     139:         this.Operation = operation;
     140:     }
     141:  
     142:     public DataSourceOperation Operation
     143:     {
     144:         get;
     145:         private set;
     146:     }
     147:  
     148:     public Object Entity
     149:     {
     150:         get;
     151:         protected set;
     152:     }
     153:  
     154:     public IList Results
     155:     {
     156:         get;
     157:         private set;
     158:     }
     159: }

    Now, let’s see concrete examples of its usage. First, using Mode Hql:

       1: <nh:NHibernateDataSource runat="server" ID="nhds" RefreshBeforeUpdate="true" EntityName="Product" Mode="Hql" Hql="from Product p where size(p.OrderDetails) > :size">
       2:     <SelectParameters>
       3:         <asp:Parameter Name="size" DefaultValue="1" Type="Int32" />
       4:     </SelectParameters>
       5:     <InsertParameters>
       6:         <asp:Parameter Name="Name" DefaultValue="Some Name" Type="String" />
       7:         <asp:Parameter Name="Price" DefaultValue="100" Type="Decimal" />
       8:     </InsertParameters>
       9:     <UpdateParameters>
      10:         <asp:QueryStringParameter Name="ProductId" QueryStringField="ProductId" Type="Int32" />
      11:         <asp:QueryStringParameter Name="Price" DefaultValue="50" Type="Decimal" />
      12:     </UpdateParameters>
      13:     <DeleteParameters>
      14:         <asp:QueryStringParameter Name="ProductId" QueryStringField="ProductId" Type="Int32" />
      15:     </DeleteParameters>
      16: </nh:NHibernateDataSource>

    You can see that the Hql property has a parameter, price, which is bound to a parameter in SelectParameters with the same name. Each parameter is an instance of the Parameter class, here I am using a parameter with a static value (Parameter) and another that takes a value from the query string (QueryStringParameter), but others exist. To help with NHibernate insert and update operations, I created a new Parameter class, EntityParameter, that knows how to retrieve a en entity or a proxy to an entity:

       1: public sealed class EntityParameter : Parameter
       2: {
       3:     public EntityParameter()
       4:     {
       5:         this.Lazy = true;
       6:     }
       7:  
       8:     public String EntityName
       9:     {
      10:         get;
      11:         set;
      12:     }
      13:  
      14:     public Object Id
      15:     {
      16:         get;
      17:         set;
      18:     }
      19:  
      20:     public Boolean Lazy
      21:     {
      22:         get;
      23:         set;
      24:     }
      25:  
      26:     protected override Parameter Clone()
      27:     {
      28:         return (new EntityParameter(){ EntityName = this.EntityName, Id = this.Id });
      29:     }
      30:  
      31:     protected override Object Evaluate(HttpContext context, Control control)
      32:     {
      33:         var dataSource = control as NHibernateDataSource;
      34:  
      35:         if (dataSource == null)
      36:         {
      37:             throw (new InvalidOperationException("EntityParameter can only be used with NHibernateDataSource."));
      38:         }
      39:  
      40:         using (var session = dataSource.EffectiveSessionFactory.OpenStatelessSession())
      41:         {
      42:             var metadata = dataSource.GetMetadata(this.EntityName);
      43:  
      44:             if (metadata == null)
      45:             {
      46:                 throw (new InvalidOperationException("Entity could not be found."));
      47:             }
      48:  
      49:             var entityType = metadata.GetMappedClass(EntityMode.Poco);
      50:             var idType = metadata.IdentifierType.ReturnedClass;
      51:             var id = Convert.ChangeType(this.Id, idType);
      52:             var entity = (this.Lazy == true) ? (metadata as IEntityPersister).CreateProxy(id, session.GetSessionImplementation()) : session.Get(entityType.FullName, id);
      53:  
      54:             return (entity);
      55:         }
      56:     }
      57: }

    As for Mode Linq, an example using a QueryExtender is in order:

       1: <asp:TextBox runat="server" ID="name"/>
       2:  
       3: <asp:QueryExtender runat="server" TargetControlID="nhds">
       4:     <asp:SearchExpression DataFields="Name" SearchType="StartsWith">
       5:         <asp:ControlParameter ControlID="name" />
       6:     </asp:SearchExpression>
       7:     <asp:OrderByExpression DataField="Price" Direction="Descending" />
       8: </asp:QueryExtender>
       9:  
      10: <nh:NHibernateDataSource runat="server" ID="nhds" RefreshBeforeUpdate="true" EntityName="Product" Mode="Linq">
      11:     <InsertParameters>
      12:         <asp:Parameter Name="Name" DefaultValue="Some Name" Type="String" />
      13:         <asp:Parameter Name="Price" DefaultValue="100" Type="Decimal" />
      14:     </InsertParameters>
      15:     <UpdateParameters>
      16:         <asp:QueryStringParameter QueryStringField="ProductId" Name="ProductId" Type="Int32" />
      17:         <asp:Parameter Name="Price" DefaultValue="50" Type="Decimal" />
      18:     </UpdateParameters>
      19:     <DeleteParameters>
      20:         <asp:QueryStringParameter QueryStringField="ProductId" Name="ProductId" Type="Int32" />
      21:     </DeleteParameters>
      22: </nh:NHibernateDataSource>

    The LINQ query produced by the NHibernateDataSource is intercepted by the QueryExtender and a where (SearchExpression) and a order by (OrderByExpression) clauses are added to it. Other expressions can be used, inheriting from DataSourceExpression, and some take parameters of type Parameter. Do notice that filtering and sorting is performed server-side, not client-side.

    Of course, this can certainly be improved, let me hear your thoughts and questions.

    And that’s it. Enjoy!

    Read more...

  • Book Reviews

    This page will list all of my unofficial book reviews, in no particular order.

    Microsoft Azure Development Cookbook Second Edition

    https://weblogs.asp.net/ricardoperes/microsoft-azure-development-cookbook-second-edition-review

    https://www.packtpub.com/application-development/microsoft-azure-development-cookbook-second-edition

    Windows Presentation Foundation 4.5 Cookbook

    https://weblogs.asp.net/ricardoperes/windows-presentation-foundation-4-5-cookbook-review

    https://www.packtpub.com/application-development/windows-presentation-foundation-45-cookbook

    NHibernate 2 Beginner's Guide

    https://weblogs.asp.net/ricardoperes/nhibernate-2-beginner-s-guide-review

    https://www.packtpub.com/application-development/nhibernate-2-beginners-guide

    NHibernate 3.0 Cookbook

    https://weblogs.asp.net/ricardoperes/nhibernate-3-0-cookbook-review

    https://www.packtpub.com/application-development/nhibernate-30-cookbook

    Visual Studio 2012 and .NET 4.5 Expert Development Cookbook

    https://weblogs.asp.net/ricardoperes/visual-studio-2012-and-net-4-5-expert-development-cookbook-review

    https://www.packtpub.com/application-development/visual-studio-2012-and-net-45-expert-development-cookbook

    NHibernate in Action Book

    https://weblogs.asp.net/ricardoperes/nhibernate-in-action-book

    http://www.manning.com/kuate/

    Instant StyleCop Code Analysis How-to Review

    https://weblogs.asp.net/ricardoperes/instant-stylecop-code-analysis-how-to-review

    https://www.packtpub.com/hardware-and-creative/instant-stylecop-code-analysis-how-instant

    Developing Windows Store Apps with HTML5 and JavaScript

    https://weblogs.asp.net/ricardoperes/developing-windows-store-apps-with-html5-and-javascript-review

    https://www.packtpub.com/application-development/developing-windows-store-apps-html5-and-javascript

    Programming Entity Framework

    https://weblogs.asp.net/ricardoperes/programming-entity-framework-book

    http://learnentityframework.com/

    Pro WF: Windows Workflow in .NET 3.5

    https://weblogs.asp.net/ricardoperes/pro-wf-windows-workflow-in-net-3-5

    http://www.apress.com/9781430209751

    Read more...

  • Getting the SQL from a LINQ Query in NHibernate

    In case you ever want to have a look at the generated SQL before it is actually executed, you can use this extension method:

       1: public static String ToSql(this IQueryable queryable)
       2: {
       3:     var sessionProperty = typeof(DefaultQueryProvider).GetProperty("Session", BindingFlags.NonPublic | BindingFlags.Instance);
       4:     var session = sessionProperty.GetValue(queryable.Provider, null) as ISession;
       5:     var sessionImpl = session.GetSessionImplementation();
       6:     var factory = sessionImpl.Factory;
       7:     var nhLinqExpression = new NhLinqExpression(queryable.Expression, factory);
       8:     var translatorFactory = new ASTQueryTranslatorFactory();
       9:     var translator = translatorFactory.CreateQueryTranslators(nhLinqExpression, null, false, sessionImpl.EnabledFilters, factory).First();
      10:     //in case you want the parameters as well
      11:     //var parameters = nhLinqExpression.ParameterValuesByName.ToDictionary(x => x.Key, x => x.Value.Item1);
      12:  
      13:     return translator.SQLString;
      14: }

    Just call it on an IQueryable or IQueryable<T> instance, such as the one you got from ISession.Query<T>:

       1: var query = session.Query<Product>().Where(p => p.Price > 100);
       2: var sql = query.ToSql();

    Parameters such as 100 will be located in the nhLinqExpression.ParameterValuesByName collection.

    Read more...