如何在通用实体上设置属性?

时间:2013-06-06 12:57:20

标签: c# entity-framework

我想检查一个实体是否有3个属性。 CreatedDate,ModifiedDate和ModifiedBy。

现在我只是在我的对象上下文的SaveChanges()方法中硬编码我知道的那些。

例如:

bool newEntity = (entry.State == EntityState.Added);

if (type == typeof(Foo))
{
  var r = entry.Entity as Foo;
  if (r != null)
  {
    if (newEntity) 
      r.CreatedDate = DateTime.Now;
    r.ModifiedDate = DateTime.Now;
    r.ModifiedBy = HttpContext.Current.User.Identity.Name;
  }
}

我知道可以使用与此类似的代码检查对象是否具有某种方法:

public static bool HasMethod(this object objectToCheck, string methodName)
{
    var type = objectToCheck.GetType();
    return type.GetMethod(methodName) != null;
} 

但是如果不直接投射实体,我将如何获得这些属性呢?

我该怎么做:

if (HasMethod(entry.Entity))
      entry.Entity.ModifiedDate = DateTime.Now;

我正在使用ASP.Net MVC 4.

3 个答案:

答案 0 :(得分:16)

您可以使用以下方法。如果存在,它将设置属性。在每次调用时使用GetType可能会产生一些开销,需要进行优化。

    private void 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);
    }

用法

 TrySetProperty(entry.Entity, "ModifiedDate", DateTime.Now);

答案 1 :(得分:6)

你可以使用反射 - 已经被几个人提到过了 - 或者你可以创建一个界面。如果您使用自动生成的实体,则使用partial关键字定义它们,这样您就可以在同一个项目中创建另一个类文件,并为其指定相同的命名空间和类定义,并且可以实现接口。然后在上面发布的代码中,检查对象是否实现了您的界面,如果是,则转换为它,然后设置值。

界面的优点在于您不使用反射(这可能是一项昂贵的操作),而且,您创建的任何未来实体都将通过实现您的界面自动运行。

如果您的实体属性与您的界面不完全匹配,您可以显式实现该界面,该界面将处理任何命名违规行为。

示例:假设您已经定义了一个接口IContainAuditProperties,并且您有所有适用的实体都实现了该接口,您可以在一个块中执行以下操作,在该块中循环所有新的/更改的实体:

var entity = entry.Entity as IContainAuditProperties;
if(entity != null)
{
  entity.CreatedDate = DateTime.Now;
  entity.ModifiedDate = DateTime.Now;
  //etc.
}

答案 2 :(得分:1)

您可以使用以下代码检查对象是否具有具有特定名称的公共可设置属性:

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);
}

然而,这不是一个非常有效的代码,因为它使用了很多反射。

如果你走这条路,我会记住这段代码的结果,所以每次检查最多只运行一次。

我在项目中实际执行的操作(我使用的是EF Code First),有一个BaseEntity类,它具有常见的属性,并且具体的实体继承自BaseEntity类。然后我有一个FillEntityMetadata方法,或多或少是这样的:

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;
}

请注意,要使其正常工作,您还可以使用接口IBaseEntity,它将以相同的方式运行。