I'm just playing with attributes, which contains some info about a specific database field. I've already created the FieldAttribute class and it has default information, like name, type, etc.
My question is: is it possible to create a single method to retrieve this information and embbed into properties? I think I can communicate better with code:
[AttributeUsage(AttributeTargets.Property,AllowMultiple=false,Inherited=true)]
public class FieldAttribute:Attribute
{
protected string _FieldName;
protected esriFiel开发者_如何学PythondType _FieldType;
protected bool _NotNullable;
protected bool _IsPrimary;
protected bool _IsForeign;
protected int _Index;
public virtual string FieldName
{
get { return this._FieldName; }
set { this._FieldName = value; }
}
public virtual esriFieldType FieldType
{
get { return this._FieldType; }
set { this._FieldType = value; }
}
public virtual bool NotNullable
{
get { return this._NotNullable; }
set { this._NotNullable = value; }
}
public virtual int Index
{
get { return this._Index; }
set { this._Index = value; }
}
public FieldAttribute(string fieldName, esriFieldType fieldType,int position)
{
this.FieldName = fieldName;
this.FieldType = fieldType;
_IsPrimary = false;
_IsForeign = false;
Index = position;
}
}
// my main abstract class
public abstract class ObjectWrapper:IObjectWrapper
{
protected int _ObjectId;
protected IObject _UnderlyingObject;
public virtual IObject UnderlyingObject
{
get { return this._UnderlyingObject; }
}
public virtual int ObjectId
{
get { return this._ObjectId; }
}
public virtual void Store()
{
try
{
_UnderlyingObject.Store();
}
catch (COMException comEx)
{
// log com exception
}
catch (Exception ex)
{
// log
}
}
// this method retrieves field information
private FieldAttribute GetFieldAttribute(string propertyName)
{
FieldAttribute propertyAttribute = null;
try
{
PropertyInfo propInfo = this.GetType().GetProperty(propertyName);
object[] attributes = propInfo.GetCustomAttributes(typeof(FieldAttribute), true);
if (attributes.Length == 1)
propertyAttribute = (FieldAttribute)attributes[0];
}
catch (AmbiguousMatchException ambEx)
{
// log
}
catch (ArgumentException argEx)
{
// log
}
return propertyAttribute;
}
}
// my concrete class
public class Foo:ObjectWrapper
{
[Field("FOO_NAME",esriFieldType.esriFieldTypeString,1);
public string FooName { get; set; }
[Field("FOO_AGE",esriFieldType.esriFieldTypeInteger,2);
public int FooAge { get; set; }
}
What I want to do here is to build a generic GetMethod, that uses Field attributes values to fetch the data from the database/_underlyingObject and just "fill out" the getter and setter for me.E.G:
when i call Foo.FooName, the getter will check for the attributes, passing the getter name to the GetFieldAttribute method.
How can I accomplish this?
The idea is that when there is a small framework, this will become a simple "ESRI database provider" for data.
I'm really sorry that I cannot explain this properly.
Thanks for all the help.
G.
Unless you're planning on instantiating your Foo-object through some Data Repository Retrieval method, this would require dynamic compilation to inject the necessary into your getter and setters.
I would instead recommend you to look at a pattern like this
var foo = DataRepository.GetObject<Foo>(some_id);
the method signature of GetObject would be
public static T GetObject<T>(object id) where T: new()
In the GetObject method you will reflect over the type finding all its Field-attributes and mapping them to a actual database call. This is very simple and common approach and can be achieved like this
public static IEnumerable<Tuple<PropertyInfo, FieldAttribute>> GetFieldAttributes<T>()
{
var properties = typeof(T).GetProperties();
foreach (var prop in properties)
{
var attribute = prop.GetCustomAttributes(typeof(FieldAttribute), true).FirstOrDefault();
if (attribute != null)
{
yield return new Tuple<PropertyInfo, FieldAttribute>(prop, attribute);
}
}
}
Now with a list og attributes you can construct a sql-string like this
var columns = GetFieldAttributes<T>().Select(t => t.Item2.FieldName);
var sql = "SELECT "+ String.Join("," columns) +" FROM table WHERE id = "+ id;
var result = ... execute the query and return ie a datareader
With the result you can instantiate a Foo-instance, fill out its properties and return the instance
var instance = new T();
result.Read()
var dictionary = new Dictionary<string, object>();
// get the columns returned from database
for (int i = 0; i < reader.FieldCount; i++)
{
string fieldName = reader.GetName(i);
object value = reader.GetValue(i);
dictionary.Add(fieldName, value);
}
// bind the columns from database to the properties of our object
foreach (var c in columns)
{
var attr = c.Item2;
var value = dictionary[attr.FieldName];
if (value is DBNull)
{
value = null;
}
typeof(T).InvokeMember(c.Item1.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, instance, new[] { value });
}
// return the new object with all its values filled out
return instance
精彩评论