How to ensure proxies are created when using the repository pattern with entity framework? - entity-framework

I have this method in my SurveyController class:
public ActionResult AddProperties(int id, int[] propertyids, int page = 1)
{
var survey = _uow.SurveyRepository.Find(id);
if (propertyids == null)
return GetPropertiesTable(survey, page);
var repo = _uow.PropertySurveyRepository;
propertyids.Select(propertyid => new PropertySurvey
{
//Setting the Property rather than the PropertyID
//prevents the error occurring later
//Property = _uow.PropertyRepository.Find(propertyid),
PropertyID = propertyid,
SurveyID = id
})
.ForEach(x => repo.InsertOrUpdate(x));
_uow.Save();
return GetPropertiesTable(survey, page);
}
The GetPropertiesTable redisplays Properties but PropertySurvey.Property is marked virtual and I have created the entity using the new operator, so a proxy to support lazy loading was never created and it is null when I access it. When we have access direct to the DbContext we can use the Create method to explicitly create the proxy. But I have a unit of work and repository pattern here. I guess I could expose the context.Create method via a repository.Create method and then I need to remember to use that instead of the new operator when I add an entity . But wouldn't it be better to encapsulate the problem in my InsertOrUpdate method? Is there some way to detect that the entity being added is not a proxy when it should be and substitute a proxy? This is my InsertOrUpdate method in my base repository class:
protected virtual void InsertOrUpdate(T e, int id)
{
if (id == default(int))
{
// New entity
context.Set<T>().Add(e);
}
else
{
// Existing entity
context.Entry(e).State = EntityState.Modified;
}
}

Based on the answer supplied by qujck. Here is how you can do it without having to employ automapper:
Edited to always check for proxy - not just during insert - as suggested in comments
Edited again to use a different way of checking whether a proxy was passed in to the method. The reason for changing the technique is that I ran into a problem when I introduced an entity that inherited from another. In that case an inherited entity can fail the entity.e.GetType().Equals(instance.GetType() check even if it is a proxy. I got the new technique from this answer
public virtual T InsertOrUpdate(T e)
{
DbSet<T> dbSet = Context.Set<T>();
DbEntityEntry<T> entry;
if (e.GetType().BaseType != null
&& e.GetType().Namespace == "System.Data.Entity.DynamicProxies")
{
//The entity being added is already a proxy type that supports lazy
//loading - just get the context entry
entry = Context.Entry(e);
}
else
{
//The entity being added has been created using the "new" operator.
//Generate a proxy type to support lazy loading and attach it
T instance = dbSet.Create();
instance.ID = e.ID;
entry = Context.Entry(instance);
dbSet.Attach(instance);
//and set it's values to those of the entity
entry.CurrentValues.SetValues(e);
e = instance;
}
entry.State = e.ID == default(int) ?
EntityState.Added :
EntityState.Modified;
return e;
}
public abstract class ModelBase
{
public int ID { get; set; }
}

I agree with you that this should be handled in one place and the best place to catch all looks to be your repository. You can compare the type of T with an instance created by the context and use something like Automapper to quickly transfer all of the values if the types do not match.
private bool mapCreated = false;
protected virtual void InsertOrUpdate(T e, int id)
{
T instance = context.Set<T>().Create();
if (e.GetType().Equals(instance.GetType()))
instance = e;
else
{
//this bit should really be managed somewhere else
if (!mapCreated)
{
Mapper.CreateMap(e.GetType(), instance.GetType());
mapCreated = true;
}
instance = Mapper.Map(e, instance);
}
if (id == default(int))
context.Set<T>().Add(instance);
else
context.Entry(instance).State = EntityState.Modified;
}

Related

How to write a generic WebAPI Put method against Entity Framework that works with child lists?

I am tinkering with WebAPI to create a generic implementation for entity framework. I am able to implement most of the methods just fine, but am finding PUT to be tricky in non-trivial cases. The implementation most commonly found online works for simple entities:
[HttpPut]
[ActionName("Endpoint")]
public virtual T Put(T entity)
{
var db = GetDbContext();
var entry = db.Entry(entity);
entry.State = EntityState.Modified;
var set = db.Set<T>();
set.Attach(entity);
db.SaveChanges();
return entity;
}
...but does not delete or update child lists:
public class Invoice
{
...
public virtual InvoiceLineItem {get; set;} //Attach method doesn't address these
}
In an MVC Controller, you could simply use "UpdateModel" and it would add/update/delete children as needed, however that method is not available on ApiController. I understand that some code would be necessary to get the original item from the database, and that it would need to use Include to get the child lists, but can't quite figure out the best way to replicate UpdateModel's functionality:
[HttpPut]
[ActionName("Endpoint")]
public virtual T Put(T entity)
{
var db = GetDbContext();
var original = GetOriginalFor(entity);
//TODO: Something similar to UpdateModel(original), such as UpdateModel(original, entity);
db.SaveChanges();
return original;
}
How can I implement UpdateModel OR somehow implement Put in such a way that it will handle child lists?
The routine dont validate entity, but fill the pre-existent entity.
protected virtual void UpdateModel<T>(T original, bool overrideForEmptyList = true)
{
var json = ControllerContext.Request.Content.ReadAsStringAsync().Result;
UpdateModel<T>(json, original, overrideForEmptyList);
}
private void UpdateModel<T>(string json, T original, bool overrideForEmptyList = true)
{
var newValues = JsonConvert.DeserializeObject<Pessoa>(json);
foreach (var property in original.GetType().GetProperties())
{
var isEnumerable = property.PropertyType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (isEnumerable && property.PropertyType != typeof(string))
{
var propertyOriginalValue = property.GetValue(original, null);
if (propertyOriginalValue != null)
{
var propertyNewValue = property.GetValue(newValues, null);
if (propertyNewValue != null && (overrideForEmptyList || ((IEnumerable<object>)propertyNewValue).Any()))
{
property.SetValue(original, null);
}
}
}
}
JsonConvert.PopulateObject(json, original);
}
public void Post()
{
var sample = Pessoa.FindById(12);
UpdateModel(sample);
}

Cascading Properties in Entity Framework via Repository Pattern

Say I have a class Person that has an "IsArchived" property. A Person can have a Collection - each of which can also be Archived.
Right now I have an UpdatePerson method in my repository that looks like this:
public Person UpdatePerson(Person person)
{
_db.Persons.Attach(person);
var entry = _db.Entry(person);
entry.Property(e => e.Name).IsModified = true;
entry.Property(e => e.Friends).IsModified = true;
SaveChanges();
return entry.Entity;
}
And I have a separate Repository method for Archive:
public void ArchivePerson(int personId)
{
var person = _db.Persons.FirstOrDefault(c => c.PersonId == personId);
if (person != null)
{
person.IsArchived = true;
foreach (var friend in person.Friends)
{
ArchivePerson(friend.PersonId);
}
SaveChanges();
}
}
This means that from my WebAPI, I have to dedicate a function to Archiving/Unarchiving. It would be cool if I could just call Update and set the Archived for the given entity, and apply this to all child entities where possible. Is there any way to accomplish this, or am I best off sticking to the ArchivePerson repository method?

How to update not every fields of an object using Entity Framework and EntityState.Modified

I need to update all fields except property1 and property2 for the given entity object.
Having this code:
[HttpPost]
public ActionResult Add(object obj)
{
if (ModelState.IsValid)
{
context.Entry(obj).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
return View(obj);
}
How to change it to add an exception to obj.property1 and obj.property2 for not being updated with this code?
Let's assume that you have a collection of the properties to be excluded:
var excluded = new[] { "property1", "property2" };
With EF5 on .NET 4.5 you can do this:
var entry = context.Entry(obj);
entry.State = EntityState.Modified;
foreach (var name in excluded)
{
entry.Property(name).IsModified = false;
}
This uses a new feature of EF5 on .NET 4.5 which allows a property to be set as not modified even after it has been previously set to modified.
When using EF 4.3.1 or EF5 on .NET 4 you can do this instead:
var entry = context.Entry(obj);
foreach (var name in entry.CurrentValues.PropertyNames.Except(excluded))
{
entry.Property(name).IsModified = true;
}
You can't define such an exception. You can however mark single properties as modified:
context.Entry(obj).Property(o => o.Property3).IsModified = true;
context.Entry(obj).Property(o => o.Property4).IsModified = true;
// etc.
Note that setting IsModified to false is not supported once you have marked the state of the whole entity to Modified.
For your purpose I would actually prefer to load the entity from the database and then update it using normal change tracking:
var objInDB = context.Objects.Single(o => o.Id == obj.Id);
obj.Property1 = objInDB.Property1;
obj.Property2 = objInDB.Property2;
context.Entry(objInDB).CurrentValues.SetValues(obj);
context.SaveChanges();
Note that only changed properties will be saved by default by Automatic Detect changes.
See EF 6 and EF Core articles
This question was already nicely answered, but I wanted to provide an extension method for anyone who would like to use it.
This code was developed for EF 4.3.1
//You will need to import/use these namespaces
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
//Update an entity object's specified columns, comma separated
//This method assumes you already have a context open/initialized
public static void Update<T>(this DbContext context, T entityObject, params string[] properties) where T : class
{
context.Set<T>().Attach(entityObject);
var entry = context.Entry(entityObject);
foreach(string name in properties)
entry.Property(name).IsModified = true;
context.SaveChanges();
}
Usage Example
using (FooEntities context = new FooEntities())
{
FooEntity ef = new FooEntity();
//For argument's sake say this entity has 4 columns:
// FooID (PK), BarID (FK), Name, Age, CreatedBy, CreatedOn
//Mock changes
ef.FooID = 1;
ef.Name = "Billy";
ef.Age = 85;
context.Update<FooEntity>(ef, "Name", "Age"); //I only want to update Name and Age
}
This is an update that works for .net CORE and maybe can help someone who needs a generic solucion and wants to exclude some properties base on different conditions.
I'm using reflection to iterate through the properties and update base on its property value, in this case, as example, i'm excluding the null properties.
public virtual TEntity Update(TEntity entity)
{
dbSet.Attach(entity);
dbContext.Entry(entity).State = EntityState.Modified;
var entry = dbContext.Entry(entity);
Type type = typeof(TEntity);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.GetValue(entity, null) == null)
{
entry.Property(property.Name).IsModified = false;
}
}
dbContext.SaveChanges();
return entity;
}
The answers above (most of them) use DbContext. For those who is using ObjectContext these solutions arent accessible.
Here is solution for ObjectContext strictly (EF5 .NET 4.5):
ctx.AddObject("ENTITYNAME", item);
ctx.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
var entry = ctx.ObjectStateManager.GetObjectStateEntry(item);
entry.RejectPropertyChanges("PROPERTY_TO_EXCLUDE");

Entity Framework Detached Object Merging

I have a scenario where I am using Entity Framework in a WCF service, and changes happen on a non-tracked instance of a type that is mapped back to the database via code-first (non-trivial updates and deletes throughout the instance's object tree). When I try to attach the non-tracked instance into the context, EF is only recognizing changes to the simple value types on the root object.
Does anyone know of an elegant solution for this scenario? I am looking for a way to do this by using a generic repository, and avoiding having to run through the instance's entire object tree managing the "attach/detach" state of every object. I have considered possibly using ValueInjecter or AutoMapper to run the changes on a fully hydrated and tracked instance of the "old" state in order for the context to pickup the changes. Also, how would Nhibernate handle this situation?
Thanks in advance for your input!
UPDATE (7/31/2012): I have updated the code to handle genericly-typed keys, and some typing issues with EF Proxies. Also added some helper extensions when dealing with IEntity types. This implementation isn't perfect, but it is very functional.
UPDATE (3/13/2012): I have added a feature request for cleaner merging in EF. The request is located here: http://data.uservoice.com/forums/72025-ado-net-entity-framework-ef-feature-suggestions/suggestions/2679160-better-merging-change-tracking
UPDATE (3/12/2012): I have posted my solution below. It uses FubuCore, ValueInjecter, and requires entities to be marked with one of two interfaces, either IEntity, or IRecursiveEntity for recursive classes. The solution will handle recursive, self-linked entities.
Also, I am referencing a generic repository (Repository) that allows me to get a reference to the IDbSet that EF exposes. This could be substituded with any other generic or specific repository. Lastly, the IEntity interface uses an int? id, however you could define that however you want (Guid/Guid?). The solution itself isn't quite as elegant as I would like, however it allows for much more elegant data access code when behind a physical WCF service boundary.
public class DomainMergeInjection : ConventionInjection
{
private readonly Repository _repository;
private readonly Dictionary<string, object> _potentialParentObjectDump;
private readonly Cache<Type, Type> _entityTypesAndKeysCache;
public DomainMergeInjection(Repository repository)
{
_repository = repository;
_potentialParentObjectDump = new Dictionary<string, object>();
_entityTypesAndKeysCache = new Cache<Type, Type>();
}
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
protected override object SetValue(ConventionInfo c)
{
if(c.SourceProp.Value == null)
return null;
//for value types and string just return the value as is
if(c.SourceProp.Type.IsSimple())
return c.SourceProp.Value;
//TODO: Expand on this to handle IList/IEnumerable (i.e. the non-generic collections and arrays).
//handle arrays
if(c.SourceProp.Type.IsArray)
{
var sourceArray = c.SourceProp.Value as Array;
// ReSharper disable PossibleNullReferenceException
var clonedArray = sourceArray.Clone() as Array;
// ReSharper restore PossibleNullReferenceException
for(int index = 0; index < sourceArray.Length; index++)
{
var sourceValueAtIndex = sourceArray.GetValue(index);
//Skip null and simple values that would have already been moved in the clone.
if(sourceValueAtIndex == null || sourceValueAtIndex.GetType().IsSimple())
continue;
// ReSharper disable PossibleNullReferenceException
clonedArray.SetValue(RetrieveComplexSourceValue(sourceValueAtIndex), index);
// ReSharper restore PossibleNullReferenceException
}
return clonedArray;
}
//handle IEnumerable<> also ICollection<> IList<> List<>
if(c.SourceProp.Type.IsGenericEnumerable())
{
var t = c.SourceProp.Type.GetGenericArguments()[0];
if(t.IsSimple())
return c.SourceProp.Value;
var tlist = typeof(List<>).MakeGenericType(t);
dynamic list = Activator.CreateInstance(tlist);
var addMethod = tlist.GetMethod("Add");
foreach(var sourceItem in (IEnumerable)c.SourceProp.Value)
{
addMethod.Invoke(list, new[] { RetrieveComplexSourceValue(sourceItem) });
}
return list;
}
//Get a source value that is in the right state and is tracked if needed.
var itemStateToInject = RetrieveComplexSourceValue(c.SourceProp.Value);
return itemStateToInject;
}
private object RetrieveComplexSourceValue(object source)
{
//If the source is a non-tracked type, or the source is a new value, then return its value.
if(!source.ImplementsIEntity(_entityTypesAndKeysCache) || source.IsEntityIdNull(_entityTypesAndKeysCache))
return source;
object sourceItemFromContext;
//Handle recursive entities, this could probably be cleaned up.
if(source.ImplementsIRecursiveEntity())
{
var itemKey = source.GetEntityIdString(_entityTypesAndKeysCache) + " " + ObjectContext.GetObjectType(source.GetType());
//If we have a context item for this key already, just return it. This solves a recursion problem with self-linking items.
if(_potentialParentObjectDump.ContainsKey(itemKey))
return _potentialParentObjectDump[itemKey];
//Get the source from the context to ensure it is tracked.
sourceItemFromContext = GetSourceItemFromContext(source);
//Add the class into the object dump in order to avoid any infinite recursion issues with self-linked objects
_potentialParentObjectDump.Add(itemKey, sourceItemFromContext);
}
else
//Get the source from the context to ensure it is tracked.
sourceItemFromContext = GetSourceItemFromContext(source);
//Recursively use this injection class instance to inject the source state on to the context source state.
var itemStateToInject = sourceItemFromContext.InjectFrom(this, source);
return itemStateToInject;
}
private object GetSourceItemFromContext(object source)
{
if(source == null)
return null;
//Using dynamic here to "AutoCast" to an IEntity<>. We should have one, but it's important to note just in case.
dynamic sourceEntityValue = source;
var sourceEntityType = ObjectContext.GetObjectType(source.GetType());
var sourceKeyType = sourceEntityType.GetEntityKeyType();
var method = typeof(DomainMergeInjection).GetMethod("GetFromContext", BindingFlags.Instance | BindingFlags.NonPublic);
var generic = method.MakeGenericMethod(sourceEntityType, sourceKeyType);
var sourceItemFromContext = generic.Invoke(this, new object[] { new object[] { sourceEntityValue.Id } });
return sourceItemFromContext;
}
// ReSharper disable UnusedMember.Local
private TItem GetFromContext<TItem, TKey>(object[] keys) where TItem : class, IEntity<TKey>
// ReSharper restore UnusedMember.Local
{
var foundItem = _repository.GetDbSet<TItem>().Find(keys);
return foundItem;
}
}
public static class EntityTypeExtensions
{
/// <summary>
/// Determines if an object instance implements IEntity.
/// </summary>
/// <param name="entity"></param>
/// <param name="entityCache">A cache to hold types that do implement IEntity. If the cache does not have the Type and the Type does implement IEntity, it will add the type to the cache along with the </param>
/// <returns></returns>
public static bool ImplementsIEntity(this object entity, Cache<Type, Type> entityCache = null)
{
//We need to handle getting the proxy type if this is an EF Code-First proxy.
//Please see for more info: http://msdn.microsoft.com/en-us/library/dd456853.aspx
var entityType = ObjectContext.GetObjectType(entity.GetType());
if(entityCache != null && entityCache.Has(entityType))
return true;
var implementationOfIEntity = entityType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof (IEntity<>));
if(implementationOfIEntity == null)
return false;
if(entityCache != null)
{
var keyType = implementationOfIEntity.GetGenericArguments()[0];
entityCache.Fill(entityType, keyType);
}
return true;
}
/// <summary>
/// Determines if an object instances implements IRecurisveEntity
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static bool ImplementsIRecursiveEntity(this object entity)
{
//We need to handle getting the proxy type if this is an EF Code-First proxy.
//Please see for more info: http://msdn.microsoft.com/en-us/library/dd456853.aspx
var entityType = ObjectContext.GetObjectType(entity.GetType());
var implementsIRecursiveEntity = entityType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IRecursiveEntity<>));
return implementsIRecursiveEntity;
}
/// <summary>
/// Determines whether or not an Entity's Id is null. Will throw an exception if a type that does not implement IEntity is passed through.
/// </summary>
/// <param name="entity"></param>
/// <param name="entityCache"></param>
/// <returns></returns>
public static bool IsEntityIdNull(this object entity, Cache<Type, Type> entityCache = null)
{
bool isEntityIdNull = ExecuteEntityIdMethod<bool>("IsEntityIdNull", entity, entityCache);
return isEntityIdNull;
}
/// <summary>
/// Determines whether or not an Entity's Id is null. Will throw an exception if a type that does not implement IEntity is passed through.
/// </summary>
/// <param name="entity"></param>
/// <param name="entityCache"></param>
/// <returns></returns>
public static string GetEntityIdString(this object entity, Cache<Type, Type> entityCache = null)
{
string entityIdString = ExecuteEntityIdMethod<string>("GetEntityIdString", entity, entityCache);
return entityIdString;
}
private static T ExecuteEntityIdMethod<T>(string methodName, object entityInstance, Cache<Type, Type> entityCache = null)
{
if(!entityInstance.ImplementsIEntity(entityCache))
throw new ArgumentException(string.Format("Parameter entity of type {0} does not implement IEntity<>, and so ist not executable for {1}!", entityInstance.GetType(), methodName));
//We need to handle getting the proxy type if this is an EF Code-First proxy.
//Please see for more info: http://msdn.microsoft.com/en-us/library/dd456853.aspx
var entityType = ObjectContext.GetObjectType(entityInstance.GetType());
var keyType = entityCache != null ? entityCache[entityType] : entityType.GetEntityKeyType();
var method = typeof(EntityTypeExtensions).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic);
var generic = method.MakeGenericMethod(keyType);
T returnValue = (T)generic.Invoke(null, new[] { entityInstance });
return returnValue;
}
private static string GetEntityIdString<TKey>(IEntity<TKey> entity)
{
var entityIdString = entity.Id.ToString();
return entityIdString;
}
private static bool IsEntityIdNull<TKey>(IEntity<TKey> entity)
{
//We need to handle getting the proxy type if this is an EF Code-First proxy.
//Please see for more info: http://msdn.microsoft.com/en-us/library/dd456853.aspx
var entityType = ObjectContext.GetObjectType(entity.GetType());
if(entityType.IsPrimitive)
return false;
//NOTE: We know that this entity's type is NOT primitive, therefore we can cleanly test for null, and return properly.
// ReSharper disable CompareNonConstrainedGenericWithNull
var entityIdIsNull = entity.Id == null;
// ReSharper restore CompareNonConstrainedGenericWithNull
return entityIdIsNull;
}
public static Type GetEntityKeyType(this Type typeImplementingIEntity)
{
var implementationOfIEntity = typeImplementingIEntity.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEntity<>));
if(implementationOfIEntity == null)
throw new ArgumentException(string.Format("Type {0} does not implement IEntity<>", typeImplementingIEntity));
var keyType = implementationOfIEntity.GetGenericArguments()[0];
return keyType;
}
}
public interface IEntity<TKey>
{
TKey Id { get; set; }
}
public interface IRecursiveEntity<TKey> : IEntity<TKey>
{
IRecursiveEntity<TKey> Parent { get; }
IEnumerable<IRecursiveEntity<TKey>> Children { get; }
}
you could use the detached object only as a DTO,
and after refill the object from context with values from the DTO
with ValueInjecter this would be:
//manually
conObj.InjectFrom(dto);
conObj.RefTypeProp.InjectFrom(dto.RefTypeProp);
...
//or by writing a custom injection:
conObj.InjectFrom<ApplyChangesInjection>(dto);
here's the Injection that will do that automatically, (I did it by modifying a bit the DeepClone Injection from VI's home page)
the trick here is that the Injection uses itself in the SetValue method
public class ApplyChangesInjection : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
protected override object SetValue(ConventionInfo c)
{
if (c.SourceProp.Value == null) return null;
//for value types and string just return the value as is
if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string))
return c.SourceProp.Value;
//handle arrays - not impl
//handle IEnumerable<> also ICollection<> IList<> List<> - not impl
//for simple object types apply the inject using the corresponding source
return c.TargetProp.Value
.InjectFrom<ApplyChangesInjection>(c.SourceProp.Value);
}
}
//Note: I'm not handling collections in this injection, I just wanted you to understand the principle,
you can look at the original http://valueinjecter.codeplex.com/wikipage?title=Deep%20Cloning&referringTitle=Home

Using DataAnnotations (DisplayColumn) in WCF RIA Services

I have created an entity framework 4.0 (DB-First) model, added my partial classes and used DataAnnotations on them to have a perfect UI on the client.
I have some relations between my tables and used DisplayColumn on top my classes. e.g. I have a User class that has [DataColumn("UserName")] attribute on top of the class. And a Message class which has "public User Sender" which has [Include] attribute on top of the property.
Also, I have used .Include("User") in my DomainService to load the User who's related to a message.
But in my datagrid, I see User : (UserID) (UserID=Key property of User entity) instead of UserName that I have specified. I looked in the generated code in my SL project and it correctly decorated my User class with DisplayColumn attribute. But still, I cannot see UserName in my grid.
Any help would be greatly appreciated.
Update: Here's my question in code:
As I have mentioned, Owner, UserName, MessageId, UserId have been defined in my auto-generated model. UserMeta class has nothing special.
[MetadataType(typeof(MessageMeta))]
public partial class Message
{
}
public class MessageMeta
{
[Include()]
[Display(Name = "Belongs to", Order = 4)]
[Association("Message_User","MessageId","UserId",IsForeignKey = true)]
public virtual User Owner { get; set; }
}
[MetadataType(typeof(UserMeta))]
[DisplayColumn("UserName")]
public partial class User
{
}
In my DomainService:
public IQueryable<Message> GetMessages()
{
return this.ObjectContext.Messages.Include("Owner");
}
At last, I had to use Reflection. For DataGrid:
private void OnAutoGenerateColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
//Need to get an array, but should always just have a single DisplayColumnAttribute
var atts = e.PropertyType.GetCustomAttributes(typeof(DisplayColumnAttribute),true);
foreach (DisplayColumnAttribute d in atts)
{
DataGridTextColumn col = (DataGridTextColumn)e.Column;
//Make sure that we always have the base path
if(col.Binding.Path.Path!="")
{
col.Binding = new Binding()
{
Path = new PropertyPath(col.Binding.Path.Path + "." + d.DisplayColumn)
};
}
//Only do the first one, just in case we have more than one in metadata
break;
}
}
And for Telerik RadGridView:
var column = e.Column as GridViewDataColumn;
if (column == null)
{
return;
}
// Need to get an array, but should always just have a single DisplayColumnAttribute
var atts = column.DataType.GetCustomAttributes(typeof(DisplayColumnAttribute), true);
foreach (DisplayColumnAttribute d in atts)
{
// Make sure that we always have the base path
if (column.DataMemberBinding.Path.Path != "")
{
column.DataMemberBinding = new Binding()
{
Path = new PropertyPath(column.DataMemberBinding.Path.Path + "." + d.DisplayColumn)
};
}
// Only do the first one, just in case we have more than one in metadata
break;
}