9

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.

2
  • 1
    From another forum I got this answer msdn.microsoft.com/en-us/library/vstudio/… (ConvertEmptyStringToNull property) Commented Sep 29, 2014 at 15:44
  • Hi Naomi, I'm glad you found it, why not make an answer of your own? Commented Sep 30, 2014 at 23:27

8 Answers 8

7

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();
<#+ } #>
Sign up to request clarification or add additional context in comments.

1 Comment

We're using Reverse POCO Generator and I actually need the opposite (convert null strings to empty). Do you know if your suggestion is valid for this scenario?
6

I actually found a better way to this, it's actually built in the system, plus it uses the internal ordinal metadata of the entities which are loaded anyway (I haven't tested the performance difference, but this should be hell of a lot faster than reflection):

private const string StringType = "String";
private const EntityState SavingState = EntityState.Added | EntityState.Modified;
public override int SaveChanges()
{
  //when using on ObjectContext replace 'objectContext' with 'this',
  //and override SaveChanges(SaveOptions options) instead:

  var objectContext = ((IObjectContextAdapter)this).ObjectContext;
  var savingEntries = objectContext.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);
    }
  }
  return base.SaveChanges(); //SaveChanges(options) on ObjectContext
}

1 Comment

As a minor improvement, the Name == StringType check can be replaced with f => f.FieldType.TypeUsage.EdmType is PrimitiveType pt && pt.PrimitiveTypeKind == PrimitiveTypeKind.String.
2

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();

Comments

1

Not that I'm aware of.

You could possibly write a class that inherited from ObjectContext and override SaveChanges() to do that and use that instead of ObjectContext in your x.objectlayer.cs / x.designer.cs

1 Comment

oops, while im writng this you posted it... lol thanks, i voted up
1

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;
    }
}

1 Comment

Are you sure this is gonna work on DB queries? I believe it won't.
1

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);
}

Comments

0

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.

Comments

0

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);
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.