(如果方法签名看起来有点奇怪,请提前道歉,我是少数使用显式接口实现来帮助避免陈旧代码灰尘兔子乱扔代码的人之一)
使用来自 Forms Authentication Web 应用程序的会话令牌访问会话状态以从 DbContext 中获取用户的示例:
[Serializable]
public class UserDetails
{
public int UserId { get; set; }
public string DisplayName { get; set; }
}
public interface ISessionHelper
{
UserDetails CurrentUser { get; set; }
void Clear();
bool IsAuthenticated();
}
public class SessionHelper : ISessionHelper
{
private const string UserDetailsKey = "YourUniqueSessionIdKey";
private ISessionHelper This => this;
UserDetails ISessionHelper.CurrentUser
{
get
{
try
{
var token = (UserDetails)HttpContext.Current.Session[UserDetailsKey];
return token;
}
catch
{
throw new ApplicationException("The current session could not be resolved.");
}
}
set
{
try
{
HttpContext.Current.Session[UserDetailsKey] = value;
}
catch
{
throw new ApplicationException("The current session state could not be set.");
}
}
}
/// <summary>
/// <see cref="ISessionHelper.Clear"/>
/// </summary>
void ISessionHelper.Clear()
{
HttpContext.Current.Session.Clear();
}
/// <summary>
/// <see cref="ISessionHelper.IsAuthenticated(string)"/>
/// </summary>
bool ISessionHelper.IsAuthenticated()
{
try
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var userDetails = This.CurrentUser;
return (userDetails != null && userDetails.UserId != 0);
}
catch
{
return false;
}
}
}
public interface ICurrentUserLocator
{
bool IsUserLoggedIn { get; }
int CurrentUserId { get; }
string CurrentUserDisplayName { get; }
}
public sealed class FormsAuthenticationUserLocator : ICurrentUserLocator
{
private const string UnauthorizedAccessExceptionMessage = "No user is currently logged in.";
private readonly ISessionHelper _sessionHelper = null;
[ExcludeFromCodeCoverage]
private ICurrentUserLocator This => this;
private UserDetails _userDetails = null;
[ExcludeFromCodeCoverage]
private UserDetails CurrentUser
{
get { return _userDetails ?? (_userDetails = SessionHelper.UserDetails); }
}
[ExcludeFromCodeCoverage]
bool ICurrentUserLocator.IsUserLoggedIn => CurrentUser != null;
[ExcludeFromCodeCoverage]
int ICurrentUserLocator.CurrentUserId => CurrentUser?.UserId ?? throw new UnauthorizedAccessException(UnauthorizedAccessExceptionMessage);
[ExcludeFromCodeCoverage]
string ICurrentUserLocator.CurrentUserDisplayName => CurrentUser?.DisplayName ?? throw new UnauthorizedAccessException(UnauthorizedAccessExceptionMessage);
public FormsAuthenticationUserLocator(ISessionHelper sessionHelper)
{
_sessionHelper = sessionHelper ?? throw new ArgumentNullException("sessioNHelper");
}
}
假设您在容器中使用依赖注入,请在每个请求的生命周期内针对容器中各自的接口注册这两个类。然后在 DbContext 中添加对 ICurrentUserLocator 的依赖:
public class AppDbContext : DbContext
{
private readonly ICurrentUserLocator _currentUserLocator = null;
public AppDbContext(ICurrentUserLocator currentUserLocator)
{
_currentUserLocator = currentUserLocator ?? throw new ArgumentNullException("currentUserLocator");
}
}
现在您可以在覆盖的 SaveChanges 中获取当前用户 ID:
var currentUser = Users.Single(x => x.UserId == _currentUserLocator.CurrentUserId);
var updatedEntities = ChangeTracker.Entries()
.Where(x => x.State == EntityState.Modified)
.Select(x => x.Entity)
.Cast<EditableEntityBase>();
foreach (var entity in updatedEntities)
{
entity.LastModifiedBy = currentUser;
entity.LastModifiedAt = DateTime.Now;
}
var insertedEntities = ChangeTracker.Entries()
.Where(x => x.State == EntityState.Added)
.Select(x => x.Entity)
.Cast<EditableEntityBase>();
foreach (var entity in insertedEntities)
{
entity.CreatedBy = entity.LastModifiedBy = currentUser;
entity.CreatedAt = entity.LastModifiedAt = DateTime.Now;
}
如果您只公开 FK 而不是用户导航属性,那么您只需设置 ID。如果您的应用具有注销功能,请务必调用 SessionHelper.Clear() 方法。在这个例子中,我修改了我通常使用的会话助手,只关注当前用户的详细信息,以保持简单。它通常会跟踪一些其他细节,因此 Clear() 方法会擦除整个会话。它是用于处理已知会话状态的标准化包装器,而不是到处乱扔Session["somestring"] 的代码。