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.
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);
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With