Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to Get Logged User in Trigger? #23

Closed
cigano opened this Issue Jan 24, 2018 · 8 comments

Comments

Projects
None yet
5 participants
@cigano
Copy link

cigano commented Jan 24, 2018

I want to save the information about the user that inserted and/or modified the entry. This is being a pain, since I need to use the static constructor to get this information, and ASP.NET Core uses a lot of IoC/DI patterns that make the task almost impossible.

The idea was to use a class like this to get the user:

    public class CurrentUserService
    {
        private IHttpContextAccessor _contextAccessor;
        private HttpContext _context { get { return _contextAccessor.HttpContext; } }

        public CurrentUserService(IHttpContextAccessor contextAccessor)
        {
            _contextAccessor = contextAccessor;
        }

        public string UserName
        {
            get
            {
                var userName = "SystemGenerated";

                if (_context != null)
                {
                    if (_context.User != null)
                    {
                        var identity = _context.User.Identity;

                        if (identity != null && identity.IsAuthenticated)
                        {
                            userName = identity.Name;
                        }
                    }
                }

                return userName;
            }
        }
    }

But I think including this as an entry property can be better.

@NickStrupat

This comment has been minimized.

Copy link
Owner

NickStrupat commented Mar 20, 2018

I'm actually not sure how to do this in ASP.NET Core. Before Core it was easily done with
System.Web.HttpContext.Current.User.Identity.GetUserId<T>()

@cigano

This comment has been minimized.

Copy link
Author

cigano commented Mar 21, 2018

AFAIK, now this use is considered "an anti-pattern", so I had to implement this service to get the user from a HttpContext that is injected by the time the request is called.

Here is a sample using EntityFramework.Triggers and CurrentUserService:

    public abstract class MyEntity : MyReadOnlyEntity, IMyEntity
    {
        public DateTime? LastModifiedDate { get; set; }
        public string LastModifiedByUser { get; set; }

        static MyEntity()
        {
            Triggers<Entidade>.Updating += entry =>
            {
                entry.Entity.LastModifiedDate = DateTime.UtcNow;
                entry.Entity.LastModifiedByUser = CurrentUserService.UserName;
            };
        }
    }

It would be nice having this feature handy at EntityFramework.Triggers, don't you think?

@NickStrupat

This comment has been minimized.

Copy link
Owner

NickStrupat commented Mar 23, 2018

I agree that it would be a good feature in EntityFramework.Triggers. I have tinkered with it in EntityFramework.SoftDeletable as a way to grab the username responsible for the delete request.

I'm thinking the best way to do this is have the entry object contain a reference to the DI container being used. That approach would require the code to register the DI container instance with the library. But, there is a mismatch because EF.Triggers run on basically a singleton which would be quite obviously clunky when the user code assigns a container instance to a static variable.

So I'm thinking a breaking API change is required so it doesn't end up with a mismatched design.

@gregoryagu

This comment has been minimized.

Copy link

gregoryagu commented Apr 16, 2018

I also am in need of this.

@dchristensen

This comment has been minimized.

Copy link

dchristensen commented May 17, 2018

As a hacky workaround, the DbContext explicitly implements the IInfrastructure<IServiceProvider> interface, so you can cast it as such to gain access to the IServiceProvider instance used by EF.

@RKennedy9064

This comment has been minimized.

Copy link

RKennedy9064 commented Nov 26, 2018

Not sure if it's still relevant or not, but since I know my DbContext will always be used in a request, and I want to track the user used to update each object, I just inject IHttpContextAccessor like do.

private readonly IHttpContextAccessor _contextAccessor;

public ClaimsPrincipal CurrentUser
{
    get
    {
        return _contextAccessor.HttpContext.User;
    }
}

public ApiContext(DbContextOptions<ApiContext> options, IHttpContextAccessor contextAccessor)
    : base(options)
{
    _contextAccessor = contextAccessor;
}
@NickStrupat

This comment has been minimized.

Copy link
Owner

NickStrupat commented Nov 28, 2018

Hi everyone,
I made a solid effort and designed the best dependency-injection API I could come up with. I need to update the README to document the new API, but if you update to the current nuget package you can use it now.

To inject a service, either use the DbContextWithTriggers(IServiceProvider) constructor or pass is into the SaveChangesWithTriggers...(...) methods if you're calling those yourself already. And then...

Triggers<Entity, Context>.GlobalInserting.Add<IHttpContextAccessor>(entry => Console.WriteLine(entry.Service.HttpContext.User));

You can also inject multiple services easily with a value tuple...

Triggers<Entity, Context>.GlobalInserting.Add<(IHttpContextAccessor Hca, OtherService Os)>(entry => Console.WriteLine(entry.Service.Hca.HttpContext.User + " " + entry.Service.Os.Foo));

You can also inject instance of Triggers<...> to your own container so you don't have to use the global static instance.

@cigano

This comment has been minimized.

Copy link
Author

cigano commented Dec 3, 2018

This is awesome! I think I have my problem solved entirely now. Thanks, @NickStrupat!

@cigano cigano closed this Dec 3, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.