это то, что я в конце концов использовал
я есть проект WCF с множеством детей. У меня есть бизнес-уровень и уровень доступа к данным, на уровне доступа к данным у меня есть репозитории, которые извлекаются и сохраняются в моей базе данных. Я понимаю, что EF создает и уничтожает DataContext по мере необходимости. Например, допустим, у меня есть сущность Person и сущность Book (это не мое приложение, а просто попытка проиллюстрировать проблему).
Предположим, лицо выглядит следующим образом.
Person
string Name
vitual ICollection<Book> Books
С книгой может быть что-то вроде этого
Book
string Title
Person PersonLending
Теперь в моем BLL я хочу прочитать таблицу person и затем назначить книгу этому человеку, но этот человек уже существует в базе данных, поэтому BLL вызывает хранилище для объекта person.
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 на Без изменений на Персоне, но, похоже, это не работает.
Редактировать:
Я изменил вещи, чтобы создать новый DataContext для каждого запроса (AfterReceiveRequest и BeforeSendReply).
public class EFWcfDataContextAttribute : Attribute, IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase service,HostBase){}
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());
}
}
}
Initializer
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;
}