我正在制作一个辅助方法,它会自动将随机值设置为给定实体(类)的属性,这样我就不必在测试时用值填充每个属性。
在我的例子中,每个实体都继承自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对象的列表,这些对象仅对此实体是唯一的,但是我总是获得我的实体的所有属性。 此行不会按预期过滤列表。
任何帮助为什么??
答案 0 :(得分: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
。
答案 1 :(得分:1)
另一种可能性是检查你的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);
}
}
然后你只需要一个循环来过滤掉所有必要的属性。
在&#34; linqish&#34; oneliner你可以把它写成:
List<PropertyInfo> ourEntityProperties = entity.GetType().GetProperties().Where(x=>x.DeclaringType.Equals(entity.GetType())).ToList();
但它看起来很可怕;)
答案 2 :(得分:0)
PropertyInfo 包含许多属性,其中一些属性对于它们引用的对象类型具有唯一值,因此我相信您可能只想检查属性的名称属性:
//properties whose names are unique to our Entity
var propertiesToPopulate = ourEntityProperties
.Where(oep => !entityBaseProperties.Any(ebp => ebp.Name == oep.Name)).ToList();
答案 3 :(得分: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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return RandomExtensions.GetRandomString(random, length, chars);
}
}