Универсальный метод подбирает тип базового класса - PullRequest
1 голос
/ 18 февраля 2009

У меня есть следующие классы (обрезанные, чтобы показать только базовую структуру):

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()
    }
}
Класс

My Lead является производным от BaseModel , который содержит метод для сохранения изменений объекта в базе данных с помощью транзакции. Я реализовал транзакционный персистент с помощью метода расширения. Проблема в том, что, передав this в SafelyPersistChanges в моем BaseModel классе, универсальный 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' без постоянного атрибута.

Есть идеи?

Ответы [ 4 ]

3 голосов
/ 18 февраля 2009

Методы расширения статически связаны во время компиляции. В момент, когда вызывается SafelyPersistChanges, это типизируется как BaseModel и, следовательно, ваше исключение Чтобы получить желаемое поведение, вам нужно либо сделать некрасивый оператор if с большим количеством приведения, либо принудительно вызвать производный класс.

Сделать PersistChanges абстрактным методом. Затем реализуйте вызов в производных классах с точно таким же кодом.

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

Теперь это будет правильно Lead

1 голос
/ 18 февраля 2009

Я бы разработал это по-другому, заставив public bool PersistChanges () вызвать виртуальный метод, который переопределяется в каждом подклассе.

0 голосов
/ 19 февраля 2009

Вы можете решить эту проблему, используя любопытно повторяющийся шаблон :

// 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

Это бы сработало, но я, вероятно, просто следую другим советам и использую виртуальный метод.

0 голосов
/ 18 февраля 2009

Итак, вам нужна «одиночная» реализация, которая варьируется в зависимости от типа, известного вызывающей стороне. Звучит как работа для дженериков.

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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...