Частичные конструкторы классов и регистрация обработчиков событий
Мне нужно зарегистрировать обработчик событий для класса, который генерируется шаблоном - шаблоном T4 в EntityFramework.
В настоящее время мы отредактировали сгенерированный код, чтобы зарегистрировать обработчик в конструкторе сгенерированного класса (контекст модели).
Текущий код:
public MyAppContext(string connectionString)
: base(connectionString, ContainerName)
{
this.ContextOptions.LazyLoadingEnabled = true;
// Register the event handler
this.Connection.StateChange += Connection_StateChange;
}
Проблема в том, что если код когда-либо будет заново сгенерирован в будущем, то приведенный выше код будет засорен, и обработчик событий больше не будет подключен...
Повторная генерация кода происходит автоматически из самых маленьких вещей, таких как открытие EF Designer и перемещение таблицы на холсте! Поэтому мы НЕ ДОЛЖНЫ оставлять свой код в сгенерированном классе.
Есть ли в любом случае мы можем поместить регистрацию в частичный класс и оставить сгенерированный код нетронутым???
Например, есть ли какое-то событие, которое всегда будет вызываться при вызове конструктора?
3 ответа
Ответом на эту проблему является редактирование шаблона T4 для добавления вызова метода в конце конструктора.
Этот метод в контексте частичных классов, генерируемых шаблоном, должен быть частичным методом.
Шаблон должен содержать определение для частичного метода.
Тогда ваш пользовательский частичный класс может реализовать этот метод, и он будет вызываться Конструкторами, определенными в сгенерированном частичном классе - теперь вы можете регенерировать этот частичный класс столько раз, сколько захотите, и быть уверенным, что частичный метод всегда будет вызываться - при условии никто не редактирует шаблон.
Если кто-то отредактирует шаблон и удалит определение частичного метода, вы получите ошибку компилятора - ее легко исправить.
Если кто-то редактирует шаблон и удаляет вызов частичного метода из Конструктора, то, к сожалению, компилятор не может вам помочь - что-то, о чем нужно знать!
Вот мое решение в tid-битах:
Фрагмент конструктора и частичное определение метода в коде шаблона T4 "MyApp.Context.tt" (здесь вы найдете подробное объяснение синтаксиса T4 и его использования в EntityFramework):
public <#=code.Escape(container)#>(string connectionString)
: base(connectionString, ContainerName)
{
<#
WriteLazyLoadingEnabled(container);
#>
// Call the OnContextCreated() method to perform any necessary 'post creation' setup
OnContextCreated();
}
// Define the OnContextCreated partial method so that the accompanying partial Context
// class can implement this method.
partial void OnContextCreated();
Пользовательский частичный класс, который реализует частичный метод и подключает обработчик событий:
public partial class MyAppContext
{
/// <summary>
/// Performs all 'post creation' operations for the MyAppContext
///
/// *********************************
/// NOTE: If you get a compiler error:
/// 'No defining declaration found for implementing declaration of partial method 'OnContextCreated()'
/// then it is likely that the partial class MyApp.Context.cs does not contain a corresponding
/// definition for the partial method OnContextCreated().
/// This can occur if the MyApp.Context.tt template no longer generates the definition.
/// SOLUTION: Edit the MyApp.Context.tt T4 template to ensure that that partial method is defined AND
/// that it is called from EACH MyAppContext() constructor.
/// *********************************
///
/// </summary>
partial void OnContextCreated()
{
// Register the event handler
this.Connection.StateChange += Connection_StateChange;
}
}
Вы говорите об EF? Для этого есть специальный механизм расширения: частичный метод OnContextCreated().
Вы можете использовать это так:
partial class MyAppContext
{
partial void OnContextCreated()
{
// Register the event handler
this.Connection.StateChange += Connection_StateChange;
}
void Connection_StateChange(object sender, StateChangeEventArgs e) {
}
}
Как насчет промежуточного базового класса?
public class Intermediate : WhateverYourBaseWas {
public Intermediate(string connectionString, string containerName) : base(connectionString, containerName) {
this.Connection.StateChange += Connection_StateChange;
}
}
Тогда ваш сгенерированный класс может наследовать от этого.