I am using an ADO.NET Entity-Framework ObjectContext to access my data store.
I want the if values are set with empty strings, they should automatically become null.
I've just adapted the code above to the new release of Entity Framework 4.1 (DbContext).
public override int SaveChanges()
{
var objContext = ((IObjectContextAdapter)this).ObjectContext;
var entries = objContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified).Select(
entry => entry.Entity);
foreach (var entity in entries)
{
string str = typeof(string).Name;
var properties = from p in entity.GetType().GetProperties()
where p.PropertyType.Name == str
select p;
foreach (var item in properties)
{
string value = (string)item.GetValue(entity, null);
if (value != null && value.Trim().Length == 0)
{
item.SetValue(entity, null, null);
}
}
}
return base.SaveChanges();
If your using Entity Framework 4, you can use T4 templates to accomplish this. Just place this in the getter of each string property in your .tt template file, and it will replace empty strings with null and automatically trim strings. No need to use reflection.
<#+ if (primitiveProperty.TypeUsage.ToString().Split('.').Last() == "String") { #>
if (value == "") value = null;
else value = value.Trim();
<#+ } #>
Here is a solution for Entity Framework Core (tested in V2). Had to bang my way through the API due to limited documentation so there might be other ways to accomplish the same thing. Note that the original objects are modified using this method.
public override int SaveChanges()
{
ConvertWhitespaceToNulls();
return base.SaveChanges();
}
private void ConvertWhitespaceToNulls()
{
var entityEntries = this.ChangeTracker
.Entries()
.Where(x => x.State == EntityState.Modified || x.State == EntityState.Added && x.Entity != null);
foreach (var e in entityEntries)
foreach (var currentValue in e.CurrentValues.Properties.Where(p => p.ClrType == typeof(string) && p.IsNullable))
if (string.IsNullOrWhiteSpace((string) currentValue.FieldInfo.GetValue(e.Entity)))
currentValue.FieldInfo.SetValue(e.Entity, null);
}
Rather than complicating things by overriding the ObjectContext I use string extension methods to convert values for database storage
public static class StringExtensions
{
public static string EmptyStringToNull(this string s)
{
return string.IsNullOrWhiteSpace(s) ? null : s;
}
public static object EmptyStringToDBNull(this string s)
{
if (string.IsNullOrWhiteSpace(s))
return DBNull.Value;
else
return s;
}
}
Just for completeness, here is the accepted answer written as a partial class instead of inherited. Note that this version also trims strings.
using System.Data;
using System.Data.Objects;
using System.Linq;
public partial class MyObjectContext {
private const string StringType = "String";
private const EntityState SavingState = EntityState.Added | EntityState.Modified;
public override int SaveChanges(SaveOptions options) {
var savingEntries = this.ObjectStateManager.GetObjectStateEntries(SavingState);
foreach (var entry in savingEntries) {
var curValues = entry.CurrentValues;
var fieldMetadata = curValues.DataRecordInfo.FieldMetadata;
var stringFields = fieldMetadata.Where(f => f.FieldType.TypeUsage
.EdmType.Name == StringType);
foreach (var stringField in stringFields) {
var ordinal = stringField.Ordinal;
var curValue = curValues[ordinal] as string;
if (curValue != null && curValue.All(char.IsWhiteSpace)) {
curValues.SetValue(ordinal, null);
}
else if (curValue != null && curValue != curValue.Trim()) {
curValues.SetValue(ordinal, curValue.Trim());
}
}
}
return base.SaveChanges(options);
}
}
I have also shown the required usings
because I find it frustrating when I copy'n'paste code and the IDE throws up some errors about type or namespace not found.
I used Shimmy's solution and was pleased until I discovered that strings in my complex types were being missed. In other words, I needed a way to null out nullable whitespace-only strings in not only my core object/record, but any of its non-primitive object properties, and theirs, and theirs...
Following is my recursive adaptation. I can't speak to its elegance or production quality because it hasn't been live for very long, but it appears to be working for me so far and may at least serve as a starting point for someone else.
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
public class MyDataContext : DbContext
{
public override int SaveChanges()
{
ObjectStateEntry[] savingObjectStateEntries = ((IObjectContextAdapter)this)
.ObjectContext.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified).ToArray();
foreach (ObjectStateEntry savingObjectStateEntry in savingObjectStateEntries)
SetEmptyStringsToNull(savingObjectStateEntry.CurrentValues);
return base.SaveChanges();
}
private static void SetEmptyStringsToNull(CurrentValueRecord currentValueRecord)
{
if (currentValueRecord != null)
for (int i = 0; i < currentValueRecord.FieldCount; i++)
if (currentValueRecord[i] is CurrentValueRecord)
SetEmptyStringsToNull(currentValueRecord[i] as CurrentValueRecord);
else if ((currentValueRecord[i] is string)
&& (currentValueRecord.DataRecordInfo.FieldMetadata[i].FieldType as EdmProperty).Nullable
&& string.IsNullOrWhiteSpace(currentValueRecord[i] as string))
currentValueRecord.SetValue(i, null);
}
}