C# 如何设置通用实体的属性?

C# 如何设置通用实体的属性?,c#,entity-framework,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 (newEnti

我想检查一个实体是否有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.NETMVC4

您可以使用下面的方法。它将设置属性(如果存在)。每次调用时使用
GetType
可能会导致一些开销,需要优化

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


您可以使用反射(已经有好几个人提到过),也可以创建一个接口。如果您使用的是自动生成的实体,则它们是使用
partial
关键字定义的,因此您可以在同一项目中创建另一个类文件,并为其提供相同的命名空间和类定义,并且您可以让该类文件实现您的接口。然后在上面发布的代码中,检查对象是否实现了您的接口,如果实现了,则对其进行转换,然后设置值

该接口的优点是不使用反射(这可能是一个昂贵的操作),而且,您创建的任何未来实体都将通过实现接口自动工作

在实体属性与接口不完全匹配的情况下,可以显式实现接口,该接口将处理任何命名异常

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

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

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

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代码),拥有一个具有公共属性的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
,它将以相同的方式工作。

使用反射
PropertyInfo
。也许可以用于此,但我必须说我不确定。在标题中添加标记没有意义。请阅读以了解更多信息。如果您可以访问这些实体的声明,最好是创建一个包含这些属性的接口,将该接口分配给所有也包含这些属性的实体,然后检查对象是否实现了该接口。对于这种情况,我倾向于将这些属性放在所有数据库映射实体都从中继承的基类上,以便知道它们将在那里。然后,您只需添加
where T:EntityBase
,并直接从
add()
Update()
方法访问属性。我意识到这在你的情况下是不可能的,但值得一提。非常好。我将对它进行基准测试,看看它的性能如何。这两种方法都很聪明。@Smith:实际查找类型和属性需要相当长的时间,但是如果你设法将它们缓存在静态字典中,那么速度方面的效率会提高很多。(依我看)如果你要预先加上“Try”对于您的方法名称,您应该使返回类型bool与int.TryParse等方法(以及许多其他方法)保持一致。我使用分部类实现接口没有问题,因为我已经为元数据属性提供了许多anyways。我的问题是如何在不直接引用enity的情况下施展,我同意Brian的观点。接口绝对是解决这个问题的方法。这应该是最好的答案。使用反射是一个坏主意,除非您更喜欢较慢的代码和更多的代码。更好的实践几乎总是胜过最明显的解决方案(而且肯定是使用反射)。
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;
}