views:

529

answers:

4

How do i avoid using hardcoded table and field names in entity framework? For example:

  Contact contact =
        contactQuery.Include("SalesOrderHeader.SalesOrderDetail")
        .FirstOrDefault();

Is there a way to get that info from the context or metadata? I'd love to be able say SalesOrderHeader.TableName or SalesOrderHeaderFields.SalesOrderDetails.FieldName

+1  A: 

It's just a property name, so you can get it via reflection.

Craig Stuntz
I guess, but going through reflection is very expensive.
Vitalik
Not really. It never changes, so you only need to do it once. Make it a class static property. It will initialize in microseconds, and you will never have to do it again.
Craig Stuntz
I have a better very dynamic solution using expression trees, see my answer: http://stackoverflow.com/questions/683960/how-to-avoid-hardcoded-field-names-in-entity-framework/3433134#3433134
Shimmy
A: 

Example (would love to hear better ideas, didn't find a way to accept a type name as a reference like the VB GetType does):

VB:

<Extension()>
Function GetRelation(Of TEntity As Type)(entity As TEntity,
 ParamArray path() As TEntity) As String
  GetRelation = entity.Name
  For Each type In path
    GetRelation &= "." & type.Name
  Next
End Function

'Here is your line:
Dim x = Context.Employee.Include(
 GetType(Contact).GetRelation(GetType(SalesOrderHeader),
 GetType(SalesOrderDetail)))

C#:

static string GetRelation<TEntity>(this TEntity entity, params TEntity[] path)
 where TEntity : Type
{
   string ret = entity.Name;
   foreach (TEntity type in path) ret += "." + type.Name;
   return ret;
}

//Put your line here:
var x = context.Employee.Include(typeof(Contact).GetRelation(
 typeof(SalesOrderHeader),
 typeof salesOrderDetail)));

I wish there would be a dynamic way of reflection... Again, I am open minded, any suggestions would be blessed thanks

Shimmy
Trying to replace Where TEntity : Type with something more dynamical...
Shimmy
That code won't work. You need to specify entity set names and property names, not type names. The code given will generate an incorrect argument in the call to Include
Craig Stuntz
... unless the property names and entity set names just happened to be identical to the type name.
Craig Stuntz
Look at my new answer http://stackoverflow.com/questions/683960/how-to-avoid-hardcoded-field-names-in-entity-framework/3433134#3433134
Shimmy
+2  A: 

If you want to use the Entity Frameworks metadata, you need to go looking through the MetadataWorkspace which hangs off the ObjectContext.

The starting point is to get the EntityType for your base type, in your case Contact.

I have an EF tips series and in Tip 13 I show an extension method on MetadataWorkspace that gets the EntityType for a particular CLR Type:

public static EntityType GetCSpaceEntityType<T>(
        this MetadataWorkspace workspace);

You can use this like this:

var contactEntity = ctx.MetadataWorkspace.GetCSpaceEntityType<Contact>();

Once you have this you can look at it's NavigationProperties to find the relationship's and the names you are interested in including:

i.e.

foreach(var np in contactEntity.NavigationProperties)
{
    Console.WriteLine("Include: {0}", np.Name);
    Console.WriteLine("... Recursively include ");
    EntityType relatedType = 
       (np.ToEndMember.TypeUsage.EdmType as RefType).ElementType;
    //TODO: go repeat the same process... i.e. look at the relatedTypes 
    // navProps too until you decide to stop.
}

Of course how you decide what you want to Include is up to you. Hope this helps

Alex

Alex James
+1  A: 

Read Say goodbye to the hardcoded ObjectQuery(T).Include calls, and you will be able to do it like the following, using lambda & linq expression trees:

Dim c = GetObjectContext()
Dim query = c.Vendors.Include(Function(v) v.Contact.Addresses.Single.State)

There is a C# samle as well.

HTH

Shimmy