我正在开发一个非常简单的框架,我希望自动化CRUD功能。出于这个问题的目的,我已经创建了下面的代码,简化只是为了说明我的问题。
所有项目都来自(抽象)基类" DbItem" (包含CRUD函数),子类提供附加功能,并定义存储DbItem的表名。例如,"设备"和"实体"两者都来自DbItem,并定义表名("设备"和"实体"分别)。然而,"实体" class是抽象的,并且进一步由" Human"和"动物"类。 (所有人类和动物都存储在共享"实体"表中,但设备存储在单独的表中。)
这部分代码有效。 DbItem中定义的Save()方法正确解析了DbTable属性。
然而,我还有一个DbCollection类,它扩展了通用的C#Collection类。我希望DbCollection能够使用反射自动确定正确的数据库表名。
因此,如果我想创建所有设备的列表,我创建一个新的DbCollection,代码组成相应的SELECT语句(SELECT ... FROM equipment ...)。这很有效。
但是,如果我想要所有实体(动物和人类)的列表,我有一个问题。 "实体"被标记为抽象,我不应该实例化它。但是,我也不知道如何获取Entity.DbTable属性的值。
一个简单的"修复"是删除"摘要"从Entity类定义中限定。然而,这对我来说听起来不对。
请告诉我如何获取Entity.DbTable属性的值?
class Program
{
abstract class DbItem
{
public int Id { get; set; }
public abstract string DbTable { get; }
public void Save()
{
Console.WriteLine($"INSERT INTO {DbTable} (Id) VALUES ({this.Id})...");
}
}
abstract class Entity : DbItem
{
public sealed override string DbTable { get => "entities"; }
}
class Human : Entity { }
class Equipment : DbItem
{
public override string DbTable => "equipment";
}
class DbCollection<T> : System.Collections.ObjectModel.Collection<T>
{
public virtual string DbTable { get
{
Type t = typeof(T);
//System.Reflection.PropertyInfo p = t.GetProperty("DbName");
if(t.IsAbstract)
{
// What do we do here to get the value of Entity.DbTable?
var prop = t.GetProperty("DbTable");
// Herein lies the problem: One cannot instantiate an abstract class to provide to the GetValue() method
return prop.GetValue(null).ToString(); // System.Reflection.TargetException: 'Non-static method requires a target.'
}
else
{
var obj = Activator.CreateInstance(t);
//return (obj as DbItem).DbTable; // this also works
var prop = t.GetProperty("DbTable");
return prop.GetValue(obj).ToString();
}
}
}
public DbCollection()
{
Console.WriteLine($"SELECT Id FROM {DbTable} WHERE ...");
}
}
static void Main(string[] args)
{
var h = new Human();
h.Save(); // 1. Correctly outputs "entities";
var e = new Equipment();
e.Save(); // 2. Correctly outputs "equipment";
var ec = new DbCollection<Equipment>(); // 3. Correctly outputs "equipment"
var hc = new DbCollection<Human>(); // 4. Correctly outputs "entities"
var entityCollection = new DbCollection<Entity>(); // 5. Error.
Console.ReadLine();
}
}
答案 0 :(得分:1)
Don't use a property, use an attribute. That's what you want, right? To associate a class with a table name that is fixed at compile time?
First, create a custom attribute class that stores a table name:
[AttributeUsage(AttributeTargets.Class | Inherited = true)]
public class DbTableAttribute: System.Attribute
{
private readonly string _name;
public string Name { get { return _name; } }
public DbTableAttribute(string name)
{
_name = name;
}
}
Add it to your human/animal/entity/DbItem classes:
[DbTable("entities")]
abstract class Entity : DbItem
{
//Don't need this any more
//public sealed override string DbTable { get => "entities"; }
}
And retrieve it like this:
public string GetDbTable<T>() where T : DbItem
{
var attr = typeof(T).GetCustomAttributes(
typeof(DbTableAttribute), true
).FirstOrDefault() as DbTableAttribute;
return attr?.Name;
}
答案 1 :(得分:0)
您希望在第五种情况下发生什么?
绝对没有办法创建abstract
类的实例。从您的Entity类中删除abstract
关键字。如果您想阻止外部创建Entity
类,可以使用internal
构造函数。
由于这是一个集合,您可能还会使用集合的第一个条目来获取DbTable
结果 - 可能很危险,因为第二个项目可能是另一种类型。