Using SaveOrUpdate in Entity Framework

This is a common question for people that used NHibernate before using EF. The problem is, I have an object and I do not want to care about if this is a new object that needs to be added for the first time to the database, or it is an object that needs update or if it was already attached to the context, etc. I want to call a single Upsert method called SaveOrUpdate and let the ORM takes care of the rest.

Since I use NHibernate a lot, I’ve come out in the past with the following simple code that can helps you to have similar functionalities in EF.

public static void SaveOrUpdate
    (this ObjectContext context, TEntity entity)
    where TEntity : class
{
    ObjectStateEntry stateEntry = null;
    context.ObjectStateManager
        .TryGetObjectStateEntry(entity, out stateEntry);

    var objectSet = context.CreateObjectSet();
    if (stateEntry == null || stateEntry.EntityKey.IsTemporary)
    {
        objectSet.AddObject(entity);
    }

    else if (stateEntry.State == EntityState.Detached)
    {
        objectSet.Attach(entity);
        context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    }
}

public static ObjectContext GetObjectContext(this DbContext c)
{
    return ((IObjectContextAdapter)c).ObjectContext;
}

public static void SaveOrUpdate
    (this DbContext context, TEntity entity)
    where TEntity : class 
{
    context.GetObjectContext().SaveOrUpdate(entity);
}

This is some common code you can find around in the internet and thanks to extension method, it permits you to call SaveOrUpdate directly on your DbContext object and make your life easier. The principle on witch this code is based is simple, first of all a DbContext object can be cast to IObjectContextAdapter that has a property called ObjectContext that give you direct access to the real manager of the lifecycle of objects.

From that object you have access to the ObjectStateManager of the DbContext, that can be used to verify status of the entity thanks to the TryGetObjectStateEntry. If the object has no state entry of if the stateEntity has an EntityKey that IsTemporary, it means that this object should be added so you can call AddObject to the correct ObjectSet obtained from a call to the CreateObjectSet<T> of ObjectContext object. If the entity is in status Detatched I decided to attach and inform EF that the entity is in state Modified.

It is quite rude, but it works. The only problem with it is when you have inheritance, if you call SaveOrUpdate passing a derived class you can incur in the error

There are no EntitySets defined for the specified entity type xxx If xxx is a derived type, use the base type instead.
Parameter name: TEntity

This can be easily solved specifying the base type as type parameter in the call of SaveOrUpdate and everything should work as expected.

Gian Maria.

Relations with not-found=”ignore” disable lazy load and impact on performances

NHibernate has a lot of interesting and specific option for mapping entities that can really cover every scenario you have in mind, but you need to be aware of every implication each advanced option has on performances.

If you are in a legacy-database scenario where entity A reference Entity B, but someone outside the control of NHibernate can delete record from table used by Entity B, without setting the corresponding referencing field on Entity A. We will end with a Database with broken reference, where rows from Table A references with a field id a record in Table B that no longer exists. When this happens, if you load an Entity of type A that reference an Entity of type B that was deleted, it will throw an exception if you try to access navigation property, because NHibernate cannot find related entity in the Database.

If you know NHibernate you can use the not-found=”Ignore” mapping option, that basically tells NHibernate to ignore a broken reference key, if EntityA references an Entity B that was already deleted from database, the reference will be ignored, navigation property will be set to Null, and no exception occurs. This kind of solution is not without side effects, first of all you will find that Every time you load an Entity of Type A another query is issued to the database to verify if related Entity B is really there. This actually disable lazy load, because related entity is always selected. This is not an optimum scenario, because you will end with a lot of extra query and this happens because not-found=”ignore” is only a way to avoid a real problem: you have broken foreign-key in your database.

My suggestion is, fix data in database, keep the database clean without broken foreign-keys and remove all not-found=”ignore” mapping option unless you really have no other solution. Please remember that even if you are using NHibernate, you should not forget SQL capabilities. As an example SQL Server (and quite all of the relational database in the market) has the ability to setup rules for foreign-key, es ON DELETE SET NULL that automatically set to null a foreign key on a table, when related record is deleted. Such a feature will prevent you from having broken foreign key, even if some legacy process manipulates the database deleting records without corresponding update in related foreign-key.

Gian Maria.

NHibernate 3.2 and Mapping By Code

In version 3.2 of NHibernate a new mapping capabilities was introduced to easy mapping of entities in your project. To understand how it works and how easy it is here it is all initialization code you need if you want to accept default mapping rules for all of your classes.

cfg = new Configuration();
       
var mapper = new ConventionModelMapper();
mapper.IsEntity((t, declared) => t.Namespace.StartsWith("Myproject.Entities"));
mapper.AfterMapClass += (inspector, type, classCustomizer) =>
{
    classCustomizer.Lazy(false);
};
var mapping = mapper.CompileMappingFor(
    Assembly.GetExecutingAssembly().GetExportedTypes()
    .Where(t => t.Namespace.StartsWith("Myproject.Entities")));

cfg.AddDeserializedMapping(mapping, "AutoModel");
cfg.BuildSessionFactory();

Basically I’m creating a ConvetionModelMapper that is capable of mapping entities by convention, then the IsEntity method is used to specify a condition to make the mapper aware of the classes that needs to be mapped; in this example I simply states that everything that is in namespace Myproject.Entities should be mapped. Since I want lazy load to be disabled I register an handler on AfterMapClass, an event that is launched whenever an entity is mapped, and I simply disable lazy load stating Lazy(false).

In this super simple example all the entities are in the same assembly of this function, so I can pass the executing assembly to the CompileMappingFor() method of conventional mapper, specifying again the list of class I want to be mapped. Finally I can simply use AddDeserializedMapping method of Configuration object to add the mapping generated by CompileMappingFor and the game is done. Now you can create this simple class in Myproject.Entities

public class Customer
{

    public Int32 Id { get; set; }

    public String Name { get; set; }

    public String Surname { get; set; }
}

And let NHibernate generate the database, you got this table automatically generated in the database.

image

Figure 1: Table generates by automapping in the database.

The main problem of this approach is that this table has not Identity on Id column and all the nvarchar columns have 255 length, because this is the default of NHibnernate. Now you can solve this problem with two different approaches, the first is keeping track of your database with a different tool, like Database Projects in Visual Studio, because probably you have other part of the database that is not related to nhibernate (like stored procedure) that you need to manage. To simply this process I usually let nhibernate generate a test database when I add new entities, then I simply synchronize my database project to grab all the differencies, and then I refine them adding identities, length etc.

If you want NHibernate to manage all stuff of database creation and Updating, you need to give more detail to the Conventional mapper. Here is an example.

mapper.Class<Customer>(map => map.Id(c => c.Id, mapInfo => mapInfo.Generator(Generators.Native)));
mapper.Class<Customer>(map => map.Property(c => c.Surname, mapInfo => mapInfo.Length(40)));
mapper.Class<Customer>(map => map.Property(c => c.Name, mapInfo => mapInfo.Length(40)));

This approach can be useful if you have few entities, but when your domain is quite complex, it is slightly better to use a Database Project that gave you more control over the structure of the database with a tool dedicated to this purpose.

Gian Maria.

Troubleshoot WCF The underlying connection was closed: A connection that was expected to be kept alive was closed by the server

I’ve already blogged in the past on how to easily troubleshoot WCF Exception and that suggestion is valid for every exception you encounter in WCF. Today I have a function that gave the error

System.ServiceModel.CommunicationException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server. —> System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server. —> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. —> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host

This problem is usually caused by some error in the communication channel, but even in such a situation, enabling the trace listener immediately gave to you an idea of the problem.

 

SNAGHTML20daa9b

Figure 1: How to identify the problem thanks to WCF Trace Listener

In my situation the error is ClientProxy …. is not expected and this is due to the fact that I’m returning from WCF a couple of classes that are mapped with NHIbernate, the entity returned has a property of type Customer that is a lazy property, thus at runtime it is represented by a dynamic proxy that is not known to WCF. The solution is trivial, just issue a Fetch(obj => obj.Client) in the LINQ query to fetch Client and avoiding NH to create a proxy, but the key point is that with WCF trace listener, finding problems in WCF is matter of few seconds.

Gian MAria.

 

Unit test NHibernate query to verify N+1

When you work with ORM like nhibernate, having a tool like nhprof is the key of success. But even with NHProfiler you could not prevent people of doing wrong stuff because they do not use it :). I have a simple scenario, a developer changed a method on the server lazily fetching a property of a large resultset. The effect is that the service call, that usually responded in milliseconds starts to respond in 10 seconds.

The reason is really simple, the function loaded about 200 entities from the database and if you are sure you want to access for all 200 entities a property, you should do eager fetching because issuing ~200 queries to the database is not usually a good idea. Then after some time the function started to do timeout, so I inspected again the code and did not find anything strange, but NHprof reveals me that the query was actually fetching another related entity, from a table with millions of row causing timeout. This is due to a modification to a mapping, someone disabled proxy for that class, so NH decided to do a join with the table with millions of row, slowing the method again .

After the problem was fixed, I created some helpers that permits me to write a test that will alert me immediately in the future if such a problem is comeback again.

   1: [Test]

   2: public void verify_xxxx()

   3: {

   4:     var sut = new cccccService();

   5:     sut.DoSomething(1);

   6:     this.NhibernateQueryCount().Should().Be.EqualTo(1);

   7:     String query = this.NhibernateGetQueries().First();

   8:     query.Should().Not.Contain("relatedlin1_.Id");

   9: }

This test is not really a UnitTest, it is more an integration one, but it is able to verify that calling a function on a service class only one query is issued to the database and the query should not eager fetch data from the other table (relatedlin…). This is much more an integration test than a unit test, but it is quite interesting because it permits me to write assertion on number and text of SQL generated by NHibernate, a feature that is really interesting especially if you know that production database is quite big. To achieve this is really simple.

I wrote this simple test helper based on my infrastructure.

   1: public class InterceptNhQueriesHelper : ITestHelper

   2:  {

   3:      public const string nhQueries = "DFCDE96A-ADF7-4C46-A55B-219381364B7F";

   4:      private Dictionary<String, Level> _oldLevel = new Dictionary<string, Level>();

   5:  

   6:      #region ITestHelper Members

   7:  

   8:      public void FixtureSetUp(BaseTestFixture fixture)

   9:      {

  10:          ILogger loggerToForceInitializationOfLoggingSystem = IoC.Resolve<ILogger>();

  11:          var repository = LogManager.GetAllRepositories();

  12:          Log4NetLogEventSourceAppender interceptorAppender = new Log4NetLogEventSourceAppender();

  13:          foreach (var loggerRepository in repository)

  14:          {

  15:              Hierarchy hierarchy = (Hierarchy)loggerRepository;

  16:              if (hierarchy.GetAppenders()

  17:                      .Count(appender => appender.GetType() == typeof(Log4NetLogEventSourceAppender)) == 0)

  18:              {

  19:                  hierarchy.Root.AddAppender(interceptorAppender);

  20:                  hierarchy.RaiseConfigurationChanged(EventArgs.Empty);

  21:              }

  22:              _oldLevel[hierarchy.Name] = hierarchy.Root.Level;

  23:              hierarchy.Root.Level = Level.Debug;

  24:  

  25:              var loggers = loggerRepository.GetCurrentLoggers();

  26:              foreach (var logger in loggers)

  27:              {

  28:                  Logger realLogger = logger as Logger;

  29:                  if (realLogger.Name.IndexOf("NHIBERNATE", StringComparison.InvariantCultureIgnoreCase) >= 0)

  30:                  {

  31:                      _oldLevel[realLogger.Name] = realLogger.Level;

  32:                      realLogger.Level = Level.Debug;

  33:                      if (!realLogger.Appenders.OfType<IAppender>().Any(appender => appender.GetType() == typeof(Log4NetLogEventSourceAppender)))

  34:                      {

  35:                          //non ho appender intercettori

  36:                          realLogger.AddAppender(interceptorAppender);

  37:                      }

  38:                  }

  39:              }

  40:  

  41:              hierarchy.RaiseConfigurationChanged(EventArgs.Empty);

  42:          }

  43:  

  44:          Log4NetLogEventSourceAppender.OnLog += Log4NetLogEventSourceAppender_OnLog;

  45:          loggerToForceInitializationOfLoggingSystem.Debug("TESTLOG DEBUG");

  46:          loggerToForceInitializationOfLoggingSystem.Info("TESTLOG Info");

  47:          loggerToForceInitializationOfLoggingSystem.Error("TESTLOG Error");

  48:      }

  49:  

  50:      private BaseTestFixture currentFixture;

  51:      private List<String> SqlInstructions = new List<string>();

  52:  

  53:      void Log4NetLogEventSourceAppender_OnLog(object sender, OnLog4NetLogEventArgs e)

  54:      {

  55:          if (e.LoggingEvent.LoggerName.Equals("nhibernate.sql", StringComparison.OrdinalIgnoreCase))

  56:          {

  57:              SqlInstructions.Add(e.LoggingEvent.MessageObject as string);

  58:          }

  59:      }

  60:  

  61:      public void SetUp(BaseTestFixture fixture)

  62:      {

  63:          currentFixture = fixture;

  64:          fixture.SetIntoTestContext(nhQueries, SqlInstructions);

  65:          SqlInstructions.Clear();

  66:      }

  67:  

  68:      public void TearDown(BaseTestFixture fixture)

  69:      {

  70:          currentFixture = null;

  71:      }

  72:  

  73:      public void FixtureTearDown(BaseTestFixture fixture)

  74:      {

  75:          var repository = LogManager.GetAllRepositories();

  76:          foreach (var loggerRepository in repository)

  77:          {

  78:              Hierarchy hierarchy = (Hierarchy)loggerRepository;

  79:              hierarchy.Root.Level = _oldLevel[hierarchy.Name];

  80:  

  81:              var loggers = loggerRepository.GetCurrentLoggers();

  82:              foreach (var logger in loggers)

  83:              {

  84:                  Logger realLogger = logger as Logger;

  85:                  if (realLogger.Name.IndexOf("NHIBERNATE", StringComparison.InvariantCultureIgnoreCase) >= 0 &&

  86:                      _oldLevel.ContainsKey(realLogger.Name))

  87:                  {

  88:  

  89:                      realLogger.Level = _oldLevel[realLogger.Name];

  90:                  }

  91:              }

  92:              hierarchy.RaiseConfigurationChanged(EventArgs.Empty);

  93:          }

  94:      }

  95:  

  96:      public int Priority

  97:      {

  98:          get { return 1; }

  99:      }

 100:  

 101:      #endregion

 102:  }

this helpers seems complex, but basically it simply add during FixtureSetup an appender to log4net, this appender basically raises an event whenever it receives a log.

   1: public class Log4NetLogEventSourceAppender : AppenderSkeleton

   2:    {

   3:        private Object _syncRoot;

   4:  

   5:        public Log4NetLogEventSourceAppender()

   6:        {

   7:            _syncRoot = new object();

   8:        }

   9:  

  10:        /// <summary>

  11:        /// Occurs when [on log].

  12:        /// </summary>

  13:        public static event EventHandler<OnLog4NetLogEventArgs> OnLog;

  14:  

  15:        protected override void Append(LoggingEvent loggingEvent)

  16:        {

  17:            EventHandler<OnLog4NetLogEventArgs> temp = OnLog;

  18:            if (temp != null)

  19:            {

  20:                lock (_syncRoot)

  21:                {

  22:                    temp(null, new OnLog4NetLogEventArgs(loggingEvent));

  23:                }

  24:            }

  25:        }

  26:  

  27:    }

  28:  

  29:    public class OnLog4NetLogEventArgs : EventArgs

  30:    {

  31:        public LoggingEvent LoggingEvent { get; private set; }

  32:  

  33:        public OnLog4NetLogEventArgs(LoggingEvent loggingEvent)

  34:        {

  35:            LoggingEvent = loggingEvent;

  36:        }

  37:    }

This appender permits the helper to intercept all logs and since Nhibernate raise a log with name nhibernate.sql with the SQL Code whenever it raises a query to the database, the helper can filter for those kind of messages and store each query inside a standard String collection.

   1: public static class InterceptNhQueriesHelperMethods

   2: {

   3:     public static Int32 NhibernateQueryCount(this BaseTestFixtureWithHelper fixture)

   4:     {

   5:         return fixture.GetFromTestContext<List<String>>(InterceptNhQueriesHelper.nhQueries).Count;

   6:     }

   7:  

   8:     public static List<String> NhibernateGetQueries(this BaseTestFixtureWithHelper fixture)

   9:     {

  10:         return fixture.GetFromTestContext<List<String>>(InterceptNhQueriesHelper.nhQueries);

  11:     }

  12: }

Now I can simply decorate my Test Fixture with a  specific attribute and let the magic happens.

   1: [InterceptNhQueries]

   2:    public class XxxFixture : Test.Utilities.BaseTestFixtureWithHelper

Inside a test I can use NhibernateQueryCount() to know the number of the queries issued by NH during the test and NhibernateGetQueries() to grab the whole list and assert on how NH interacted with the database during a test.

Gian Maria.