我有一个带有子集合的对象集合。 我希望将其展平以用于LINQ的报告目的。
我不知道这是否可能。
例如,人物对象列表,每个人物对象都有一个子列表。
伪代码:
public sealed class Person
{
public Name { get; set;}
IEnumerable<Feature> Features
}
public sealed class Feature
{
public FeatureName { get; set;}
public FeatureValue { get; set;}
}
数据:
John
Height 183
Sex Male
Jane
Height 160
Sex Female
Additional Test
必需的输出:
Name Height Sex Additional
John 183 Male
Jane 160 Female Test
实际上我想绑定到:
class Person
{
public Name { get; set;}
public Height { get; set;}
public Sex { get; set;}
public Additional { get; set;}
}
编辑:使用带有Activator.CreateInstance的动态类型,如下所示,调用一个采用基本Person类型的构造函数:
_results = from person in _people select Activator.CreateInstance(_personWithFeaturesType, person);
创建以下答案:
System.Linq.Enumerable.WhereSelectEnumerableIterator<Person,object>
调试时扩展结果视图是我创建并存储在_personWithFeaturesType成员变量中的新类型的列表。
我不知道返回了什么,是一个用对象键入的Person对象列表? 第三方网格中的WPF绑定似乎无法处理:
System.Linq.Enumerable.WhereSelectEnumerableIterator<Person,object>
但确实处理:
IEnumerable<Person>
答案 0 :(得分:1)
我会进入类似的事情:
List<Person> persons = new List<Person>();
persons.Add(new Person()
{
Name = "John",
Features = new List<Feature>()
{
new Feature() { FeatureName = "Height", FeatureValue = "183" },
new Feature() { FeatureName = "Sex", FeatureValue = "Male" }
}
});
persons.Select(p => new MappedPerson()
{
Name = p.Name,
Height = p.Features.Where(f => f.FeatureName == "Height").DefaultIfEmpty(Feature.NullFeature).First().FeatureValue,
Sex = p.Features.Where(f => f.FeatureName == "Sex").DefaultIfEmpty(Feature.NullFeature).First().FeatureValue
});
答案 1 :(得分:1)
您无法自动执行此操作。一旦从数据库中检索到,您就需要设置映射以在对象级别执行此操作。您可以考虑使用类似AutoMapper的内容来配置映射,因为这至少可以让您测试是否为每个属性设置了映射。
例如(并重命名第二个Person类PersonDto):
Mapper.CreateMap<Person, PersonDto>()
.ForMember(dest => dest.Height, opt => opt.MapFrom(
src => src.Features.FirstOrDefault(f => f.FeatureName == "Height").FeatureValue);
将自动映射具有相同名称的属性。您可能需要处理不单独存在的功能 - 我不记得是否自动处理了这个功能。您可以通过调用以下方法验证是否已将所有属性映射到目标对象上:
Mapper.AssertConfigurationIsValid();
然后使用以下方法从数据库映射结果:
var results = context.People.Include(p => p.Features).ToList();
var report = Mapper.Map<IEnumerable<PersonDto>>(results);
答案 2 :(得分:0)
以下是根据您的观点使用代码的答案或解决方法 我试图通常为MVVM避免代码,但在这种情况下使用大量可怕的代码似乎没什么意义。
该列在第三方网格的_Loaded事件中动态添加:
DataGrid.Column featureFromList = new DataGrid.Column();
featureFromList.DisplayMemberBindingInfo = new DataGrid.DataGridBindingInfo();
featureFromList.DisplayMemberBindingInfo.Path = new PropertyPath("Features", null);
featureFromList.DisplayMemberBindingInfo.Path.Path = "Features";
featureFromList.DisplayMemberBindingInfo.ReadOnly = true;
featureFromList.DisplayMemberBindingInfo.Converter = new Converters.FlattenedPersonConverter();
featureFromList.DisplayMemberBindingInfo.ConverterParameter = "Height"; //hard coded, but would be read from database/other objects
转换器获取值列表并使用ConverterParameter作为关键字获取相关值:
[ValueConversion(typeof(object), typeof(object))]
class FlattenedPersonConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (value == null || ((IEnumerable<Feature>)value).Count == 0)
{
return null;
}
else
{
return ((Feature)((IEnumerable<Feature>)value)[parameter.ToString()]).FeatureValue;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
这是伪造的,可能无法编译。
对于&#34; pivot&#34;它仍然有用。功能行自动作为列。