2017-03-01 115 views
1

我做了一個幫助器方法,它會自動將隨機值設置爲給定實體(類)的屬性,這樣我就不必爲每個屬性填充測試時的值。列表<PropertyInfo>除列表<PropertyInfo>不工作

就我而言,每個實體都繼承自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 
    { 
    } 
} 

的propeblem是var propertiesToPopulate = entityBaseProperties.Except(ourEntityProperties).ToList();

什麼我期待的是,只有唯一這個實體的PropertyInfo對象的列表,但是我從來都是讓我的實體的屬性。 此行不會按預期過濾列表。

任何幫助爲什麼?

+1

如果屬性應該是唯一的_derived_實體類型,不應該它是'propertiesToPopulate = ourEntityProperties.Except(entityBaseProperties)'? –

+0

@RenéVogt我也認爲這種方式應該在理論上起作用。但事實並非如此。 –

回答

1

另一種可能是在你的檢查迴路​​是否相同的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(); 

但它看起來可怕;)

+0

工作得很好。謝謝先生:D –

+0

但是我怎樣才能刪除導航屬性? –

+0

@ Ra'edAlaraj什麼是導航屬性? –

3

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

0

的PropertyInfo包含許多屬性,其中有一些獨特的對象類型的值他們提到,所以我相信你可能感興趣的只是檢查了屬性的名稱屬性:

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

我創建了一個類,你可以使用這個確切的事情

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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
     return RandomExtensions.GetRandomString(random, length, chars); 
    } 
} 
+0

@JonSkeet謝謝,我使用其中一個答案來幫助構建此類 –