【问题标题】:how to convert the <T> to its base type for generic method?如何将 <T> 转换为其泛型方法的基本类型?
【发布时间】:2010-08-30 03:32:57
【问题描述】:

我有一个通用的 CRUD 类来对我的实体对象执行添加、删除、选择和创建。

其中之一 - Message 有两个派生类 - order_message 和 report_message。

我的问题是,在我的泛型类中,我需要一个对象集来执行 crud ops,但对象集不接受派生类类型,它只接受基类类型。

这是我收到的错误:

没有为指定的实体类型“CustomerWebPortal_Entities.Order_Message”定义实体集。如果“CustomerWebPortal_Entities.Order_Message”是派生类型,请改用基类型。

我尝试使用 typeof(T).BaseType 来替换 T,结果没有用。

我应该如何纠正这个问题?

这是泛型类的概述:

 public abstract class baseCrudDao<T> : ICrudDao<T> where T : class
{
    private System.Data.Objects.ObjectContext _context;

    private System.Data.Objects.ObjectSet<T> _entity; 
    public baseCrudDao()
    {

        _context = new CustomerWebPortalEntities();
        _entity = _context.CreateObjectSet<T>(); <-- error at here, only accept base type

    }

【问题讨论】:

  • 您提出的任一解决方案都取得了成功吗?
  • 尚未尝试。希望很快。

标签: c# entity-framework type-conversion


【解决方案1】:

好吧,我终于有机会按照建议编写原型。我认为这样的事情会起作用,但我还没有测试过。现在可以针对 IObjectSet&lt;&gt; 成员定义所有的 crud 方法。

public class Crud<EntityType> where EntityType : class
{
    private readonly ObjectContext Context;
    private readonly IObjectSet<EntityType> Entities;

    public Crud(ObjectContext context)
    {
        Context = context;

        Type BaseType = GetBaseEntityType();

        if (BaseType == typeof(EntityType))
        {
            Entities = Context.CreateObjectSet<EntityType>();
        }
        else
        {
            Entities = (IObjectSet<EntityType>)Activator.CreateInstance(typeof(ObjectSetProxy<,>).MakeGenericType(typeof(EntityType), BaseType), Context);
        }
    }

    private static Type GetBaseEntityType()
    {
        //naive implementation that assumes the first class in the hierarchy derived from object is the "base" type used by EF
        Type t = typeof(EntityType);

        while (t.BaseType != typeof(Object))
        {
            t = t.BaseType;
        }

        return t;
    }
}

internal class ObjectSetProxy<EntityType, BaseEntityType> : IObjectSet<EntityType>
    where EntityType : BaseEntityType
    where BaseEntityType : class
{
    private readonly IObjectSet<BaseEntityType> Entities;

    public ObjectSetProxy(ObjectContext context)
    {
        Entities = context.CreateObjectSet<BaseEntityType>();
    }

    public void AddObject(EntityType entity)
    {
        Entities.AddObject(entity);
    }

    //TODO: implement remaining proxy methods

    public IEnumerator<EntityType> GetEnumerator()
    {
        return Entities.OfType<EntityType>().GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public Type ElementType
    {
        get { return typeof(EntityType); }
    }

    public Expression Expression
    {
        get { return Entities.OfType<EntityType>().Expression; }
    }

    public IQueryProvider Provider
    {
        get { return Entities.Provider; }
    }
}

【讨论】:

  • +1 似乎是最好的解决方案。虽然我的版本只创建 ObjectSet 实例。否则,您必须使用反射调用 CreateObjectContext 方法。
【解决方案2】:

一种解决方法是使用两个泛型类型参数(在基类上),例如

public abstract class baseCrudDao<T, U> : ICrudDao<T> where U : class, T: U
{
    ...
    public baseCrudDao()
    {
        _context = new CustomerWebPortalEntities();
        _entity = _context.CreateObjectSet<U>(); <-- error at here, only accept base type
    }
    ...
}

使用 Message 代替 U。如果您有许多类型并且并非所有类型都具有继承关系,那么约束 T:U 将不起作用,您可能需要使用 T:class 约束。我还建议您创建另一个骨架基类以用于其他类型,例如

public abstract class base2CrudDao<T> : baseCrudDao<T, T>

这样你就不必为没有继承关系的类指定U参数。

【讨论】:

  • 这种方法的一大缺点是您现在依赖 crud 类的消费者来了解使用哪个基类以及何时使用。 EF 需要此决定的事实是提供程序特定的行为,crud 类的作者可能首先希望隐藏它。
  • 我同意。我已经想到了替代解决方案,但它有点难看 - 首先用两个通用参数定义类,如上所示。然后使用一个泛型参数创建一个包装类 - 该类将使用反射和通过将 T 的基类型作为 U 类型参数传递来创建第一个泛型类的实例。此调用的所有方法都将转发到我们创建的实例。
  • 是否可以在baseCrudDao中将T转换为T的基类类型,如果检测到T不是基类? C#中是否存在这样的代码?
  • @Bryan,我的意思是编写一个包装类 A,它将使用反射创建类 B 的实例(参见msdn.microsoft.com/en-us/library/…),即 Activator.CreateInstance( typeof(A).MakeGenericType(typeof(T), typeof(T).BaseType))。现在 B 的方法将 fwd 调用上述实例。不幸的是,它不起作用,因为您在这里有抽象类。
  • 另一种方法是为派生类定义一个实现IObjectSet&lt;T&gt; 接口的类,该派生类包装了CreateObjectSet 返回的实例。然后您可以使用反射来确定您是否可以使用CreateObjectSet 返回的实例,或者您是否需要使用新类来包装它。这个新类将负责调用内部对象集(基类型)的方法,并在适当的情况下向下转换为派生类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 2018-12-01
  • 1970-01-01
相关资源
最近更新 更多