【问题标题】:Use custom property attribute to map value to another type使用自定义属性属性将值映射到另一种类型
【发布时间】:2015-06-03 00:23:28
【问题描述】:

我正在尝试将我的 MVVM 中的各种模型将派生的抽象类放在一起。其中一部分是用于抽象 IEditableObject/IChangeTracking 细节。

对于我的 IEditableObject,我想存储“核心数据”(一种定义将被序列化的数据的结构,通常用于数据库存储)的浅表副本,以便可以取消或提交它。我不想为我想出的每个新模型输入这个。

我已经定义了一个 [DataCoreItem] 自定义属性,我认为它可以用于派生类的适用属性。由于一些不相关的原因,抽象类采用通用 DataCoreType 和 IDType:

    public abstract class ModelObject<T, DataCoreType, IDType> : INotifyPropertyChanged, IEditableObject
{

    public abstract DataCoreType Load(IDType id);
    public abstract bool Save(DataCoreType dataCore);
    public abstract bool Delete(IDType id);
    // etc...

这是我的 CompanyModel 的示例 数据核心:

public struct CompanyDataCore
{
    public int? ID;
    public string Code;
    public string Name;
    public string PrimaryWebsite;
    public string PrimaryPhone;
    public string PrimaryEmail;
}

派生类:

public class CompanyModel : ModelObject<CompanyModel, CompanyDataCore, int> {

    CompanyDataCore dataCore;

    [DataCoreMember]
    public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }
    [DataCoreMember]
    public string Name { get { return dataCore.Name; } set {SetProperty(ref dataCore.Name, value); } }
    [DataCoreMember]
    public string Code { get { return dataCore.Code; } set { SetProperty(ref dataCore.Code, value); } }
    [DataCoreMember]
    public string PrimaryPhone { get { return dataCore.PrimaryPhone; } set {SetProperty(ref dataCore.PrimaryPhone, value); } }
    [DataCoreMember] 
    public string PrimaryEmail { get { return dataCore.PrimaryEmail; } set { SetProperty(ref dataCore.PrimaryEmail, value); } }
    [DataCoreMember]
    public string PrimaryWebsite { get { return dataCore.PrimaryWebsite; } set { SetProperty(ref dataCore.PrimaryWebsite, value); } }

好的,最后...我希望我的抽象类使用 BeginEdit()、EndEdit() 和 CancelEdit() 方法来自动处理数据核心备份副本的存储。这是我的设想:

[DataCoreMember(MemberName="ID")]
public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }
// etc etc

在我的抽象类中:

    public virtual void BeginEdit() {

        Type t = typeof(T);

        var props = t.GetProperties().Where(
            prop => Attribute.IsDefined(prop, typeof(DataCoreMemberAttribute)));


        // WHAT TO DO HERE???  everything else looks good up to here
        foreach (object o in props) {
            this.dataCoreBackup.???? = o.value;
        }

        IsEditing = true;
    }

如何将 DataCoreMember 应用到的属性映射到指定的结构属性?

我对反射没有经验(也没有使用泛型类型),但我认为这是可以做到的。我找到了一些示例(尚未尝试过),说明如何获取这些属性的列表以及应用于它们的属性,但我不确定如何最终引用 DataCore 的属性。谁能告诉我怎么做?非常感谢。

【问题讨论】:

  • 后续问题:据我了解,这些属性是在运行时处理的,开发人员无法真正控制它们的可用性时间。在尝试使用此元信息来提供功能时,我是否采取了错误的方法?

标签: c# reflection mapping custom-attributes


【解决方案1】:

事实证明,使用反射是一项相当容易的任务。下面的解释(去掉了很多不相关的代码)

这保存了数据备份,因此支持 CancelEdit:

public struct CompanyDataCore
{
    public int? ID;
    public string Code;
    public string Name;
    public string PrimaryWebsite;
    public string PrimaryPhone;
    public string PrimaryEmail;
    public string RootPath;
}

这是用于表示哪些字段得到备份的属性类:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DataCoreMemberAttribute : Attribute
{
    public string MemberName { get; set; }
}

这是派生类:

public class CompanyModel : ModelObject<CompanyModel, CompanyDataCore, int>
{

    [Identifier]
    [DataCoreMember(MemberName="ID")]
    public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }

    [DataCoreMember(MemberName="Name")]
    public string Name { get { return dataCore.Name; } set {SetProperty(ref dataCore.Name, value); } }

    [DataCoreMember(MemberName="Code")]
    public string Code { get { return dataCore.Code; } set { SetProperty(ref dataCore.Code, value); } }

    [DataCoreMember(MemberName="PrimaryPhone")]
    public string PrimaryPhone { get { return dataCore.PrimaryPhone; } set {SetProperty(ref dataCore.PrimaryPhone, value); } }

    [DataCoreMember(MemberName="PrimaryEmail")] 
    public string PrimaryEmail { get { return dataCore.PrimaryEmail; } set { SetProperty(ref dataCore.PrimaryEmail, value); } }

    [DataCoreMember(MemberName="PrimaryWebsite")]
    public string PrimaryWebsite { get { return dataCore.PrimaryWebsite; } set { SetProperty(ref dataCore.PrimaryWebsite, value); } }

}

这是抽象类:

public abstract class ModelObject<T, DataCoreType, IDType> : INotifyPropertyChanged, IEditableObject
{
    protected DataCoreType dataCoreBackup;

    public virtual void BeginEdit() {

        Type t = typeof(T);

        // get a the properties with the attribute
        var props = t.GetProperties().Where(
            prop => Attribute.IsDefined(prop, typeof(DataCoreMemberAttribute)));

        // backup needs to be boxed because it's a struct
        object boxedBackup = this.dataCoreBackup;

        foreach (var prop in props) {

            foreach (CustomAttributeData attribData in prop.GetCustomAttributesData()) {

                if (attribData.Constructor.DeclaringType == typeof(DataCoreMemberAttribute)) {

                    object origValue = prop.GetValue(this);
                    FieldInfo field = boxedBackup.GetType().GetField(attribData.NamedArguments[0].TypedValue.Value.ToString());
                    field.SetValue(boxedBackup, origValue);
                }
            }
        }

        this.dataCoreBackup = (DataCoreType)boxedBackup;

        IsEditing = true;
    }

...现在我可以在一个抽象类中处理 INotifiyPropertyChanged 和 IEditbaleObject,因此我不必在我将要使用的每个特定模型中编写大量管道。

希望其他人能发现这很有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 2015-10-27
    • 1970-01-01
    • 2018-09-15
    • 2012-03-05
    相关资源
    最近更新 更多