Question

As a part of my API, I have an abstract BaseEntity class. I would like to provide a way to somehow hook into new properties that are introduced in the descendants of this class. What I was thinking is this:

public class MyEntity : BaseEntity
{
    [DataElement]
    public int NewProperty { get; set; }
}

If I could write an attribute DataElement that would hook in the getter and setter, then my API would be made aware of this property on access.

Is this possible?

UPD: I'll try to explain where this comes from. I have this BaseEntity that does not have any data by itself. Its descendants will declare what data they may hold as properties. I want to be able to iterate through all the data the object has (to store it in the database in a very specific form). One way to do it is reflection. But I thought about doing it via attributes that would register the data whenever the property is accessed.

Was it helpful?

Solution

Of course, it would look similar to the following:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class DataElementAttribute : Attribute 
{ }

You will have to enumerate the properties with reflection to see if the property contains the attribute. (which begs the question, do you really need the attribute).

        Assembly assembly = Assembly.GetExecutingAssembly();
        foreach (Type type in assembly.GetTypes())
        {
            IList<PropertyInfo> properties = type.GetProperties();
            foreach (PropertyInfo pi in properties)
            {
                if (type.IsDefined(typeof(DataElementAttribute), false))
                {
                    // Perform Logic
                }
            }
        }

However injecting Logic into the setter is a different task which would need to be done post compilation: Why is post-compilation code injection a better idea than pre-compilation code injection?

Injecting MSIL is not easy. Take a peek at this example code: http://www.codeproject.com/Articles/37549/CLR-Injection-Runtime-Method-Replacer

OTHER TIPS

You could force inheritors to specify how the object should be saved/loaded by adding a couple of abstract methods.

Example, inspired by the ISerializable interface:

public abstract class BaseEntity
{
    public void SaveToDatabase()
    {
        var objectData = new Dictionary<string, object>();
        this.GetObjectData(objectData);
        DatabaseManager.Save(objectData);
    }

    public void LoadFromDatabase(Dictionary<string, object> data)
    {
        this.SetObjectData(data);
    }

    protected abstract void GetObjectData(Dictionary<string, object> data);

    protected abstract void SetObjectData(Dictionary<string, object> data);
}

public class MyEntity : BaseEntity
{
    public int NewProperty { get; set; }

    protected override void GetObjectData(Dictionary<string, object> data)
    {
        data.Add("NewProperty", this.NewProperty);
    }

    protected override void SetObjectData(Dictionary<string, object> data)
    {
        this.NewProperty = (int)data["NewProperty"];
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top