使用LINQ将子对象展平为特性
本文关键字:对象 LINQ 使用 | 更新日期: 2023-09-27 17:57:59
我有一个带有子集合的对象集合。为了使用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>
我会进入以下内容:
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
});
您将无法自动执行此操作。一旦从数据库中检索到映射,就需要在对象级别设置映射来完成此操作。您可以考虑使用AutoMapper之类的工具来配置映射,因为这至少可以让您测试是否为每个属性设置了映射。
例如(并将您的第二个Person类重命名为PersonDto):
Mapper.CreateMap<Person, PersonDto>()
.ForMember(dest => dest.Height, opt => opt.MapFrom(
src => src.Features.FirstOrDefault(f => f.FeatureName == "Height").FeatureValue);
具有相同名称的属性将自动映射。您可能需要单独处理不存在的功能——我不记得这是否由automapper自动处理。您可以通过调用来验证是否已将所有属性映射到目标对象上
Mapper.AssertConfigurationIsValid();
然后使用从数据库中映射您的结果
var results = context.People.Include(p => p.Features).ToList();
var report = Mapper.Map<IEnumerable<PersonDto>>(results);
以下是一个答案,或者根据您的观点使用代码隐藏来解决问题
我试图避免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
}
这是伪代码,可能无法编译。
自动将要素行作为列进行"枢轴旋转"仍然很有用。