【问题标题】:Generic method is picking up type of base class通用方法正在获取基类的类型
【发布时间】:2010-10-08 10:33:40
【问题描述】:

我有以下类(修剪后只显示基本结构):

public abstract class BaseModel {
    public bool PersistChanges() {
        // Context is of type "ObjectContext"
        DatabaseHelper.Context.SafelyPersistChanges(this);
    }
}

public static class ObjectContextExtensions {
    public static bool SafelyPersistChanges<T>(this ObjectContext oc, T obj) {
        // Persist the object using a transaction
    }
}

[Persistent("LEADS")]
public class Lead : BaseModel {
    // Extra properties
}

public class LeadsController : Controller {
    public ActionResult Save(Lead lead) {
        lead.PersistChanges()
    }
}

我的 Lead 类派生自 BaseModel,其中包含使用事务将对象的更改持久保存到数据库的方法。我使用扩展方法实现了事务持久化。问题是通过在我的 BaseModel 类中将 this 传递给 SafelyPersistChanges,扩展方法上的通用 T 设置为 BaseModel。但是,由于 BaseModel 没有被标记为持久对象(它不可能),ORM 框架会引发异常。

例子:

Lead lead = LeadRepository.FindByNumber(2);
lead.SalesmanNumber = 4;
// Calls "ObjectContextExtensions.SafelyPersistChanges<BaseModel>(BaseModel obj)"
// instead of "ObjectContextExtensions.SafelyPersistChanges<Lead>(Lead obj)"
lead.PersistChanges();

上述代码块引发以下异常:

无法为没有持久属性的“SalesWeb.Data.BaseModel”类型创建映射。

有什么想法吗?

【问题讨论】:

    标签: c# generics inheritance


    【解决方案1】:

    您可以使用curiously recurring template pattern 解决此问题:

    // change your code to this
    
    public abstract class BaseModel<TDerived> where TDerived : BaseModel
    {
        public bool PersistChanges() {
            // Context is of type "ObjectContext"
            DatabaseHelper.Context.SafelyPersistChanges((TDerived)this);
            //                                            ^
            // note the cast here: -----------------------|
        }
    }
    
    public class Lead : BaseModel<Lead> { }
    
    // the rest of your code is unchanged
    

    这可行,但我可能只是遵循其他建议并使用虚拟方法。

    【讨论】:

      【解决方案2】:

      因此,您需要一个“单一”实现,该实现因调用者已知的类型而异。听起来像是泛型的工作。

      public static bool PersistChanges<T>(this T source)
        where T : BaseModel
      {
              // Context is of type "ObjectContext"
      //static property which holds a Context instance is dangerous.
              DatabaseHelper.Context.SafelyPersistChanges<T>(source);
      }
      

      【讨论】:

        【解决方案3】:

        扩展方法在编译时被静态绑定。在调用 SafelyPersistChanges 时,它的类型为 BaseModel,因此您的异常。为了获得您想要的行为,您要么需要执行带有大量强制转换的丑陋 if 语句,要么强制调用派生类。

        使 PersistChanges 成为一个抽象方法。然后用完全相同的代码在派生类中实现调用。

        public class Lead { 
            public override bool PersistChanges() {
                // Context is of type "ObjectContext"
                DatabaseHelper.Context.SafelyPersistChanges(this);
            }
        }
        

        现在这将是 Lead

        【讨论】:

        • 谢谢!它会造成代码重复,但我想这只是我必须做出的牺牲。从好的方面来说,它让模型有机会在持久化之前执行其他操作。
        【解决方案4】:

        我会以不同的方式设计这个,让“public bool PersistChanges()”调用一个虚拟方法,该方法在每个子类中被覆盖。

        【讨论】:

          猜你喜欢
          • 2011-03-05
          • 1970-01-01
          • 2011-01-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多