Тип объекта EntityFramework из ObjectContext?

#c# #entity-framework #objectcontext

#c# #entity-framework #objectcontext

Вопрос:

У меня есть метод с такой подписью:

public void GenerateLog<TEntity>(TEntity entity) where TEntity : EntityObject

Как я могу выполнить цикл по моему ObjectContext и вызвать это для каждой сущности в моем ObjectContext?
Я знаю, что могу это сделать:

 foreach (ObjectStateEntry entry in
                context.ObjectStateManager.GetObjectStateEntries(
                EntityState.Added | EntityState.Modified))
{
    string entityName = entry.Entity.GetType().Name;
}
  

Но я не знаю, как перейти от строкового представления имени к GenerateLog<MYSTRING> вместо GenerateLog<TEntity> .

Комментарии:

1. Нет способа вызвать универсальный метод со строкой, вам пришлось бы предоставить перегрузку, которая просто принимает object (или какой-либо другой общий базовый класс). Что именно GenerateLog делает внутренне? Другими словами, почему он изначально является универсальным? Что вы делаете в теле метода, чтобы было значение в том, что он является универсальным методом? Возможно, обладая этими знаниями, люди могли бы предоставить альтернативное решение или предложение о том, как выполнить то, что вы хотите.

2. Это метод, который регистрирует изменения по записям в базе данных. Значение TEntity фильтруется по всему объекту целиком. Все это работает замечательно … за исключением того, что я зациклился на этом пункте. Если я вызываю его, предоставляя фактический тип объекта, такой как GenerateLog<Contact>, он работает безупречно. Но мне нужно вызвать это, не делая этого, поскольку есть МНОЖЕСТВО объектов, для которых мне пришлось бы это вызывать.

Ответ №1:

Вам нужно создать универсальный метод из вашего GenerateLog , а затем вызвать его. Обычно мне нужно немного повозиться, прежде чем я заставлю что-то подобное работать, но это должно быть близко

 MethodInfo generateLog = typeof(YourClass)
    .GetMethod("GenerateLog", BindingFlags.Public | BindingFlags.Instance );

MethodInfo genericGenerateLog = generateLog.MakeGenericMethod(entry.Entity.GetType());

genericGenerateLog.Invoke(this, new object[] { entry.Entity });
  

YourClass это просто класс, в котором находится GenerateLog.

Комментарии:

1. Черт возьми…. это потрясающе. Пока все работает ОТЛИЧНО! Я еще не проверил ваш ответ, потому что я не уверен на 100%, что это мое решение, потому что сначала я должен исправить другие ошибки. 😛 Хотя пока все выглядит ИДЕАЛЬНО.

2. Блестящее решение, Стив. Отлично работает!

3. @James Такой была моя реакция, когда я впервые использовал MakeGenericMethod, что было не так давно. Это очень удобно. Компромисс заключается в том, что это медленнее, чем если бы у вас был гигантский оператор case со строго типизированными вызовами или что-то в этом роде.

Ответ №2:

Как сказал Дрю Марш, нет способа вызвать универсальный метод, используя только имя аргумента универсального типа. Из-за этого я могу только предложить то, что, по вашему мнению, является немного мусорным решением, использующим разрешение метода во время выполнения — хотя это сработало бы…

Во-первых, назначьте динамическую переменную внутри foreach и вызовите частный метод с именем (например) CallGenerateLog() :

 foreach (ObjectStateEntry entry in
                context.ObjectStateManager.GetObjectStateEntries(
                EntityState.Added | EntityState.Modified))
{
    dynamic dynamicEntity = entry.Entity;

    CallGenerateLog(dynamicEntity);
}
  

… предоставьте одну перегрузку CallGenerateLog() для каждого из типов сущностей, которые вы хотите зарегистрировать, и пусть каждый из них вызовет ваш GenerateLog() метод, например:

 private static void CallGenerateLog(User user)
{
    GenerateLog(user);
}

private static void CallGenerateLog(Customer customer)
{
    GenerateLog(customer);
}
  

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

 private static void CallGenerateLog(object entity)
{
}
  

Проблемы с этим включают:

  1. Вам нужна перегрузка CallGenerateLog() для каждого типа объекта, поэтому, если вы добавите новый тип объекта, который вы хотите зарегистрировать, вам придется не забыть добавить перегрузку для него (хотя вы могли бы решить это с помощью шаблонов T4)

  2. Существуют некоторые накладные расходы на разрешение метода во время выполнения, поэтому вам, возможно, придется определить, насколько хорошо работает метод, и решить, вызовет ли это у вас какие-либо проблемы.

Комментарии:

1. Выполнение вызова для каждого типа сущности — это именно то, чего я хотел избежать. Что это за «Шаблоны T4», о которых вы говорите?

2. Достаточно справедливо, re: по одному вызову на тип — это просто единственный способ, который я могу придумать для достижения того, чего вы хотите достичь. Шаблоны T4 представляют собой файлы генерации кода — Entity Framework использует их для генерации исходного кода по умолчанию и класса объектов POCO entity — вы все равно будете выполнять один вызов для каждого типа, но вы могли бы уменьшить вероятность того, что вы забудете перегрузку, поскольку код будет автоматически сгенерирован.