0

I want to access some data within my overriden SaveChanges() in my dbcontext without passing any parameters. Any suggestions? I'm working with MVC4 and Entity Framework Database-First.

public partial class Entities : DbContext
{
    public override int SaveChanges()
    {
        // i want to get user info from MVC model but no need to pass any parameters when call SaveChanges()
        var UserInfo = userInfo;

        // Call the original SaveChanges(), which will save both the changes made and the audit records
        return base.SaveChanges();
    }
}
7
  • 2
    Are you using a dependency injection framework? Are you using the dbcontext in your controllers or do you have some service / repository layer? Commented Dec 10, 2014 at 3:50
  • Thanks for your concern:D..i'm using service/repository layer Commented Dec 10, 2014 at 3:53
  • You could add Ninject as a dependency injection framework, bind your UserInfo in RequestScope and let it inject as dependency into the Entities class. Commented Dec 10, 2014 at 3:57
  • Honestly this is the first time i hear about Ninject, so could you give me some demos or examples about RequestScope ? Commented Dec 10, 2014 at 4:04
  • What's wrong with handling this in your service/repo layer? Commented Dec 10, 2014 at 4:06

1 Answer 1

1

Solution 1: Dependency Injection

This solution is pretty extensible, but you would have to modify the code in your repositories and controllers to use the injected dependencies instead of creating new instances with new.

  1. Install Ninject. In Visual Studio, find the Package Manager Console and run Install-Package Ninject.MVC4 -dependencyVersion Highest in there.

  2. Add constructor injection. Modify your controller, so that it gets an instance of your repository in its constructor. Modify your repository, so that it gets an instance of your entity context in its constructor. Cache your dependencies in private fields. Example code:

    // In your controller:
    public MyController(MyRepository repo)
    {
        this.repo = repo;
    }
    
    // In your repository:
    public MyRepository(Entities context)
    {
        this.context = context;
    }
    
    // In your entities:
    public Entities(UserInfo userInfo)
    {
        this.userInfo = userInfo;
    }
    
  3. Add a UserInfo provider. We need to tell Ninject where to get the UserInfo from. We can use the provider interface here:

    public class UserInfoProvider : Provider<UserInfo>
    {
        protected override UserInfo CreateInstance(IContext context)
        {
            UserInfo UserInfo = new UserInfo();
    
            // Do some complex initialization here.
    
            return userInfo;
        }
    }
    
  4. Add bindings. We need to tell Ninject to use the provider. We also want the lifetime of a UserInfo instance and of our entity context to be bound to the request cycle of MVC. Update your App_Start\NinjectWebCommon.cs:

    private static void RegisterServices(IKernel kernel)
    { 
        kernel.Bind<Entities>()
              .ToSelf()
              .InRequestScope();
    
        kernel.Bind<UserInfo>()
              .ToProvider<UserInfoProvider>()
              .InRequestScope();
    }
    
  5. Run your app. Ninject should use your constructors and provide the requested dependencies.

For further information, visit the Ninject Wiki.


Solution 2: Thread local context

This requires no modification to your repositories, but it makes the code less testable and resembles an anti pattern somehow. This won't work if your controller calls multithreaded code.

  1. Add context class.

    public class UserInfoContext : IDisposable
    {
        private static readonly ThreadLocal<UserInfo> UserInfos = new ThreadLocal<UserInfo>();
    
        public static UserInfo Current
        {
            get 
            {
                if (UserInfos == null)
                {
                    throw new InvalidOperationException("UserInfoContext has not been set.");
                }
    
                return UserInfos.Value;
            }
        }
    
        public static UserInfoContext Create(UserInfo userInfo)
        {
            if (userInfo == null)
            {
                throw new ArgumentNullException("userInfo");
            }
    
            if (UserInfos.Value != null)
            {
                throw new InvalidOperationException("UserInfoContext should not be nested.");
            }
    
            UserInfos.Value = userInfo;
    
            return new UserInfoContext();
        }
    
        private UserInfoContext() { }
    
        public void Dispose()
        {
            UserInfos.Value = null;
        }
    }
    
  2. Wrap your controller code. Example:

    public ActionResult Index()
    {
        using (UserInfoContext.Create(myUserInfo))
        {
            // do stuff that calls your repositories
    
            return View();
        }
    }
    
  3. Update your Entities class.

    public partial class Entities : DbContext
    {
        public override int SaveChanges()
        {
            var UserInfo = UserInfoContext.Current;
    
            // Call the original SaveChanges(), which will save both the changes made and the audit records
            return base.SaveChanges();
        }
    }
    
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.