문제

I am having a great deal of difficulty in splitting my monolithic ASP.NET MVC application into an N-tier app. In the following example, during the first call to _messageRepo.Create() an exception is thrown saying that the DbContext cannot be used since it was already disposed.

I cannot see how that is happening, and attempting to break on the Dispose() method doesn't actually cause the application to break during debugging.

The basic structure is as follows:

  • Controllers are injected with instances of each service they use //ie: public MyController(IMessageService messageService)
  • Services contain any needed instances of repositories (ie: _messageRepository)
  • Repositories utilize an instance of MyContext, a sub-class of DbContext
  • These instances are reconstructed whenever needed as in the following example

    using(var context = new MyContext())
    {
        _messageRepo = new MessageRepository(context);
        _idRepo = new IdentityRepository(context);
    
        var status = _messageRepo.GetStatus(Int32.Parse(message.To));
        message.To = status.Header.From.Name;
        message.ToHash = Obfuscate.SaltAndHash(message.To);
        message.Subject = "RE:" + status.Header.Subject;
    
        var toUser = _idRepo.Get(message.To);
        var fromUser = _idRepo.Get(_userName);
        var rawMessage = new Message()
        {
            Content = message.Content,
            Attachments = GetAttachments(message.AttachmentIds)
        };
        var header = new MessageHeader()
        {
            To = toUser,
            From = fromUser,
            Subject = message.Subject
        };
        _messageRepo.Create(new MessageStatus()
        {
            CreatedAt = DateTime.Now,
            IsRead = false,
            IsSpam = false,
            IsTrash = false,
            Message = rawMessage,
            Header = header,
            Owner = header.To
        });
        _messageRepo.Create(new MessageStatus()
        {
            CreatedAt = DateTime.Now,
            IsRead = false,
            IsSpam = false,
            IsTrash = false,
            Message = rawMessage,
            Header = header,
            Owner = header.From
        });
        context.Commit();
        Email.SendNewMessageNotification(fromUser.Email, toUser.Email);
    }
    

The repository methods are LINQ one-liners that retrieve models from a database through Entity Framework using the code first approach.

Is there something wrong with this approach? I did have MyContext implement IUnitOfWork at first, but I removed that until I get this less complicated method functional.

Additionally, I am using an IoC framwork (AutoFac) to load instances of these interface implementations. If this is the problem, then what do I need to change about my logic to accomodate AutoFac?

//in Global.asax.cs
builder.RegisterType<PonosContext>().As<PonosContext>().InstancePerHttpRequest();

//Example repo constructor
public MessageRepository(PonosContext context)
{
    _db = context;
}
도움이 되었습니까?

해결책

When you use a IoC container, you should never call new of a service managed by the container. In this example you should not use:

using(var context = new MyContext())
_messageRepo = new MessageRepository(context);
_idRepo = new IdentityRepository(context);

Your dependencies should be injected (by constructor for example).

How do you register your repositories with AutoFac? Maybe you have your repositories configurard as singleton? This can cause the dispose exception when a repository is reused for a second http request.

다른 팁

DataContexts should not live too long :) They should be created and disposed close to their usage.

Consider not to pass the same instance between different repositories. If you want to wrap multiple operations in a single transaction, look at TransactionScope

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top