实体类型ApplicationUser不是当前上下文的模型的一部分,DB First具有自定义用户存储 [英] The entity type ApplicationUser is not part of the model for the current context, DB First with custom user store

查看:230
本文介绍了实体类型ApplicationUser不是当前上下文的模型的一部分,DB First具有自定义用户存储的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我正在一个现有的数据库之上构建一个应用程序,我有限的权限进行更改(这个想法是尽可能少地修改DB模式)。这是一个试图将身份系统与自定义用户商店一起使用到MySQL数据库的MVC 5应用程序。



问题:尝试通过自动生成的AccountController在注册方法中注册用户时出现以下异常:

  IdentityResult result = await UserManager。 CreateAsync(user,model.Password); 




System.InvalidOperationException:实体类型ApplicationUser不是模型的一部分为当前上下文。
在System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType(Type entityType)
在System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
在System.Data.Entity .Internal.Linq.InternalSet 1.Initialize()
在System.Data.Entity.Internal.Linq.InternalSet
1.get_InternalContext()
在System.Data.Entity.Infrastructure.DbQuery 1.System.Linq.IQueryable.get_Provider()
在System.Data.Entity.QueryableExtensions.FirstOrDefaultAsync [TSource](IQueryable
1来源,表达式 1谓词,CancellationToken cancelToken)
在System.Data.Entity.QueryableExtensions.FirstOrDefaultAsync [TSource](IQueryable
1 source,Expression 1谓词)
在Microsoft.AspNet.Identity.EntityFramework.UserStore
6.d__6c.MoveNext()


我尝试过的:


  1. 帐户控制器的UserManager实例化从
    HttpContext.GetOwinContext()。GetUserManager< ApplicationUserManager>();
    new ApplicationUserManager新的UserStoreService(新的Entities())); 这解决了直接的问题,让我注册。然而,当我尝试重置密码时,这会导致问题,并且我遇到另一个问题,我无法解决我在哪里获得无效的用户令牌(尽管我可以确认用户令牌正常工作,当我使用HttpContext.GetOwinContext ... UserManager的版本

  2. 有几个帖子关于如下更改自动生成的连接字符串:

< add name =EntitiesconnectionString =metadata = res://*/Models.tools.csdl | res://*/Models.tools .ssdl | res://*/Models.tools.msl; provider = MySql.Data.MySqlClient; provider connection string =& quot; server = localhost; user id = user; password = ***; persistsecurityinfo = True; data = db& quot;providerName =System.Data.EntityClient/>



到正常的连接字符串,如下所示:
< add name =EntitiesconnectionString =server = localhost; user id = user; password = ***; persistsecurityinfo = True; database = dbproviderName = MySql.Data.MySqlClient/> 。这与非故意的代码第一例异常很快爆炸。然后还有一些其他问题在路上似乎是螺旋式的(在表上没有声明的键修正问题之后),我可以接受这方面的建议,但宁可不必去这条路线。 / p>

以下是与设置相关的代码。任何想法,我还能在这里失踪?或者是使用连接字符串想法来解决这个问题的唯一方法?



谢谢!



设置
我正在使用数据库(EDMX)到现有的MySQL数据库。随着此更改用户到一个int的主键,我有一个自定义用户服务来附加到我的数据库和用户表。



DbContext设置(我已经修改自动生成的文件尝试使用Identity系统):

  public partial class Entities:IdentityDbContext< ApplicationUser,CustomRole,int, CustomUserLogin,CustomUserRole,CustomUserClaim> // DbContext 
{
public Entities()
:base(name = Entities)
{
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}

public static Entities Create()
{
return new Entities();
}

// DbSets在这里
}

ApplicationUser.cs:

  public class ApplicationUser:IdentityUser< int,CustomUserLogin,CustomUserRole,CustomUserClaim> // IUser< int> // IdentityUser 
{
//自定义属性在这里

public async任务< ClaimsIdentity> GenerateUserIdentityAsync(UserManager< ApplicationUser,int> manager)
{

var userIdentity = await manager.CreateIdentityAsync(this,DefaultAuthenticationTypes.ApplicationCookie);
//在此添加自定义用户声明

return userIdentity;
}
}

ApplicationUserManager.cs

  public class ApplicationUserManager:UserManager< ApplicationUser,int> 
{
public ApplicationUserManager(IUserStore< ApplicationUser,int> store)
:base(store)
{}

public static ApplicationUserManager Create(IdentityFactoryOptions& ApplicationUserManager>选项,IOwinContext上下文)
{
var manager = new ApplicationUserManager(new UserStoreService(context.Get< Entities>()));

//为用户名配置验证逻辑
manager.UserValidator = new UserValidator< ApplicationUser,int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};

//为密码配置验证逻辑
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};

manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;

manager.EmailService = new EmailService();

var dataProtectionProvider = options.DataProtectionProvider;
if(dataProtectionProvider!= null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider< ApplicationUser,int>(dataProtectionProvider.Create(ASP.NET Identity)){TokenLifespan = TimeSpan.FromHours(24)};
}
return manager;
}
}

UserStoreService.cs

  public class UserStoreService:UserStore< ApplicationUser,CustomRole,int,CustomUserLogin,CustomUserRole,CustomUserClaim> //,IUserStore< ApplicationUser,int>,IUserPasswordStore< ApplicationUser,int>,IUserEmailStore< ApplicationUser,int>,IUserLockoutStore< ApplicationUser,int> IUserSecurityStampStore< ApplicationUser,int> 
{
private Entities _db; // = new Entities();

public UserStoreService(Entities db):base(db)
{
_db = db;
}

公共覆盖任务CreateAsync(ApplicationUser用户)
{
var profile = new ffs_profile {
//设置道具
} ;

_db.ffs_profile.Add(profile);
return _db.SaveChangesAsync();
}

公共异步覆盖任务< ApplicationUser> FindByNameAsync(string userName)
{
var profile = await _db.ffs_profile.Where(u => u.email == userName).FirstOrDefaultAsync();

ApplicationUser user = null;
if(profile!= null)
user = ToApplicationUser(profile);

返回用户;
}

private ApplicationUser ToApplicationUser(ffs_profile个人资料)
{
返回新的ApplicationUser
{
//设置属性

};
}
public override Task< string> GetPasswordHashAsync(ApplicationUser user)
{
if(user == null)
{
throw new ArgumentException(null user);
}

返回Task.FromResult(user.PasswordHash);
}

public override Task< bool> HasPasswordAsync(ApplicationUser用户)
{
返回Task.FromResult(user.PasswordHash!= null);
}

public override Task SetPasswordHashAsync(ApplicationUser user,string passwordHash)
{
return Task.Run(()=>
{
if(passwordHash == null)
throw new ArgumentNullException(passwordHash);
if(string.IsNullOrWhiteSpace(passwordHash))
throw new ArgumentException(passwordHash不能为空,为空,或由空格组成。);
user.PasswordHash = passwordHash;

});
}

公共覆盖异步任务< ApplicationUser> FindByIdAsync(int userId)
{
var profile = await _db.ffs_profile.Where(u => u.profile_id == userId).FirstOrDefaultAsync();

ApplicationUser user = null;
if(profile!= null)
user = ToApplicationUser(profile);

返回用户;
}

public override Task< string> GetSecurityStampAsync(ApplicationUser user)
{
if(user == null)
{
throw new ArgumentNullException(user);
}
返回Task.FromResult< string>(user.SecurityStamp);
}
public override Task SetSecurityStampAsync(ApplicationUser user,string stamp)
{
if(user == null)
{
throw new ArgumentNullException(用户);
}
user.SecurityStamp = stamp;
return Task.FromResult< int>(0);
}
}

最后,帐户控制器的相关部分:

  public class AccountController:Controller 
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;

public AccountController()
{
}

public AccountController(ApplicationUserManager userManager,ApplicationSignInManager signInManager)
{
UserManager =的UserManager;
SignInManager = signInManager;
}

public ApplicationUserManager UserManager
{
get
{
return _userManager? 。HttpContext.GetOwinContext()GetUserManager< ApplicationUserManager>();
}
私人集
{
_userManager = value;
}
}
//其他自动生成方法
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async任务< ActionResult>注册(RegisterViewModel模型)
{
if(ModelState.IsValid)
{
var user = new ApplicationUser
{
//设置道具
};
try
{
IdentityResult result = await UserManager.CreateAsync(user,model.Password);
if(result.Succeeded)
{

await SignInManager.SignInAsync(user,isPersistent:false,rememberBrowser:false);


返回RedirectToAction(Index,Home);
}
AddErrors(result);
}
catch(Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}

}
return View(model);
}
}


解决方案

对我的懊恼,我不得不与上面的选项2一起去。我更改了我的连接字符串,就像一个常规的连接字符串(见上文),然后必须重写一些我以前没有处理的UserManager方法(例如 manager.GetRolesAsync() manager.CreateIdentityAsync )。我现在可以注册,重置密码等。


So I'm building an application on top of an existing database to which I have limited rights to change (the idea is to make as few changes to the DB schema as possible). This is an MVC 5 application attempting to use the Identity system with a custom user store to a MySQL database.

Problem: I'm getting the following exception when attempting to register a user via the auto-generated AccountController in the register method:

IdentityResult result = await UserManager.CreateAsync(user, model.Password); 

System.InvalidOperationException: The entity type ApplicationUser is not part of the model for the current context. at System.Data.Entity.Internal.InternalContext.UpdateEntitySetMappingsForType(Type entityType) at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) at System.Data.Entity.Internal.Linq.InternalSet1.Initialize() at System.Data.Entity.Internal.Linq.InternalSet1.get_InternalContext() at System.Data.Entity.Infrastructure.DbQuery1.System.Linq.IQueryable.get_Provider() at System.Data.Entity.QueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable1 source, Expression1 predicate, CancellationToken cancellationToken) at System.Data.Entity.QueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable1 source, Expression1 predicate) at Microsoft.AspNet.Identity.EntityFramework.UserStore6.d__6c.MoveNext()

What I've tried:

  1. I've tried changing the Account controller's UserManager instantiation from HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();to new ApplicationUserManager(new UserStoreService(new Entities())); This solves the immediate problem and lets me register. HOWEVER, this causes problems later on when I try to reset a password and I get another issue that I haven't been able to solve where I get an invalid user token (although I can confirm that the user tokens work correctly when I use the HttpContext.GetOwinContext... version of the UserManager
  2. There are several posts regarding changing the connection string from the autogenerated one like this:

<add name="Entities" connectionString="metadata=res://*/Models.tools.csdl|res://*/Models.tools.ssdl|res://*/Models.tools.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot;server=localhost;user id=user;password=***;persistsecurityinfo=True;database=db&quot;" providerName="System.Data.EntityClient" />

to a normal connection string like this: <add name="Entities" connectionString="server=localhost;user id=user;password=***;persistsecurityinfo=True;database=db" providerName="MySql.Data.MySqlClient" />. This explodes pretty quickly with the unintentional code first exception. And then a few other problems on down the road that seemed to spiral (after fixing issues with no declared Keys on the tables, etc.).I am open to suggestions on this, but would prefer to not have to go this route.

Below is the code relevant to the setup. Any ideas what else I could be missing here? Or is the only way to solve this with the connection string idea?

Thanks!

Setup I'm using database first (EDMX) to an existing MySQL database. Following along with this to change the primary key for users to an int, I have a custom userstore service to attach to my database and user table.

DbContext setup (I have modified from the autogenerated file in attempt to work with the Identity system):

public partial class Entities : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>// DbContext
{
    public Entities()
        : base("name=Entities")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public static Entities Create()
    {
        return new Entities();
    }

    //DbSets are here
}

ApplicationUser.cs:

public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim> // IUser<int>//IdentityUser
{
    //custom properties are here

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
    {

        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here            

        return userIdentity;
    }
}

ApplicationUserManager.cs

public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
    public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
        : base(store)
    { }

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new UserStoreService(context.Get<Entities>()));

        // Configure validation logic for usernames
        manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

        // Configure validation logic for passwords
        manager.PasswordValidator = new PasswordValidator
        {
            RequiredLength = 6,
            RequireNonLetterOrDigit = true,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
        };

        manager.UserLockoutEnabledByDefault = true;
        manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
        manager.MaxFailedAccessAttemptsBeforeLockout = 5;

        manager.EmailService = new EmailService();

        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            manager.UserTokenProvider =
                new DataProtectorTokenProvider<ApplicationUser, int>(dataProtectionProvider.Create("ASP.NET Identity")) { TokenLifespan = TimeSpan.FromHours(24) };
        }
        return manager;
    }
}

UserStoreService.cs

 public class UserStoreService : UserStore<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim> //, IUserStore<ApplicationUser, int>, IUserPasswordStore<ApplicationUser, int>, IUserEmailStore<ApplicationUser, int>, IUserLockoutStore<ApplicationUser, int>, IUserSecurityStampStore<ApplicationUser, int>
{
    private Entities _db; // = new Entities();

    public UserStoreService(Entities db) : base(db)
    {
        _db = db;
    }

    public override Task CreateAsync(ApplicationUser user)
    {
        var profile = new ffs_profile {
            //set props here
        };

        _db.ffs_profile.Add(profile);
        return _db.SaveChangesAsync();
    }

    public async override Task<ApplicationUser> FindByNameAsync(string userName)
    {
        var profile = await _db.ffs_profile.Where(u => u.email == userName).FirstOrDefaultAsync();

        ApplicationUser user = null;
        if (profile != null)
            user = ToApplicationUser(profile);

        return user;
    }

    private ApplicationUser ToApplicationUser(ffs_profile profile)
    {
        return new ApplicationUser
        {
           //set properties here

        };
    }
    public override Task<string> GetPasswordHashAsync(ApplicationUser user)
    {
        if (user == null)
        {
            throw new ArgumentException("null user");
        }

        return Task.FromResult(user.PasswordHash);
    }

    public override Task<bool> HasPasswordAsync(ApplicationUser user)
    {
        return Task.FromResult(user.PasswordHash != null);
    }

    public override Task SetPasswordHashAsync(ApplicationUser user, string passwordHash)
    {
        return Task.Run(() =>
        {
            if (passwordHash == null)
                throw new ArgumentNullException("passwordHash");
            if (string.IsNullOrWhiteSpace(passwordHash))
                throw new ArgumentException("passwordHash cannot be null, empty, or consist of whitespace.");
             user.PasswordHash = passwordHash;

        });
    }

    public override async Task<ApplicationUser> FindByIdAsync(int userId)
    {
        var profile = await _db.ffs_profile.Where(u => u.profile_id == userId).FirstOrDefaultAsync();

        ApplicationUser user = null;
        if (profile != null)
            user = ToApplicationUser(profile);

        return user;
    }

    public override Task<string> GetSecurityStampAsync(ApplicationUser user)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }
        return Task.FromResult<string>(user.SecurityStamp);
    }
    public override Task SetSecurityStampAsync(ApplicationUser user, string stamp)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }
        user.SecurityStamp = stamp;
        return Task.FromResult<int>(0);
    }
}

And finally, the relevant portions of the account controller:

 public class AccountController : Controller
{
    private ApplicationSignInManager _signInManager;
    private ApplicationUserManager _userManager;

    public AccountController()
    {
    }

    public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
    {
        UserManager = userManager;
        SignInManager = signInManager;
    }

    public ApplicationUserManager UserManager
    {
        get
        {   
            return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); 
        }
        private set
        {
            _userManager = value;
        }
    }
// other autogenerated methods
[HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = new ApplicationUser
                {
                //set props here
            };
            try
            {
                IdentityResult result = await UserManager.CreateAsync(user, model.Password); 
                if (result.Succeeded)
                {

                    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);


                    return RedirectToAction("Index", "Home");
                }
                AddErrors(result);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }

        }
        return View(model);
    }
}

解决方案

Much to my chagrin, I had to go with option 2 above. I changed my connection string to be like a regular connection string (see above) and then had to consequently override a number of UserManager methods that I wasn't dealing with before (such as manager.GetRolesAsync() and manager.CreateIdentityAsync). I'm now able to register, reset passwords, etc.

这篇关于实体类型ApplicationUser不是当前上下文的模型的一部分,DB First具有自定义用户存储的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆