Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I extract the database table and column name for a property on an EF4 entity?

I'm writing an auditing component for an application that uses EF4 for the data access layer. I'm able to very easily determine which entities have been modified, and via the ObjectStateEntry object I can extract the original values, current values, entity name, and property names that were modified, but I would also like to extract the raw table and and column names used in SQL Server (since they do not always match the entity and property names of the model)

Does anyone know of a good way to do this? Is it even possible? The mappings are obviously stored in the MSL, but I can't figure out a way to programmatically access those mappings.

like image 331
mrmcderm Avatar asked Oct 11 '22 12:10

mrmcderm


2 Answers

After a peek into a entity framework model designer I saw that it uses the EdmEntityTypeAttribute and DataMemberAttribute to decorate generated classes and properties. Each of them have a Name property which contains the name of the mapped entity (table, column respectively). When the property name matches the name of the column, the designer does not supply a value for positional argument Name. The code below works fine for me.

 private static string GetTableName<T>() where T : EntityObject
    {
        Type type = typeof(T);
        var at = GetAttribute<EdmEntityTypeAttribute>(type);
        return at.Name;
    }

    private static string GetColumnName<T>(Expression<Func<T, object>> propertySelector) where T : EntityObject
    {
        Contract.Requires(propertySelector != null, "propertySelector is null.");

        PropertyInfo propertyInfo = GetPropertyInfo(propertySelector.Body);
        DataMemberAttribute attribute = GetAttribute<DataMemberAttribute>(propertyInfo);
        if (String.IsNullOrEmpty(attribute.Name))
        {
            return propertyInfo.Name;
        }
        return attribute.Name;
    }

    private static T GetAttribute<T>(MemberInfo memberInfo) where T : class
    {
        Contract.Requires(memberInfo != null, "memberInfo is null.");
        Contract.Ensures(Contract.Result<T>() != null);

        object[] customAttributes = memberInfo.GetCustomAttributes(typeof(T), false);
        T attribute = customAttributes.Where(a => a is T).First() as T;
        return attribute;
    }

    private static PropertyInfo GetPropertyInfo(Expression propertySelector)
    {
        Contract.Requires(propertySelector != null, "propertySelector is null.");
        MemberExpression memberExpression = propertySelector as MemberExpression;
        if (memberExpression == null)
        {
            UnaryExpression unaryExpression = propertySelector as UnaryExpression;
            if (unaryExpression != null && unaryExpression.NodeType == ExpressionType.Convert)
            {
                memberExpression = unaryExpression.Operand as MemberExpression;
            }
        }
        if (memberExpression != null && memberExpression.Member.MemberType == MemberTypes.Property)
        {
            return memberExpression.Member as PropertyInfo;
        }
        throw new ArgumentException("No property reference was found.", "propertySelector");
    }

    // Invocation example
    private static Test()
    {
         string table = GetTableName<User>();
         string column = GetColumnName<User>(u=>u.Name);
    }
like image 76
RePierre Avatar answered Oct 21 '22 05:10

RePierre


All of the model data is available through this methods myObjectContext.MetadataWorkspace.GetEntityContainer(myObjectContext.DefaultContainerName, DataSpace.CSSpace);

that should at least give you a start on how to do what you want. DataSpace.CSSpace specifies the mapping between the Conceptual names and the Store names. DataSpace.CSpace gives you the Conceptual Model, and DataSpace.SSpace gives you the storage model.

like image 41
Zackary Geers Avatar answered Oct 21 '22 06:10

Zackary Geers