How do I get EF to persist empty strings as NULL? - entity-framework

In my domain, there's no important distinction between NULL and an empty string. How do I get EF to ignore the difference between the two and always persist an empty string as NULL?

Empty string is not default value for string property so it means your code is setting empty strings somewhere. In such case it is your responsibility to handle it.
If you are using code first with POCOs you can use custom setter:
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
if (value == String.Empty)
{
_myProperty = null;
}
else
{
_myProperty = value;
}
}
}

Here is a function I placed in my DbContext subclass that replaces empty or whitespace strings with null.
I still didn't optimize it so any performance hints will be very appreciated.
private const string StringType = "String";
private const EntityState SavingState = EntityState.Added | EntityState.Modified;
public override int SaveChanges()
{
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();
}
Optimization considerations:
Identify a string type property by different way other than string comparison I tried to look-up some enumeration of the built-in types but didn't find
Cache string fields for types (maybe is unnecessary, will have to decompile and see what the original impl does
Order result by entity type, backup iterated entity type, if next iterated entity is same type, use previous metadata, again, if the metadata is anyway there, performance is cheaper the way it is
Limit string length for whitespace check - i.e. if a string length > x, skip checking whether its a whitespace string or not
I'm using Silverlight and the TextBoxes in the UI set all the string properties to empty strings.
I tried setting:
<TextBox
Text="{Binding MyStringProperty,
Mode=TwoWay,
ValidatesOnDataErrors=True,
TargetNullValue=''}"/>
But it didn't help much.

That's not Entity Framework's job.
You should do it in your repository, or in the database with triggers.
Or do it at the start (e.g when the data comes in, UI, external source, etc)

Related

Serialization approach in relationship

We are using codefluent entities for the BOM, webapi for the controllers and angularjs Framework on the client side.
We are facing a problem when storing reference of an object in the parent object. Anytime, reference would be nulled by generated code.
Given two entities with a Relationship EntA[EntAId, prop1, EntB] and EntB[EntBId, prop1, prop2]
I end up with two classes:
class EntA{
EntAId
prop1
EntB
EntBEntBId
}
and
class EntB{
EntBId
prop1
prop2
}
CodeFluent has generated following code:
[System.Xml.Serialization.XmlElementAttribute(IsNullable=false)]
[System.ComponentModel.DataObjectFieldAttribute(true)]
public System.Guid EntBEntBId
{
get
{
if (((this._EntBEntBId.Equals(CodeFluentPersistence.DefaultGuidValue) == true)
&& (this._entB != null)))
{
this._EntBEntBId = this._entB.EntBId;
}
return this._EntBEntBId;
}
set
{
if ((System.Collections.Generic.EqualityComparer<System.Guid>.Default.Equals(value, this.EntBEntBId) == true))
{
return;
}
this._entB = null;
this._EntBEntBId = value;
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntB"));
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntBEntBId"));
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Namespace.EntB.EntB EntB
{
get
{
if ((this._entB == null))
{
this._entB = Namespace.EntB.EntB.Load(this._EntBEntBId);
}
return this._entB;
}
set
{
this._EntBEntBId = CodeFluentPersistence.DefaultGuidValue;
this._entB = value;
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntB"));
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntBEntBId"));
}
}
On client side (using Angular) I want to register objectA.EntBEntBId without sending the whole EntB object. Thus I would consider this snippet below to nullify EntB.
if(objectA.EntB)
objectA.EntB = null
This end up sending correct Stream to server (without the whole B object being serialized).
When HTTP PUT call is triggered, webapi would valuate classes first going through get/set methods. Property EntBEntBId would be valuated correctly, but then setter of EntB property would keep erasing previous value (as EntB is currently null).
Is there any way to avoid such behavior ?
Thanks in advance for your answer;
I might be answering my own question, but when using delete objectA.entB instead of objectA.entB = null Stream would not contain entB property, thus not going through setter of it.

Filehelpers and Entity Framework

I'm using Filehelpers to parse a very wide, fixed format file and want to be able to take the resulting object and load it into a DB using EF. I'm getting a missing key error when I try to load the object into the DB and when I try and add an Id I get a Filehelpers error. So it seems like either fix breaks the other. I know I can map a Filehelpers object to a POCO object and load that but I'm dealing with dozens (sometimes hundreds of columns) so I would rather not have to go through that hassle.
I'm also open to other suggestions for parsing a fixed width file and loading the results into a DB. One option of course is to use an ETL tool but I'd rather do this in code.
Thanks!
This is the FileHelpers class:
public class AccountBalanceDetail
{
[FieldHidden]
public int Id; // Added to try and get EF to work
[FieldFixedLength(1)]
public string RecordNuber;
[FieldFixedLength(3)]
public string Branch;
// Additional fields below
}
And this is the method that's processing the file:
public static bool ProcessFile()
{
var dir = Properties.Settings.Default.DataDirectory;
var engine = new MultiRecordEngine(typeof(AccountBalanceHeader), typeof(AccountBalanceDetail), typeof(AccountBalanceTrailer));
engine.RecordSelector = new RecordTypeSelector(CustomSelector);
var fileName = dir + "\\MOCK_ACCTBAL_L1500.txt";
var res = engine.ReadFile(fileName);
foreach (var rec in res)
{
var type = rec.GetType();
if (type.Name == "AccountBalanceHeader") continue;
if (type.Name == "AccountBalanceTrailer") continue;
var data = rec as AccountBalanceDetail; // Throws an error if AccountBalanceDetail.Id has a getter and setter
using (var ctx = new ApplicationDbContext())
{
// Throws an error if there is no valid Id on AccountBalanceDetail
// EntityType 'AccountBalanceDetail' has no key defined. Define the key for this EntityType.
ctx.AccountBalanceDetails.Add(data);
ctx.SaveChanges();
}
//Console.WriteLine(rec.ToString());
}
return true;
}
Entity Framework needs the key to be a property, not a field, so you could try declaring it instead as:
public int Id {get; set;}
I suspect FileHelpers might well be confused by the autogenerated backing field, so you might need to do it long form in order to be able to mark the backing field with the [FieldHidden] attribute, i.e.,
[FieldHidden]
private int _Id;
public int Id
{
get { return _Id; }
set { _Id = value; }
}
However, you are trying to use the same class for two unrelated purposes and this is generally bad design. On the one hand AccountBalanceDetail is the spec for the import format. On the other you are also trying to use it to describe the Entity. Instead you should create separate classes and map between the two with a LINQ function or a library like AutoMapper.

How to get the maximum length of a string from an EDMX model in code?

I've created an EDMX object from a database I'm programming against.
I need to get input from a user and save it to a row in the database table. The problem is that I need to limit the length of input strings to the width of the corresponding VARCHAR column in the database.
When I browse the model, I can clearly see in the properties window that the model knows the max length of the string, but I don't know how to access this data in code.
If I want to write something like this:
Entities entities = new Entities();
myTable = entities.myTable.First();
if (userInput.length > myTable.columnA.MaxLength)
{
// tell the user that the input is too long.
}
else
{
myTable.columnA = userInput;
}
How do I write it?
Update: I would like to point out that the IObjectContextAdapater mentioned in the answers below is in the System.Data.Entity.Infrastructure namespace.
Here are two methods by which you can read the meta data:
int? GetMaxLength(DbContext context, string tableName, string propertyName)
{
var oc = ((IObjectContextAdapter)context).ObjectContext;
return oc.MetadataWorkspace.GetItems(DataSpace.CSpace).OfType<EntityType>()
.Where(et => et.Name == tableName)
.SelectMany(et => et.Properties.Where(p => p.Name == propertyName))
.Select (p => p.MaxLength)
.FirstOrDefault();
}
int? GetMaxLength<T>(DbContext context, Expression<Func<T, object>> property)
{
var memberExpression = (MemberExpression)property.Body;
string propertyName = memberExpression.Member.Name;
return GetMaxLength(context, typeof(T).Name, propertyName);
}
So you can either enter the table name and property name, or an expression that specifies the property you're interested in.
Another approach could be to create a MetaData class and use the MaxLength attribute.
It's not very pretty; reading edmx properties at runtime is not something Microsoft exposed easily or documented well (or in some cases, at all). context is your DBContext.
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var entityType = objectContext.MetadataWorkspace.GetItems<EntityType>(DataSpace.CSpace).Where(e => e.Name == "your entity name").First();
var facets = entityType.Properties["your property name"].TypeUsage.Facets;
facets will look something like this, so you'll need to look for the MaxLength Name(may not exist, depending on the underlying field type) and get the Value:
Count = 5
[0]: Nullable=false
[1]: DefaultValue=null
[2]: MaxLength=250
[3]: Unicode=false
[4]: FixedLength=false
If you modify the T4 template you can add your own attribute to the properties that have MaxLength set.
If you can find the right place to add it, it's something as simple as this:
var lengthAttributeText = edmProperty.MaxLength.HasValue
? string.Format("[MaxLength({0})] ", edmProperty.MaxLength.Value)
: "";
And then add this into the text for the property line. (Tricky to be more detailed since I've already modified my .tt file a lot; also the lack of proper IDE support for .tt files makes this a lot harder than it could be.)

Ormlite and PostgreSQL - Error inserting text array with custom persister

I have been working to setup Ormlite as the primary data access layer between a PostgreSQL database and Java application. Everything has been fairly straightforward, until I started messing with PostgreSQL's array types. In my case, I have two tables that make use of text[] array type. Following the documentation, I created a custom data persister as below:
public class StringArrayPersister extends StringType {
private static final StringArrayPersister singleTon = new StringArrayPersister();
private StringArrayPersister() {
super(SqlType.STRING, new Class<?>[]{String[].class});
}
public static StringArrayPersister getSingleton() {
return singleTon;
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
String[] array = (String[]) javaObject;
if (array == null) {
return null;
} else {
String join = "";
for (String str : array) {
join += str +",";
}
return "'{" + join.substring(0,join.length() - 1) + "}'";
}
}
#Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
String string = (String) sqlArg;
if (string == null) {
return null;
} else {
return string.replaceAll("[{}]","").split(",");
}
}
}
And then in my business object implementation, I set up the persister class on the column likeso:
#DatabaseField(columnName = TAGS_FIELD, persisterClass = StringArrayPersister.class)
private String[] tags;
When ever I try inserting a new record with the Dao.create statement, I get an error message saying tags is of type text[], but got character varying... However, when querying existing records from the database, the business object (and text array) load just fine.
Any ideas?
UPDATE:
PostGresSQL 9.2. The exact error message:
Caused by: org.postgresql.util.PSQLException: ERROR: column "tags" is
of type text[] but expression is of type character varying Hint: You
will need to rewrite or cast the expression.
I've not used ormlite before (I generally use MyBatis), however, I believe the proximal issue is this code:
private StringArrayPersister() {
super(SqlType.STRING, new Class<?>[]{String[].class});
}
SqlType.String is mapped to varchar in SQL in the ormlite code, and so therefore I believe is the proximal cause of the error you're getting. See ormlite SQL Data Types info for more detail on that.
Try changing it to this:
private StringArrayPersister() {
super(SqlType.OTHER, new Class<?>[]{String[].class});
}
There may be other tweaks necessary as well to get it fully up and running, but that should get you passed this particular error with the varchar type mismatch.

How to get the type of an object in a collection of objects at runtime?

In my code I get the type of an object (Party object) in a for loop and get the property info of a particular property "firstname". All the objects in the Parties[] collection return the same type so I would like to get the type outside of do while loop only once and still need to be able to get the property "firstname" from the correct party object.
Is it possible to do this way? Thanks for any help.
public List<Party> Parties { get; set; }
PropertyInfo info = null;
i = 1
do
{
foreach (Field field in TotalFields)
{
info = Parties[i - 1].GetType().GetProperty("firstname");
//some other code here
}
i++;
} while (i <= Parties.Count);
When you get the value for a property through a PropertyInfo object, you need to pass an object instance from which to fetch the value. This means that you can reuse the same PropertyInfo instance for several objects, given that they are of the same type as the PropertyInfo was created for:
// Note how we get the PropertyInfo from the Type itself, not an object
// instance of that type.
PropertyInfo propInfo = typeof(YourType).GetProperty("SomeProperty");
foreach (YourType item in SomeList)
{
// this assumes that YourType.SomeProperty is a string, just as an example
string value = (string)propInfo.GetValue(item, null);
// do something sensible with value
}
Your question is tagged as being C# 3, but for completeness it's worth mentioning that this can be made somewhat simpler in C# 4 by using dynamic:
foreach (dynamic item in SomeList)
{
string value = item.SomeProperty;
// do something sensible with value
}