How to set properties on a generic entity?
You can either use reflection -- which has been mentioned by several people already -- or you can create an interface. If you are using auto-generated entities, they are defined with the partial
keyword, so you can create another class file in the same project, and give it the same namespace and class definition, and you can have that implement your interface. Then in the code you posted above, you check to see if the object implements your interface, if so, cast to it, then set the values.
The advantage of the interface is that you aren't using reflection (which can be an expensive operation), and also, any future entities you create will work automatically just by implementing your interface.
In cases where your entity properties don't exactly match your interface, you can explicitly implement the interface, that will handle any naming irregularities.
Example: Let's say you've defined an interface, IContainAuditProperties
, and you have all your applicable entities implement that interface, you could do the following inside a block where you are looping over all your new/altered entities:
var entity = entry.Entity as IContainAuditProperties;
if(entity != null)
{
entity.CreatedDate = DateTime.Now;
entity.ModifiedDate = DateTime.Now;
//etc.
}
Starting with C# 7.0, the above code can be simplified:
if(entry.Entity is IContainAuditProperties entity) {
entity.CreatedDate = DateTime.Now;
entity.ModifiedDate = DateTime.Now;
//etc.
}
You can use below method. It will set the property if it exists. Using GetType
at each call may cause some overhead, it needs optimization.
private bool TrySetProperty(object obj, string property, object value) {
var prop = obj.GetType().GetProperty(property, BindingFlags.Public | BindingFlags.Instance);
if(prop != null && prop.CanWrite) {
prop.SetValue(obj, value, null);
return true;
}
return false;
}
Usage
TrySetProperty(entry.Entity, "ModifiedDate", DateTime.Now);
You can check if an object has a public settable property with a certain name using this code:
public static bool HasWritableProperty(this object objectToCheck, string propertyName)
{
var type = objectToCheck.GetType();
//get a property info for the property, but only if it is a public instance property
var pi = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public)
//return false if no propery is found
if (pi==null) return false;
//get the set method for the property
var setter = pi.GetSetMethod();
//if it's null the property is not writable
return (setter != null);
}
However, this is not a very efficient code, as it uses a lot of reflection.
If you go this route, I would memoize the result of this code, so every check will be run at most once.
What I actually do in my projects (I use EF Code First), is have a BaseEntity
class that has the properties that are common, and have the concrete entities inherit from the BaseEntity
class. Then I have a FillEntityMetadata
method, that is more or less this:
protected void FillEntityMetadata(BaseEntity entity, bool isUpdate = false)
{
// Set audit data.
if (!isUpdate)
{
entity.CreatedBy = CurrentUser.ID;
entity.CreatedOn = DateTime.Now;
entity.IsActive = true;
}
entity.LastModifiedBy = CurrentUser.ID;
entity.LastModifiedOn = DateTime.Now;
}
Note that for this to work, you can also use an interface, IBaseEntity
, and it would function in the same way.