Posts Tagged ‘Insert’
StaleStateException for Entity with Assigned Id and version in NHibernate
I was trying to persist an entity using NHibernate that uses Assigned Id’s and Version concurrency mechanism. Every time I try to persist a new instance, I am getting a StaleStateException. As per my knowledge the concurrency check will happen when we try to do an update, in my scenario it’s throwing while doing an insert. Also in the nhibernate log, I could see nhiberate is firing update command instead of insert.
After doing some googling I come across a stackoverflow post, that NHibernate will fire update or insert based on three parameters.
-
Id
-
Version
-
Timestamp
In my case Id is assigned and it will exist even if it’s a new data and I can’t help it. I am not using Timestamp. So the issue narrowed down to Version. Entity do have a version and unfortunately it’s updated using Automapper, while converting DTO to Entity. I use unix timestamp as version and we added a custom mapping in automapper to fill current date in unix timestamp format if version field is coming as empty from the client and is useful in update scenario. So for NHibernate this is not a new data because it has Id and version, so it will try to do an update instead of insert and that causes all the issues for me.
To solve this issue, either we can use Save() instead of SaveOrUpdate() or set version to zero for new data.
.NET Reflection–Helpful Functions
In this post I will explain few functions I created to solve some of the scenarios in one of my project.
Property Defaulter
In one of my project I had to deal with a table that have hundreds of fields but we are dealing with very few field in the table. The several fields of the table needs default values either zero or empty. I know if I am not finding a generic solution I will be in trouble as I had to fill all the fields my self and I don’t want to do that. So I created a small function that will do the job of setting my entities to default values if the properties doesn’t have any values. Here is my function
public static void SetDefaultValue<TEntity>(TEntity entity) where TEntity : class { try { PropertyInfo[] properties = typeof(TEntity).GetProperties(); bool isSetDefaultVal = false; foreach (PropertyInfo property in properties) { object propertyValue; if (property.CanRead) { isSetDefaultVal = false; if (property.PropertyType.Name != "String" &&
property.PropertyType.Name != "Single" &&
property.PropertyType.Name != "Int32") continue; try { propertyValue = property.GetValue(entity, null); } catch (Exception ex) { //log error continue; } if (propertyValue==null) { if (property.PropertyType.Name == "String") propertyValue = ""; else propertyValue = 0; isSetDefaultVal = true; } //set the value if property value is null and set to 0 in above if block. if (property.CanWrite && isSetDefaultVal == true) { if (property.PropertyType.Name == "Single") property.SetValue(entity, Convert.ToSingle(propertyValue), null); else if (property.PropertyType.Name == "Int32") property.SetValue(entity, Convert.ToInt32(propertyValue), null); else if (property.PropertyType.Name == "String") property.SetValue(entity, "", null); } } } } catch (Exception ex) { throw; } }
So how we use it, lets see with a e.g
PropertyDefaulter.SetDefaultValue<ItemMaster>(itemMaster);
The above function will inspect the properties in the itemMaster object, if any property doesn’t has value then based on the type the function will set the value. Int or float property will set to 0 and string property will set to empty.
Copy DataReader to Entities
When I was dealing with ADO.NET I used to fetch data from database table using SqlCommand’s execute reader and get the data in DataReader. I have DTO’s (Data Transfer Object) to transfer data between layers. So I have to copy all the data in DataReader to my DTO’s. I need to find an easy way rather than manually assigning values from datareader to properties. Here is my method to do the copy of data from DataReader to my DTO’s
public static List<TEntity> CopyDataReaderToEntity<TEntity>(IDataReader dataReader)
where TEntity : class { List<TEntity> entities = new List<TEntity>(); PropertyInfo[] properties = typeof(TEntity).GetProperties(); while (dataReader.Read()) { TEntity tempEntity = Activator.CreateInstance<TEntity>(); foreach (PropertyInfo property in properties) { SetValue<TEntity>(property, tempEntity, dataReader[property.Name]); } entities.Add(tempEntity); } return entities; } public static TEntity SetValue<TEntity>(PropertyInfo property, TEntity entity, object propertyValue)
where TEntity : class { if (property.CanRead) { if (property.PropertyType.Name != "String" &&
property.PropertyType.Name != "Single" &&
property.PropertyType.Name != "Int32") return entity; if (propertyValue == null) { if (property.PropertyType.Name == "String") propertyValue = ""; else propertyValue = 0; } if (property.CanWrite) { if (property.PropertyType.Name == "Single") property.SetValue(entity, Convert.ToSingle(propertyValue), null); else if (property.PropertyType.Name == "Int32") property.SetValue(entity, Convert.ToInt32(propertyValue), null); else if (property.PropertyType.Name == "String") property.SetValue(entity, propertyValue, null); } } return entity; }
Here we need to follow a convention to get it working. The convention I follow here is, the Property name in the DTO and the table field should be same.
Let’s see how we can use it.
SqlCommand cmd = new SqlCommand(qryToExecute, connection); SqlDataReader reader = cmd.ExecuteReader(); List<ItemDetails> itemDetails = SQLHelper.CopyDataReaderToEntity<ItemDetails>(reader);
Create Select and Insert SQL statement Dynamically
When I was dealing with some table that have a lot of fields, I always have trouble in creating select or insert statement. So I created a small function that creates the Select and Insert statement dynamically. Here also I follow the same convention I explained earlier, that is the field name and the property name should be same.
private static List<FiledAndValueHolder> CreateFieldValueMapper<TEntity>(TEntity entity)
where TEntity : class { List<FiledAndValueHolder> filedAndValues = new List<FiledAndValueHolder>(); PropertyInfo[] properties = typeof(TEntity).GetProperties(); foreach (PropertyInfo property in properties) { object propertyValue; string propertyName; if (property.CanRead) { if (property.PropertyType.Name != "String" &&
property.PropertyType.Name != "Single" &&
property.PropertyType.Name != "Int32") continue; propertyValue = property.GetValue(entity, null); propertyName = property.Name; if (propertyName.StartsWith("Native")) continue; FiledAndValueHolder fieldAndValue = new FiledAndValueHolder(); fieldAndValue.FieldValue = propertyValue; fieldAndValue.FiledName = propertyName; fieldAndValue.FieldType = property.PropertyType.Name; filedAndValues.Add(fieldAndValue); } } return filedAndValues; } public static string CreateInsertStatement<TEntity>(TEntity entity, string tableName)
where TEntity : class { List<FiledAndValueHolder> filedAndValues = CreateFieldValueMapper<TEntity>(entity); string sql = "insert into [dbo]." + tableName + "({0}) values ({1})"; StringBuilder fieldBuilder = new StringBuilder(); StringBuilder valueBuilder = new StringBuilder(); string seprator = string.Empty; foreach (FiledAndValueHolder filedAndValue in filedAndValues) { fieldBuilder.Append(seprator); fieldBuilder.Append(filedAndValue.FiledName); valueBuilder.Append(seprator); if (filedAndValue.FieldType == "String") { valueBuilder.Append(string.Format("'{0}'", filedAndValue.FieldValue as string)); } else { valueBuilder.Append(filedAndValue.FieldValue); } if (string.IsNullOrEmpty(seprator)) seprator = ","; } string sqlStatement = string.Format(sql, fieldBuilder.ToString(), valueBuilder.ToString()); return sqlStatement; }
public static string CreateSelectStatement<TEntity>(TEntity entity, string tableName, string selectCondition) where TEntity : class { List<FiledAndValueHolder> filedAndValues = CreateFieldValueMapper<TEntity>(entity); string sql = "select {0} from dbo.{1} where ({2})"; StringBuilder fieldBuilder = new StringBuilder(); string seprator = string.Empty; foreach (FiledAndValueHolder filedAndValue in filedAndValues) { fieldBuilder.Append(seprator); fieldBuilder.Append(filedAndValue.FiledName); if (string.IsNullOrEmpty(seprator)) seprator = ","; } return string.Format(sql, fieldBuilder.ToString(), tableName, selectCondition); }
Let’s see how we can use it.
insertItemMaster = SQLHelper.CreateInsertStatement<ItemMaster>(itemMaster, "ItemMaster");
Most of the functions I explained above doesn’t required much explanations. If any one need any more details then leme know.