That isn't going to work. Objects loaded from a DbContext are tied to that context. If you create a new DbContext and then Include
some data then it will look to see if the current context has loaded the data (which it only does once) and then load it if necessary. This is by design and you cannot prevent that. Of course the alternative is lazy loading but that isn't recommended. The reason it works this way is because it doesn't know what you may or may not want to edit so everything is loaded as tracking by default and therefore there is a proxy tied to it. If you make any changes that need to be applied back to the DB then it has to do so on the same context it was created on.
It is also not recommended that you ever create a DbContext
and leave it open for the life of your app. That is just wasting a DB connection. Open your context (or have it passed in as part of DI), do your work and dispose of it. Leaving a context open actually makes things worse because by default if you make changes to the objects obtained by the context then every time you call SaveChanges
it'll try to push them out. If they fail then they are still modified and will be attempted the next time. This causes you to see saves fail even though the data you thought you were changing were valid. Cleaning up a context that has "broken" modifications is a pain, or at least used to be.
Firstly I'd have to ask the question how often do tags change. If they almost never change then lazy load them the first time they are needed. This is what a wrapper service or equivalent is for. Using a context directly in your code, while common in sample code, is prone to issues in a more professional codebase. Instead have your app call a service/repo/whatever that uses the context behind the scenes. Add caching here. When the app asks for a tag (or tags) the first time then load them from your context as no tracking. Then cache the results into a memory cache, static field, whatever works for your app. On subsequent calls to get the tags then it can pull from cache.
For the remaining code you have to decide how performant loading the tags really are. In general I recommend that you go ahead and use Include
to load the related tags for the objects you're loading. Honestly it is a join and the DB is going to return that data quickly. You aren't going to notice a perf hit. But if your app needs to display a list of tags then it calls your service which just loads the data once. Since the app isn't going to be modifying the list of tags then it doesn't need to worry about mapping the data back to a context.
Updating an object is a special case. If you include the tags on load then you can add and remove tags and save the changes and everything works. Again, perf probably isn't an issue. However if you do need to avoid loading the tags then don't include them when loading the object to be edited from the context. Use your service from earlier to grab the tags that you need and set the tag IDs on your object (or however you're storing the data) to populate them. Note that you cannot attach the shared tags to your parent object because they are shared. However in most cases you're using a relationship table (for many to many) and only need the "ID" of the tag and since that is a primitive you can set it without issue.