У меня есть проект WCF с сущностями, у которых много детей.У меня есть бизнес-уровень и уровень доступа к данным, на уровне доступа к данным у меня есть репозитории, которые извлекаются и сохраняются в моей базе данных.Я понимаю, что EF создает и уничтожает DataContext по мере необходимости.Например, допустим, у меня есть сущность Person и сущность Book (это не мое приложение, а просто попытка проиллюстрировать проблему).
Предположим, что Person выглядит следующим образом.
Person
string Name
vitual ICollection<Book> Books
С Книгой может быть что-то вроде этого
Book
string Title
Person PersonLending
Теперь в моем BLL я хочу прочитать таблицу лиц и затем назначить книгу этому человеку, но человек уже существует в базе данных, поэтомуBLL вызывает хранилище для объекта лица.
var person = repository.GetPerson("John Doe");
Мой репозиторий имеет этот код.
using(var context = new MyContext())
{
return (from p in context.Person
where p.Name == person
select p).FirstOrDefault());
}
Теперь в BLL я создаю новую книгу и назначаю этому человеку это.
var book = new Book();
book.PersonLending = person;
book.Title = "New Book";
repository.SaveBook();
Наконец, в хранилище я пытаюсь сохранить книгу обратно.
using(var context = new MyContext())
{
context.Book.Add(book);
context.SaveChanges();
}
Теперь, что происходит, я получаю две строки Person в таблице.Насколько я понимаю, это вызвано тем, что первый контекст разрушен, а второй контекст не знает, что Персона уже существует.
У меня есть два вопроса, я думаю.
- Что такоелучшая практика для обработки DataContext в WCF?Если только один текстовый текст передается из класса в класс и обратно в репозитории.
- Или есть способ сделать это сохранение.
Я попытался установить EntityStateНе изменяется для Person, но, похоже, он не работает.
Редактировать:
Я изменил параметры, чтобы создать новый DataContext для запроса (AfterReceiveRequest и BeforeSendReply),
public class EFWcfDataContextAttribute : Attribute, IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase){}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters){}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (var endpoint in channelDispatcher.Endpoints)
{
endpoint.DispatchRuntime.MessageInspectors.Add(new EFWcfDataContextInitializer());
//endpoint.DispatchRuntime.InstanceContextInitializers.Add(new EFWcfDataContextInitializer());
}
}
}
Инициализатор
public class EFWcfDataContextInitializer : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
instanceContext.Extensions.Add(new EFWcfDataContextExtension(new MyDataContext()));
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
WcfDataContextFactory.Dispose();
}
}
И расширение
public class EFWcfDataContextExtension : IExtension<InstanceContext>
{
public ICoreDataContext DataContext { get; private set; }
public EFWcfDataContextExtension(ICoreDataContext coreDataContext)
{
if(DataContext != null)
throw new Exception("context is not null");
DataContext = coreDataContext;
}
public void Attach(InstanceContext owner){}
public void Detach(InstanceContext owner) {}
}
Это, кажется, создает совершенно новую проблему.Я получаю текущий контекст, вызывая OperationContext.Current.InstanceContext.Extensions.Find (). DataContext, но теперь кажется, что два контекста влияют друг на друга.По тому же запросу первый вернет нулевую запись, а второй будет успешным.Они оба находятся в уникальных сеансах, и когда они оба созданы, они являются нулевыми и создаются как новый DataContext.Когда я проверяю свойство Database.Connection с первого раза, оно закрывается, и попытка открыть его вручную создает дополнительные ошибки.Я действительно думал, что это решит проблему.
Я также попытался сделать это с IContractBehaviour, с тем же результатом.Поэтому я либо делаю что-то не так, либо упускаю что-то очевидное.
PS: Я пытался установить состояние «Без изменений» до того, как сделал исходное сообщение.PPS: В случае, если кто-то задается вопросом, моя фабрика данных просто использует эти два метода
public static void Dispose()
{
ICoreDataContext coreDataContext = OperationContext.Current.InstanceContext.Extensions.Find<EFWcfDataContextExtension>().DataContext;
coreDataContext.Dispose();
coreDataContext = null;
}
public static ICoreDataContext GetCurrentContext()
{
var context = OperationContext.Current.InstanceContext.Extensions.Find<EFWcfDataContextExtension>().DataContext;
if (context != null)
{
if (context.Database.Connection.State == ConnectionState.Closed)
context.Database.Connection.Open();
}
return context;
}