【问题标题】:Reflection, properties and attributes - Studying ORM architecture反射、属性和属性——学习ORM架构
【发布时间】:2011-06-08 18:00:51
【问题描述】:

我只是在玩属性,其中包含有关特定数据库字段的一些信息。我已经创建了 FieldAttribute 类,它具有默认信息,例如名称、类型等。

我的问题是:是否可以创建一个方法来检索这些信息并嵌入到属性中?我想我可以用代码更好地沟通:

[AttributeUsage(AttributeTargets.Property,AllowMultiple=false,Inherited=true)]
public class FieldAttribute:Attribute
{
    protected string _FieldName;
    protected esriFieldType _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; }
}

我在这里要做的是构建一个通用的 GetMethod,它使用字段属性值从数据库/_underlyingObject 中获取数据,并为我“填写”getter 和 setter。E.G:

当我调用 Foo.FooName 时,getter 将检查属性,将 getter 名称传递给 GetFieldAttribute 方法。

我怎样才能做到这一点?

这个想法是,当有一个小框架时,这将成为一个简单的数据“ESRI 数据库提供者”。

很抱歉,我无法正确解释这一点。

感谢大家的帮助。

G.

【问题讨论】:

  • 觉得有必要说一下:希望这是出于自己的好奇心,不要用于生产系统。
  • @qstarin:为什么不应该在生产中使用?这个想法有那么糟糕吗?我并不是说这将立即用于生产,但所有项目都必须从某个地方开始。谢谢你的帮助。
  • 学习和使用现有的 ORM 解决方案比开发自己的解决方案要更有效率。如果是为了你自己的学习,那是有价值的。如果这是有偿工作,这似乎是疏忽和不负责任的(这有点强,但也不远了)。编写自己的 ORM 的想法是一种反模式。

标签: c# orm properties custom-attributes


【解决方案1】:

除非您计划通过某些数据存储库检索方法来实例化您的 Foo 对象,否则这将需要动态编译以将必要的内容注入您的 getter 和 setter。

我建议你看看这样的模式

var foo = DataRepository.GetObject<Foo>(some_id);

GetObject 的方法签名是

public static T GetObject<T>(object id) where T: new()

在 GetObject 方法中,您将反映类型,查找其所有字段属性并将它们映射到实际的数据库调用。这是非常简单常用的方法,可以这样实现

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);
        }
    }
}

现在有了一个 og 属性列表,您可以像这样构造一个 sql 字符串

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

根据结果,您可以实例化一个 Foo 实例,填写其属性并返回实例

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

【讨论】: