Dynamic declaration in attribute property

Asked

Viewed 141 times

12

As part of a microplataforma ORM I’m developing, I’m defining a generic class that implements exclusively tight Coupling (1 record x 1 object).

public class Course : MicroEntity<Course>
{
    public string fullname { get; set; }
    public string shortname { get; set; }
    public string summary { get; set; }
    public string format { get; set; }
    [...]
}

To define the behavior of this class, I have a Attribute which contains all boot characteristics:

[MicroEntity(
    TableName = "mdl_course",
    IdentifierColumnName = "ID",
    IsReadOnly = true,
    UseDistributedCaching = true)]
public class Course : MicroEntity<Course>
{
    public string fullname { get; set; }
    public string shortname { get; set; }
    public string summary { get; set; }
    public string format { get; set; }
    [...]
}

Recently I implemented a Databasedapter mechanism to allow agnostic connection to different databases:

public abstract class BaseAdapter
{
    internal abstract void CheckDatabaseEntities<T>() where T : MicroEntity<T>;
    internal abstract void SetSqlStatements<T>() where T : MicroEntity<T>;
    internal abstract void SetConnectionString<T>() where T : MicroEntity<T>;
    internal abstract void RenderSchemaMicroEntityNames<T>() where T : MicroEntity<T>;
    internal abstract BaseDynamicParameters Parameters<T>(object obj) where T : MicroEntity<T>;
    internal abstract DbConnection Connection(string connectionString);
}

From there, I declare Adapters for different banks. At the moment, I have adapters for Oracle and Mysql.

Question

I wish I could declare the adapter as a property of the Attribute:

[MicroEntity(
    TableName = "mdl_course",
    IdentifierColumnName = "ID",
    IsReadOnly = true,
    Adapter = new InternalAdapters.MySql.Adapter();
    UseDistributedCaching = true)]
public class Course : MicroEntity<Course>
{
    [...]

But the use of new() is not allowed. Which model would best suit this type of behavior?

  • I think it depends a little on how you’re going to use the attribute. I need to focus more on the problem. Anyway, I can’t wait until you can publish this whole thing. It looks like it’s gonna get really interesting.

  • @bigown would only be during the constructor (once initialized it would not be possible to change the Adapter). I also intend to use this engine for the distributed caching engine - by default Redis, but any other Key/Value Storage could be used. However this would be a global Setting. Imagine the zone of 2 or more cache used at the same time.

  • I don’t know if it would suit your purpose but I guess I’d have to use one enum or another way to identify and have some method that treats this to create the instance when needed. This has some difficulties but has to automate so you don’t have to keep changing the code whenever you have a new adapter. It is not a simple and perfect solution, but it meets some needs. I need to think of others.

  • @bigown To tell the truth, at the moment I’m using an Enum - internally I have a switch, and instant dynamism the type needed. But my doubt is exactly this, using new adapters without having to update the construction method.

  • One of the solutions I think is to write down these types and with reflection "discover" all existing. Another way is to have a way to "register" the adapters for this method to know where to look. Both require the adapter to be written in a way that informs it exists. Obviously the enum does not take care of this. http://answall.com/q/21997/101

  • @bigown I have an interface that serves as contract for Addons, and any new Adapter need to implement this interface. I dynamically load all assemblies when the application is initialized, and so can identify new Adapters. I made an XGH implementation that checks a literal string against an interface property. Ugly to give do, I know, but 'works'.

  • I think you’re on the right track. It’s still a form of note. I’ll think about if I find something better.

  • @Bigown I appreciate any help!

Show 3 more comments

2 answers

6

I’ve seen this problem solved using a Type (I can’t remember now where, but I’m sure I’ve seen it more than once).

public class MicroEntityAttribute : Attribute
{
    public Type Adapter { get; set; }
}

[MicroEntity(Adapter = typeof(InternalAdapters.MySql.Adapter))]
public class SomeClass {}

The expression new Class() is not a constant, but typeof(Class) is - and therefore can be used to initialize an attribute.

The attribute interpreter should then initialize an Adapter instance using Activator, and making an exception if you don’t have a public constructor without parameters.

  • 1

    Excellent solution, arrived just seconds after @Cigano. Anyway, +1 - and thank you!

5


The problem is that attributes do not accept dynamic object initialization. There are two alternatives:

1. Mark attribute with adapter type

[MicroEntity(
    TableName = "mdl_course",
    IdentifierColumnName = "ID",
    IsReadOnly = true,
    Adapter = typeof(InternalAdapters.MySql.Adapter),
    UseDistributedCaching = true)]

2. Mark attribute with an enumeration

[MicroEntity(
    TableName = "mdl_course",
    IdentifierColumnName = "ID",
    IsReadOnly = true,
    Adapter = Adapter.MySql,
    UseDistributedCaching = true)]

I am in favor of the first, especially since you have virtually no limits to inject new adapters into the application when this is desired.

When reading the attribute, the initialization would be quite simple:

var course = new Course();
var atributoMicroEntity = course.GetType().GetAttribute<MicroEntityAttribute>();
if (atributoMicroEntity != null) 
{
    var adaptador = Activator.CreateInstance(atributoMicroEntity.Adapter);
}
  • 1

    This... is... perfect! It never occurred to me to store the type, and activate dynamically. I just implemented; working perfectly.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.