关于将 nested/hierarchical 个对象的字段映射到平面列表的意见?
Opinions on mapping fields of nested/hierarchical objects to flat list?
我正在编写一个工具来访问 word 文档以预先填充数据。该文档有一个 custom document properties 的子集,每个子集都由一个名称标识,其值用于更新文档中的字段。
我的 ViewModel 应该能够 initiate/update 从那些文档属性的数据中获取它的实例,以及写回它的值并更新文档的字段。
像这样:
class PersonVM : INotifyPropertyChanged
{
// properties
string Name { get; set; }
string PhoneNumber { get; set; }
// methods to get data or save data of this properties to or from the word document
void saveMyPropertyValuesToWord()
{
// …
}
void updateMyPropertiesFromWord()
{
// …
}
}
class ProjectVM : INotifyPropertyChanged
{
int ProjectNumber { get; set; }
PersonVM Manager { get; set; }
PersonVM Mechanic1 { get; set; }
PersonVM Mechanic2 { get; set; }
void saveMyPropertyValuesToWord()
{
Manager.saveMyPropertyValuesToWord();
Mechanic1.saveMyPropertyValuesToWord();
Mechanic2.saveMyPropertyValuesToWord();
// handle ProjectNumber etc.
}
void updateMyPropertiesFromWord()
{
Manager.updateMyPropertiesFromWord();
Mechanic1.updateMyPropertiesFromWord();
Mechanic2.updateMyPropertiesFromWord();
// handle ProjectNumber etc.
}
class CompanyVM : INotifyPropertyChanged
{
string Name { get; set; }
PersonVM Owner { get; set; }
ProjectVM Project1 { get; set; }
ProjectVM Project2 { get; set; }
// …
}
// …
}
现在我有一个 class 每个文档的静态字符串属性 属性 可能存在于我想从中相应地加载数据的 word 文档中:
class WordUtils
{
// Company
static string CompanyName = "dp_CompanyName";
// Company.Owner
static string CompanyOwnerName = "dp_CompanyOwnerName";
static string CompanyOwnerPhone = "dp_CompanyOwnerPhone";
// Company.Project1
static string CompanyProject1Number = "dp_CompanyProject1Number";
// Company.Project1.Manager
static string CompanyProject1ManagerName = "dp_CompanyProject1ManagerName";
static string CompanyProject1ManagerPhone = "dp_CompanyProject1ManagerPhone";
// Company.Project1.Mechanic1
// … etc
}
现在回到实施那些 PersonVM.saveMyPropertyValuesToWord()
- 我想到了这样的事情:
void saveMyPropertyValuesToWord()
{
Name = MyApp.MyWordDocument.GetCustomProperty(WordUtils.OwnerName);
}
但在这里我需要在 class 级别上确切地知道它是从哪个实例调用的(即我是什么 PersonVM,Company.Owner 或Project1.Manager 或 ?) 以决定我需要提供哪个 WordUtils.Name。
我不确定这应该如何完成,也许可以使 PersonVM 抽象并为每个角色创建一个新的 class(这将再次只有一个实例,在我看来不是很漂亮) ?我还简要了解了 Attributes 并希望这些在这种情况下可能会有所帮助。也许我遗漏了一些明显的东西,但是到目前为止,对解决这个问题的可靠方法的广泛搜索没有结果。
这样的事情怎么样:
class Property
{
public string Key { get; }
public string Value { get; set; }
public Property(string key) => Key = key;
}
interface IPropertyTree
{
IEnumerable<IPropertyTree> ChildNodes { get; }
IEnumerable<Property> Properties { get; }
}
class PersonVM : IPropertyTree
{
private readonly string prefix;
public PersonVM(string prefix)
{
Name = new Property(prefix + "Name" );
PhoneNumber = new Property(prefix + "PhoneNumber");
}
public Property Name { get; }
public Property PhoneNumber { get; }
public IEnumerable<IPropertyTree> ChildNodes => Enumerable.Empty<IPropertyTree>();
public IEnumerable<Property> Properties => new[] {Name, PhoneNumber};
}
static class PropertyTreeExtensions
{
public static void Update(this IPropertyTree self)
{
foreach (var property in self.Flatten().SelectMany(tree => tree.Properties))
{
property.Value = MyApp.MyWordDocument.GetCustomProperty(property.Key);
}
}
public static IEnumerable<IPropertyTree> Flatten(this IPropertyTree self)
{
var stack = new Stack<IPropertyTree>();
stack.Push(self);
while (stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach (var child in current.ChildNodes)
{
stack.Push(child);
}
}
}
}
这应该允许每个 属性 有一个唯一的键,并保持键和 属性 值紧密耦合。它还应该允许您将 save/update 逻辑移动到一个集中的地方。
当然,您可以为每种类型实现 IPerson
的具体 class,并对各个实现进行硬编码。
由于您在创建 PersonVMM
的实例时知道人的类型,因此您可以添加一个属性 PersonTypeId
并从构造函数中设置它,
void SomeMethod()
{
var personVm = new PersonVM(WordUtils.OwnerName);
}
class PersonVM : INotifyPropertyChanged
{
// properties
string PersonTypeId { get; set; }
string Name { get; set; }
string PhoneNumber { get; set; }
public PersonVM()
{}
public PersonVM(string personTypeId)
{
PersonTypeId = personTypeId;
}
// methods to get data or save data of this properties to or from the word document
void saveMyPropertyValuesToWord()
{
Name = MyApp.MyWordDocument.GetCustomProperty(PersonTypeId);
}
}
我正在编写一个工具来访问 word 文档以预先填充数据。该文档有一个 custom document properties 的子集,每个子集都由一个名称标识,其值用于更新文档中的字段。
我的 ViewModel 应该能够 initiate/update 从那些文档属性的数据中获取它的实例,以及写回它的值并更新文档的字段。
像这样:
class PersonVM : INotifyPropertyChanged
{
// properties
string Name { get; set; }
string PhoneNumber { get; set; }
// methods to get data or save data of this properties to or from the word document
void saveMyPropertyValuesToWord()
{
// …
}
void updateMyPropertiesFromWord()
{
// …
}
}
class ProjectVM : INotifyPropertyChanged
{
int ProjectNumber { get; set; }
PersonVM Manager { get; set; }
PersonVM Mechanic1 { get; set; }
PersonVM Mechanic2 { get; set; }
void saveMyPropertyValuesToWord()
{
Manager.saveMyPropertyValuesToWord();
Mechanic1.saveMyPropertyValuesToWord();
Mechanic2.saveMyPropertyValuesToWord();
// handle ProjectNumber etc.
}
void updateMyPropertiesFromWord()
{
Manager.updateMyPropertiesFromWord();
Mechanic1.updateMyPropertiesFromWord();
Mechanic2.updateMyPropertiesFromWord();
// handle ProjectNumber etc.
}
class CompanyVM : INotifyPropertyChanged
{
string Name { get; set; }
PersonVM Owner { get; set; }
ProjectVM Project1 { get; set; }
ProjectVM Project2 { get; set; }
// …
}
// …
}
现在我有一个 class 每个文档的静态字符串属性 属性 可能存在于我想从中相应地加载数据的 word 文档中:
class WordUtils
{
// Company
static string CompanyName = "dp_CompanyName";
// Company.Owner
static string CompanyOwnerName = "dp_CompanyOwnerName";
static string CompanyOwnerPhone = "dp_CompanyOwnerPhone";
// Company.Project1
static string CompanyProject1Number = "dp_CompanyProject1Number";
// Company.Project1.Manager
static string CompanyProject1ManagerName = "dp_CompanyProject1ManagerName";
static string CompanyProject1ManagerPhone = "dp_CompanyProject1ManagerPhone";
// Company.Project1.Mechanic1
// … etc
}
现在回到实施那些 PersonVM.saveMyPropertyValuesToWord()
- 我想到了这样的事情:
void saveMyPropertyValuesToWord()
{
Name = MyApp.MyWordDocument.GetCustomProperty(WordUtils.OwnerName);
}
但在这里我需要在 class 级别上确切地知道它是从哪个实例调用的(即我是什么 PersonVM,Company.Owner 或Project1.Manager 或 ?) 以决定我需要提供哪个 WordUtils.Name。
我不确定这应该如何完成,也许可以使 PersonVM 抽象并为每个角色创建一个新的 class(这将再次只有一个实例,在我看来不是很漂亮) ?我还简要了解了 Attributes 并希望这些在这种情况下可能会有所帮助。也许我遗漏了一些明显的东西,但是到目前为止,对解决这个问题的可靠方法的广泛搜索没有结果。
这样的事情怎么样:
class Property
{
public string Key { get; }
public string Value { get; set; }
public Property(string key) => Key = key;
}
interface IPropertyTree
{
IEnumerable<IPropertyTree> ChildNodes { get; }
IEnumerable<Property> Properties { get; }
}
class PersonVM : IPropertyTree
{
private readonly string prefix;
public PersonVM(string prefix)
{
Name = new Property(prefix + "Name" );
PhoneNumber = new Property(prefix + "PhoneNumber");
}
public Property Name { get; }
public Property PhoneNumber { get; }
public IEnumerable<IPropertyTree> ChildNodes => Enumerable.Empty<IPropertyTree>();
public IEnumerable<Property> Properties => new[] {Name, PhoneNumber};
}
static class PropertyTreeExtensions
{
public static void Update(this IPropertyTree self)
{
foreach (var property in self.Flatten().SelectMany(tree => tree.Properties))
{
property.Value = MyApp.MyWordDocument.GetCustomProperty(property.Key);
}
}
public static IEnumerable<IPropertyTree> Flatten(this IPropertyTree self)
{
var stack = new Stack<IPropertyTree>();
stack.Push(self);
while (stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach (var child in current.ChildNodes)
{
stack.Push(child);
}
}
}
}
这应该允许每个 属性 有一个唯一的键,并保持键和 属性 值紧密耦合。它还应该允许您将 save/update 逻辑移动到一个集中的地方。
当然,您可以为每种类型实现 IPerson
的具体 class,并对各个实现进行硬编码。
由于您在创建 PersonVMM
的实例时知道人的类型,因此您可以添加一个属性 PersonTypeId
并从构造函数中设置它,
void SomeMethod()
{
var personVm = new PersonVM(WordUtils.OwnerName);
}
class PersonVM : INotifyPropertyChanged
{
// properties
string PersonTypeId { get; set; }
string Name { get; set; }
string PhoneNumber { get; set; }
public PersonVM()
{}
public PersonVM(string personTypeId)
{
PersonTypeId = personTypeId;
}
// methods to get data or save data of this properties to or from the word document
void saveMyPropertyValuesToWord()
{
Name = MyApp.MyWordDocument.GetCustomProperty(PersonTypeId);
}
}