1

I know the title wasn't the greatest, but here is what I actually want to accomplish.

I have a details view that represents Entity1 and its associations. I am capturing the property names and values in a key/value pair. I am currently using reflection to set the Entity properties to the corresponding values for non associations. I doubt this is the most efficient way, but I have been unable to find a better way using Expression Trees. So now I need to set the associations of Entity1 to their corresponding entities based on the primary key of those entity associations, call them Entity2-4.

When iterating the Properties of Entity1, I don't know how to construct a dynamic query to Entity2-4 and set Entity1.association to the corresponding entity. Here is the code I have so far:

foreach (string k in e.Values.Keys)
{
    if (e.Values[k] != null && !String.IsNullOrEmpty(e.Values[k].ToString()))
    {
        System.Type objectType = Entity1.GetType();
        PropertyInfo[] p = objectType.GetProperties();

        foreach (PropertyInfo pi in p)
        {
            // set Entity1.Property for non associations (works just fine)
            if (pi.Name == k)
            {
                System.Type t = pi.PropertyType;
                pi.SetProperty(e.Values[k].ToString(), Entity1);
                break;
            }
            // when i see pi.Name contain Reference, I know I'm working on an association
            else if (pi.Name.Contains("Reference"))
            {
                // k is in the form of Entity.Property
                var name = pi.Name.Left("Reference");
                var keys = k.Split('.');
                var ent = keys[0];
                var prop = keys[1];
                if (name == ent)
                {
                    // here I need to obtain an instance from the db
                    // ie generate my dynamic query to the Entity with the name
                    // contained within the var "ent"

                    // I tried using reflection and could instantiate the entity 
                    // but it did me no good as I needed the entity from the db

                    var entityInstance = some dynamic query;

                    // here I need to set the association of Entity1 to entityInstance from above
                    // normally I would use reflection, but I'm not sure that would work
                    // since EntityReference is the actual property returned by reflection
                    Entity1.SetAssocation(prop, Entity2);
                    break;
                }
            }
        }
    }
}

EDIT

I basically need construct the entity and its association entities so that I can submit them to the data context. Entity 2 through 4 exist in the db, I need to query the db to obtain the instances to that I may associate them to the new Entity1 I am creating and going to submit.

My basic model:

Entity1

Entity1.ID
Entity1.Prop1
Entity1.Prop2
Entity1.Prop3
Entity1.Entity2
Entity1.Entity3
Entity1.Entity4

Entity2

Entity2.ID
Entity2.Name

Entity3

Entity3.ID
Entity3.Name

Entity4

Entity4.ID
Entity4.Name
m4chine
  • 431
  • 1
  • 7
  • 16
  • 1
    You shouldn't need any reflection at all to display data in a grid. Maybe you better show your entity model (the essential part) and a sample output. There's bound to be a much easier way to achieve what you want. – Gert Arnold Apr 10 '13 at 20:36
  • I can help with the `associations discovery` part - but I 2nd what Gert said - that's a bit unlikely unless you really have a unique scenario. – NSGaga-mostly-inactive Apr 10 '13 at 20:39
  • Ok so here's the deal, I am using Dynamic Data, and am using a Details View to perform an insert. But I have properties that I need to manage in code, so I use the details view to set up the UI, I do my magic in the DetailsView_ItemInserting event, submit the entity, and call Cancel on the ItemInserting since I have just manually added the entity. – m4chine Apr 10 '13 at 20:51
  • Whats the best way to show my entity model? – m4chine Apr 10 '13 at 20:51
  • Sorry its not a grid but a Details View, typo fixed. – m4chine Apr 10 '13 at 20:54
  • Also note, this all worked when I was using Linq-to-SQL, but I am in the process of changing the data model to Entity Framework. In Linq-to-SQL, I was able to set the properties via reflection and it was just fine. But EF has EntityReferences that arent't the actual entity themselves, which is why I'm stumped as to what to do. – m4chine Apr 10 '13 at 20:58
  • @NSGaga can you help with how to perform the association discovery? – m4chine Apr 10 '13 at 21:10
  • @m4chine I can - the discovery part at least, you seem to have/want some dynamic constructing also, not sure about that. If you want me I'll post an answer. – NSGaga-mostly-inactive Apr 10 '13 at 21:45

2 Answers2

3

We've had an in-depth discussion about how to get the metadata out of the DbContext. Here are a few links to get you started. And I'll add some specific comments.

How I can read EF DbContext metadata programmatically?

A short summary (but you should check in there for more):

using (var db = new MyDbContext())
{
    var objectContext = ((IObjectContextAdapter)db).ObjectContext;
    var container = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace);

    var dependents = ((EntitySet)(set)).ForeignKeyDependents;
    var principals = ((EntitySet)(set)).ForeignKeyPrincipals;
    var navigationProperties = ((EntityType)(set.ElementType)).NavigationProperties;

    // and e.g. for many-to-many (there is more for other types)
    ManyToManyReferences = navigationProperties.Where(np =>
        np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many &&
        np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
        .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
        .ToList();
}

@Goran Obradovic did a great job to wrap up what I started into a set of reusable queries (my due credits to him:).

I have worked out all other sorts of information that's in there. This is just for the DataSpace.CSpace (which is the most useful to you), but there is also DataSpace.SSpace etc. - which are more for creating SQL queries etc. I'll put most links at the bottom.


Specifics:
In your case following might be helpful:
(note: I'm not entirely sure what you're after, but I'm trying to guess here what's the direction that you're heading)
db.Set<Worker>().Find(1);

Is the generic method for accessing DbSet for a specific entity.

You could also construct it from a Type if you need it to be fully dynamic, e.g....
(I always wanted to do this:)

MethodInfo setMethod = typeof(DbContext).GetMethod("Set", new Type[]{});
MethodInfo genericSetMethod = setMethod.MakeGenericMethod(new Type[] { typeof(YourEntity) });
var set = genericSetMethod.Invoke(db, new object[] {});  

Put your entity - or your Type instead of typeof(YourEntity).

You could then proceed and query that for e.g. Find(id) - for that entity - to get concrete values etc.

That's as dynamic as it gets - I'm not sure if that's what you want - but I'm just throwing things out here in case you need it.

That should get you started at least I hope.


links:
(all are posts of mine - some may be more or less relevant but might help)
How I can read EF DbContext metadata programmatically?
How check by unit test that properties mark as computed in ORM model?
Get Model schema to programmatically create database using a provider that doesn't support CreateDatabase
Programmatic data transformation in EF5 Code First migration
Community
  • 1
  • 1
NSGaga-mostly-inactive
  • 14,052
  • 3
  • 41
  • 51
  • I guess I should have mentioned I am stuck on EF1 for .net 3.5 at the moment. I have been unable to translate the DbContext referenced methods into ObjectContext equivalents. Any suggestions? – m4chine Apr 15 '13 at 17:53
  • you should have :) that was early versions, and things changed **a lot** since then. Now it's more stable, minor changes in between. I have no idea what was the syntax back then, sorry. – NSGaga-mostly-inactive Apr 15 '13 at 19:03
  • try to find some info on the web about DbContext to ObjectContext for earlier, It's in there for sure. Or maybe it is the same for you but you just didn't manage or something differs, minor. What you're doing 'below' isn't pretty :). What we have made above (my code) works nicely - and solved many issues around here with dynamic stuff (but not everything). Or try to move to newer version. No other ideas, I don't have that early EF anywhere any more. – NSGaga-mostly-inactive Apr 16 '13 at 20:02
0

So, I wasn't able to perform this dynamically. But here is my working solution. Can anyone advise on how to perform this dynamically?

foreach (string k in e.Values.Keys)
{
    if (e.Values[k] != null && !String.IsNullOrEmpty(e.Values[k].ToString()))
    {
        System.Type objectType = roster.GetType();
        PropertyInfo[] p = objectType.GetProperties();

        foreach (PropertyInfo pi in p)
        {
            if (pi.Name == k)
            {
                System.Type t = pi.PropertyType;
                pi.SetProperty(e.Values[k].ToString(), roster);
                break;
            }
            else if (pi.Name.Contains("Reference"))
            {
                var name = pi.Name.Left("Reference");
                var keys = k.Split('.');
                var entityName = keys[0];
                var prop = keys[1];
                if (name == entityName )
                {
                    var val = e.Values[k].ToString();
                    switch (pi.Name)
                    {
                        case "Entity2Reference":
                            Entity1.Entity2Reference.EntityKey = new EntityKey("MyEntities." + entityName + "s", prop, val);
                            break;

                        case "Entity3Reference":
                            Entity1.Entity3Reference.EntityKey = new EntityKey("MyEntities." + entityName + "s", prop, val);
                            break;

                        case "Entity4Reference":
                            Entity1.Entity4Reference.EntityKey = new EntityKey("MyEntities." + entityName + "s", prop, Int64.Parse(val));
                            break;
                    }
                }
            }
        }
    }
}
m4chine
  • 431
  • 1
  • 7
  • 16