【问题标题】:List<PropertyInfo> except List<PropertyInfo> not workingList<PropertyInfo> 除了 List<PropertyInfo> 不起作用
【发布时间】:2017-03-02 00:34:58
【问题描述】:

我正在创建一个辅助方法,它会自动为给定实体(类)的属性设置随机值,这样我就不必在测试时为每个属性填充值。

在我的例子中,每个实体都继承自 BaseEntity 类,该类具有 ID、CreatedBy、CreatedOn 等...属性。基本上这个类具有所有实体共享的所有属性。

我在这里尝试完成的是将独特属性与常见属性分开。

这是我的代码:

public static TEntity PopulateProperties<TEntity>(TEntity entity)
{
    try
    {
        // Since every entity inherits from EntityBase, there is no need to populate properties that are in EntityBase class
        // because the Core project populates them.
        // First of all, we need to get all the properties of EntityBase
        // and then exlude them from the list of properties we will automatically populate

        // Get all properties of EntityBase
        EntityBase entityBase = new EntityBase();
        List<PropertyInfo> entityBaseProperties = new List<PropertyInfo>();
        foreach (var property in entityBase.GetType().GetProperties())
        {
            entityBaseProperties.Add(property);
        }

        // Get all properties of our entity
        List<PropertyInfo> ourEntityProperties = new List<PropertyInfo>();
        foreach (var property in entity.GetType().GetProperties())
        {
            ourEntityProperties.Add(property);
        }

        // Get only the properties related to our entity
        var propertiesToPopulate = ourEntityProperties.Except(entityBaseProperties).ToList();

        // Now we can loop throught the properties and set values to each property
        foreach (var property in propertiesToPopulate)
        {
            // Switch statement can't be used in this case, so we will use the if clause                  
            if (property.PropertyType == typeof(string))
            {
                property.SetValue(entity, "GeneratedString");
            }
            else if (property.PropertyType == typeof(int))
            {
                property.SetValue(entity, 1994);
            }
        }

        return entity;
    }
    finally
    {
    }
} 

问题在于var propertiesToPopulate = entityBaseProperties.Except(ourEntityProperties).ToList();

我期待的是一个仅对该实体唯一的 PropertyInfo 对象列表,但是我总是得到我的实体的所有属性。 此行未按预期过滤列表。

有什么帮助吗??

【问题讨论】:

  • 如果属性对于派生实体类型应该是唯一的,不应该是propertiesToPopulate = ourEntityProperties.Except(entityBaseProperties)吗?
  • @RenéVogt 我也认为这种方法理论上应该可行。但事实并非如此。

标签: c# entity-framework


【解决方案1】:

PropertyInfo“知道”你曾经要求过哪种类型。例如:

using System;
using System.Reflection;

class Base
{
    public int Foo { get; set; }
}

class Child : Base
{    
}

class Test
{
    static void Main()
    {
        var baseProp = typeof(Base).GetProperty("Foo");
        var childProp = typeof(Child).GetProperty("Foo");
        Console.WriteLine(baseProp.Equals(childProp));
        Console.WriteLine(baseProp.ReflectedType);
        Console.WriteLine(childProp.ReflectedType);
    }
}

有输出:

False
Base
Child

幸运的是,您可以更简单地执行此操作 - 如果您只想知道在 TEntity 中声明了哪些属性,您可以使用:

var props = typeof(entity.GetType()).GetProperties(BindingFlags.Instance | 
                                                   BindingFlags.Public | 
                                                   BindingFlags.DeclaredOnly);

如果您还需要静态属性,请进行调整。重点是BindingFlags.DeclaredOnly

【讨论】:

    【解决方案2】:

    另一种可能性是在你的 for 循环中检查 DeclaringType 是否与 entity 的类型相同:

    // Get all properties of our entity
    List<PropertyInfo> ourEntityProperties = new List<PropertyInfo>();
    foreach (var property in entity.GetType().GetProperties())
    {
        // check whether it only belongs to the child
        if (property.DeclaringType.Equals(entity.GetType())) 
        {
            ourEntityProperties.Add(property);
        }       
    }
    

    那么你只需要一个循环来过滤掉所有必要的属性。

    在“linqish”oneliner 中,您可以将其写为:

    List<PropertyInfo> ourEntityProperties = entity.GetType().GetProperties().Where(x=>x.DeclaringType.Equals(entity.GetType())).ToList();
    

    但它看起来很可怕;)

    【讨论】:

    • 运行良好。谢谢先生:D
    • 但是我怎样才能删除导航属性呢?
    • @Ra'edAlaraj 什么是导航属性?
    • 没关系,if 语句会过滤它们。导航属性是类中的属性,EF 在使用代码优先方法时用于确定表之间的关系
    【解决方案3】:

    PropertyInfo 包含许多属性,其中一些具有它们所引用的对象类型独有的值,所以我相信您可能有兴趣只检查属性的名称属性:

    //properties whose names are unique to our Entity
    var propertiesToPopulate = ourEntityProperties
        .Where(oep => !entityBaseProperties.Any(ebp => ebp.Name == oep.Name)).ToList();
    

    【讨论】:

      【解决方案4】:

      我创建了一个类,你可以用它来做这件事

      public class DTOGeneratorRule
          {
              #region Members
      
              protected Random _random;
      
              protected readonly Dictionary<Type, Func<object>> typeToRandomizerFuncMap = new Dictionary<Type, Func<object>>
              {
              };
      
              #endregion
      
              #region Constructors
      
              public DTOGeneratorRule()
              {
                  // Do Not Change this
                  // This is explicitly set to assure that values are generated the same for each test
                  _random = new Random(123); 
      
                  typeToRandomizerFuncMap.Add(typeof(int), () => _random.Next());
                  typeToRandomizerFuncMap.Add(typeof(bool), () => _random.Next() % 2 == 0);
                  // Most codes on our system have a limit of 10, this should be fixed when configuration exits
                  typeToRandomizerFuncMap.Add(typeof(Guid), () => Guid.NewGuid());
                  typeToRandomizerFuncMap.Add(typeof(string), () => _random.GetRandomAlphanumericCode(10));
                  //Most of the times we need to work with dates anyway so truncate the time
                  typeToRandomizerFuncMap.Add(typeof(DateTime), () =>  DateTime.Now.Date);
                  typeToRandomizerFuncMap.Add(typeof(Char), () =>_random.GetRandomAlphanumericCode(1)[0]);
                  typeToRandomizerFuncMap.Add(typeof(Double), () => _random.NextDouble());
                  typeToRandomizerFuncMap.Add(typeof(float), () => _random.NextFloat());
                  typeToRandomizerFuncMap.Add(typeof(Decimal), () => _random.NextDecimal());
              }
      
              #endregion
      
              #region Public Methods
      
              public T SetAutoGeneratedDTOValues<T>(IEnumerable<Action<T>> explicitValueSetters = null, Dictionary<string, Type> typeCaster = null)
                  where T : new()
              {
                  T initialDTO = new T();
                  return this.SetAutoGeneratedDTOValues<T>(initialDTO, explicitValueSetters, typeCaster);
              }
      
              public T SetAutoGeneratedDTOValues<T>(T initialDTO, IEnumerable<Action<T>> explicitValueSetters = null, Dictionary<string, Type> typeCaster = null)
              {
                  if (null == initialDTO)
                  {
                      throw new ArgumentNullException(nameof(initialDTO));
                  }
      
                  //TODO: This needs to work with Members as well
                  foreach (var property in typeof (T).GetProperties())
                  {
                      if (null == property.GetSetMethod())
                      {
                          continue;
                      }
      
                      object value = null;
      
                      Type propertyType = property.PropertyType;
                      if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                      {
                          propertyType = Nullable.GetUnderlyingType(propertyType);
                      }
      
                      var targetType = propertyType;
                      if (typeCaster != null && typeCaster.ContainsKey(property.Name))
                      {
                          targetType = typeCaster.Get(property.Name);
                      }
      
                      value = this.GetRandomValue(targetType);
                      value = this.convertToType(value, propertyType);
      
                      property.SetValue(initialDTO, value);
                  }
      
                  if (null != explicitValueSetters)
                  {
                      foreach (var setter in explicitValueSetters)
                      {
                          setter.Invoke(initialDTO);
                      }
                  }
      
                  return initialDTO;
              }
      
              #endregion
      
              #region Protected Methods
      
              protected object convertToType(object value, Type type)
              {
                  return Convert.ChangeType(value, type);
              }
      
              protected bool TryGetRandomValue(Type type, out object value)
              {
                  Func<object> getValueFunc;
                  if (type.IsEnum)
                  {
                      var values = Enum.GetValues(type);
                      int index = _random.Next(0, values.Length);
                      value = values.GetValue(index);
                      return true;
                  }
      
                  if (typeToRandomizerFuncMap.TryGetValue(type, out getValueFunc))
                  {
                      value = getValueFunc();
                      return true;
                  }
      
                  value = null;
                  return false;
              }
      
              protected object GetRandomValue(Type type)
              {
                  object value = null;
                  Func<object> getValueFunc;
                  if (type.IsEnum)
                  {
                      var values = Enum.GetValues(type);
                      int index = _random.Next(0, values.Length);
                      value = values.GetValue(index);
                  }
                  else if (typeToRandomizerFuncMap.TryGetValue(type, out getValueFunc))
                  {
                      value = getValueFunc();
                  }
                  else
                  {
                      value = this.getDefault(type);
                  }
      
                  return value;
              }
      
              protected object getDefault(Type type)
              {
                  if (type.IsValueType)
                  {
                      return Activator.CreateInstance(type);
                  }
                  return null;
              }
      
              #endregion
      
          }
      

      你还需要一些静态扩展

      public static class RandomExtensions
      {
          public static decimal NextDecimal(this Random rng)
          {
              // The max value should not be too large to avoid out of range errors when saving to database.
              return Math.Round(rng.NextDecimal(10000, 25), 2);
          }
      
          //From Another Jon Skeet: http://stackoverflow.com/a/3365374/1938988 
          public static float NextFloat(this Random rng)
          {
              // Perform arithmetic in double type to avoid overflowing
              double range = (double)float.MaxValue - (double)float.MinValue;
              double sample = rng.NextDouble();
              double scaled = (sample * range) + float.MinValue;
              return (float)scaled;
          }
      
          public static string GetRandomAlphanumericCode(this Random random, int length)
          {
            string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            return RandomExtensions.GetRandomString(random, length, chars);
          }
      }
      

      【讨论】:

      • @JonSkeet 谢谢,我用你的一个答案来帮助建立这个类
      猜你喜欢
      • 1970-01-01
      • 2012-04-01
      • 1970-01-01
      • 2013-07-25
      • 2014-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-12
      相关资源
      最近更新 更多