Есть ли простой способ получить доступ к DataContext в классе сущности linq2sql.
Я пытаюсь создать что-то вроде EntitySet, но не могу понять, как EntitySet имеет доступ к контексту, который изначально создал объект сущности.
Я хочу иметь обычный класс сущности linq2sql с возможностью доступа класса к DataContext, который его создал. Я знаю, что это возможно, потому что, когда у вас есть класс сущности с первичным ключом, linq2sql дает вам возможность загружать всех дочерних элементов без создания нового DataContext.





В основном нет.
Класс EntitySet<T> имеет внутреннее свойство Source, которое назначается контекстом данных, и именно так он получает данные по запросу. Однако для самих классов данных ничего подобного нет.
Однако я считаю, что Entity Framework имеет гораздо больший доступ к этому за счет принудительной иерархии объектов.
В отличие от Entity Framework, LINQ-to-SQL (по замыслу) может использоваться с обычными, игнорирующими персистентность классами, поэтому он не предполагает, что у него есть доступ к этому типу данных.
Класс Entity не должен знать контекст данных, поскольку это просто отображение таблицы, но контекст данных знает все сущности и свойства соединения.
Вы можете связываться с дочерней таблицей через родительский класс сущностей из-за связи сущностей, а не через контекст данных.
Класс контекста данных будет использоваться в конце, где потребляются сущности, я не вижу необходимости, чтобы сущности знали о контексте,
Если вы можете рассказать о конкретном сценарии, мы можем попробовать другой подход.
Я точно знаю, что ты имеешь в виду. Предполагается, что мы будем выполнять наши вычисления / проверку в частичном классе сущности, но если у сущности нет доступа к тексту данных, то сколько мы можем сделать? Например, в моем объекте SalesOrder всякий раз, когда адрес «Доставить» изменяется, SalesOrder должен запрашивать базу данных, чтобы узнать, применяется ли налог к этому штату / почтовому индексу. Я боролся с этим некоторое время, но сегодня я сломался и пошел с уродливым методом, но пока все хорошо. По сути, все, что я делаю, - это создаю свойство «Контекст» в моем частичном классе и устанавливаю его с текстом данных всякий раз, когда создается сущность.
Partial Class SalesOrder
Private moContext As L2S_SalesOrdersDataContext
Friend Property Context() As L2S_SalesOrdersDataContext
Get
Return moContext
End Get
Set(ByVal value As L2S_SalesOrdersDataContext)
moContext = value
End Set
End Property
...
YMMV, особенно если вы отключаете свои сущности.
По сути, вы можете сделать это с помощью небольшого взлома. DataCOntext прикрепляет StandardChangeTracker к вашей сущности:
DataContext context = null;
object changeTracker = (from i in o1.GetInvocationList() where i.Target.GetType().FullName == "System.Data.Linq.ChangeTracker+StandardChangeTracker" select i.Target).FirstOrDefault();
if (changeTracker != null) // DataCOntext tracks our changes through StandardChangeTracker
{
object services = Reflector.GetFieldValue(changeTracker, "services");
context = (DataContext)Reflector.GetFieldValue(services, "context");
}
Где Reflector.GetFieldValue равно
public static object GetFieldValue(object instance, string propertyName)
{
return instance.GetType().GetField(propertyName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(instance);
}
Мне просто пришлось сделать то же самое. Вот мое решение (хотя, вероятно, не лучший подход, но, по крайней мере, довольно элегантно):
Во-первых, создайте интерфейс для всех ваших сущностей, который будет унаследован от INotifyPropertyChanging. Это используется для подключения некоторых методов расширения и сохранения отдельной реализации нашей реализации. В моем случае интерфейс называется ISandboxObject:
public interface ISandboxObject : INotifyPropertyChanging
{
// This is just a marker interface for Extension Methods
}
Затем создайте новый статический класс, содержащий метод расширения для получения DataContext. Это достигается путем поиска обработчика событий в LINQ Change Tracker, прикрепленном к событию INotifyPropertyChanging.PropertyChanging. Найдя средство отслеживания изменений, мы можем получить оттуда DataContext:
/// <summary>
/// Obtain the DataContext providing this entity
/// </summary>
/// <param name = "obj"></param>
/// <returns></returns>
public static DataContext GetContext(this ISandboxObject obj)
{
FieldInfo fEvent = obj.GetType().GetField("PropertyChanging", BindingFlags.NonPublic | BindingFlags.Instance);
MulticastDelegate dEvent = (MulticastDelegate)fEvent.GetValue(obj);
Delegate[] onChangingHandlers = dEvent.GetInvocationList();
// Obtain the ChangeTracker
foreach (Delegate handler in onChangingHandlers)
{
if (handler.Target.GetType().Name == "StandardChangeTracker")
{
// Obtain the 'services' private field of the 'tracker'
object tracker = handler.Target;
object services = tracker.GetType().GetField("services", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(tracker);
// Get the Context
DataContext context = services.GetType().GetProperty("Context").GetValue(services, null) as DataContext;
return context;
}
}
// Not found
throw new Exception("Error reflecting object");
}
Теперь у вас есть хороший метод расширения, который предоставит вам DataContext из любого объекта, реализующего ISandboxObject. Пожалуйста, сделайте еще несколько проверок на ошибки, прежде чем использовать это в гневе!
У меня такая же проблема, и хотя я понимаю ваши аргументы в пользу этого (поверьте мне, я за это), у меня есть реальная потребность в этом. В некоторые из наших частичных классов сущностей мы добавили некоторые свойства, которые должны запрашивать базу данных на основе свойства их сущности, чтобы захватить некоторую коллекцию других сущностей, которые напрямую не связаны в SQL Server. Это действительно уродливо, потому что мы создаем новый контекст данных при вызове этих свойств (некоторые из них в цикле).