2

I work on a MVC 5 project with EF6.1. As a beginner, I try to keep the code generated by the MVC 5 template in order to use the default authentification system with AccountController. However, I had to add some classes, like "User.cs" as below, so I'm using Entity Framework Data Model.

public partial class User
{
    public User()
    {
        this.Logs= new HashSet<Log>();
    }

    public int IdUser { get; set; }
    public string AspNetUsersId { get; set; } //References to AspNetUsers.Id
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Job{ get; set; }

    public virtual ICollection<Log> Logs { get; set; }
}

So, I have two contexts :

public partial class MyEFDataModelContainer : DbContext
{
    public MyEFDataModelContainer ()
        : base("name=MyEFDataModelContainer")
    {
    }

    public DbSet<User> Users { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
}

So, my scenario is that I need to create an AspNetUsers, then add a AspNetRoles to this AspNetUsers and then create a User. We suppose the AspNetUsers creation and AspNetRoles works, but if the User creation fails, how can I rollback the AspNetUsers and AspNetRoles?

Can I create a transaction with these two contexts?

Unfortunately, I also need to call WCF webservices to execute this task? What can I do?

Best regards,

Updated

My merged contexts:

public partial class User: IdentityUser { }
public partial class MyEFDataModelContainer : IdentityDbContext<User> 
{ 
public MyEFDataModelContainer() : base("name=MyEFDataModelContainer") 
{ }
}

My AccountController.cs:

public class AccountController : Controller
{
    public AccountController()
    {
       UserManager =new UserManager<User>(new UserStore<User>(new MyEFDataModelContainer()))
    }
    public UserManager<User> UserManager {get;set;}
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = new USER() { UserName = model.UserName, Email = model.Email, PhoneNumber = model.Phone };
            var result = await UserManager.CreateAsync(user, model.Password);
            var role = await UserManager.AddToRoleAsync(user.Id, "Membre");
            if (result.Succeeded)
            {
                using (UserBusiness business = new UserBusiness())
                {
                    var userResult = business.AddUser(model.FirstName, model.LastName, user.Id);
                }

                await SignInAsync(user, isPersistent: false);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                AddErrors(result);
            }
        }
        return View(model);
    }
}

My Web.config :

<connectionStrings>    
<add name="MyEFDataModelContainer" connectionString="metadata=res://*/MyEFDataModel.csdl|res://*/MyEFDataModel.ssdl|res://*/MyEFDataModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=localhost;initial catalog=DB;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>

But now, I have the error The specified type member ' UserName ' is not supported in LINQ to Entities . Only initializers , entity members , and entity navigation properties are supported . at the following line:

var result = await UserManager.CreateAsync(user, model.Password);
madhatterx
  • 53
  • 8

1 Answers1

1

So, my first question would be, why do you need two contexts? Is there a reason that the aspnet tables have to be separate from the business tables?

Could you do something like:

public partial class MyEFDataModelContainer : IdentityDbContext<User>
{
    public MyEFDataModelContainer ()
        : base("name=MyEFDataModelContainer")
    {
    }

    public DbSet<User> Users { get; set; }
}

Make sure to have your custom User class inherit from IdentityUser.

public partial class User : IdentityUser
{
    public User()
    {
        this.Logs= new HashSet<Log>();
    }

    public int IdUser { get; set; }
    public string AspNetUsersId { get; set; } //References to AspNetUsers.Id
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Job{ get; set; }

    public virtual ICollection<Log> Logs { get; set; }
}

Then, change your AccountController to this connection string. This way everything is in the same database.

public class AccountController : Controller
{
    public AccountController()
        : this(new UserManager<User>(
               new UserStore<User>(new MyEFDataModelContainer())))
    {
    }
}

Although, to answer your question, yes you can wrap access to two contexts inside a transaction. You will have to deal with MSDTC, however, which, in my experience, can sometimes be frustrating.

Wyatt Earp
  • 1,783
  • 13
  • 23
  • I tried to merge the two contexts, now I have:`public class ApplicationUser : IdentityUser { } public partial class MyEFDataModelContainer : IdentityDbContext { public MyEFDataModelContainer() : base("name=MyEFDataModelContainer") { }` but I have an error when I try to login : **The entity type ApplicationUser is not part of the model for the current context** Do I need to create entity in the Data Model? – madhatterx Jan 26 '15 at 14:47
  • @madhatterx Sorry, I think I missed a step in the answer. You want your custom User class to implement from IdentityUser, then pass your User class into the IdentityDbContext base class. – Wyatt Earp Jan 26 '15 at 15:12
  • is it possible to merge AspNetUsers table and my custom Users table? – madhatterx Jan 26 '15 at 17:19
  • @madhatterx, yes, that's what I'm trying to get at. If your User class inherits from IdentityUser, it should merge your custom stuff into the aspnet_users table. – Wyatt Earp Jan 26 '15 at 18:04
  • I have updated my context, my AccountController and I have the following error : **The specified type member ' UserName ' is not supported in LINQ to Entities . Only initializers , entity members , and entity navigation properties are supported** at the line `var result = await UserManager.CreateAsync(user, model.Password);` – madhatterx Jan 26 '15 at 20:58
  • I added the generated aspnet tables to my edmx, I create an association between User and AspNetRoles, AspNetLogins and AspNetClaims. I had to set the `base.UserName = username`, otherwise, it says **Name can not be null or empty**. But now, I have an **ambiguous match found** while creating the user :s – madhatterx Jan 27 '15 at 11:03
  • I also tried to inherite my User table with AspNetUsers, same ambiguous error. Do you have an example of what should be done in the edmx and the IdentityDbContext definition please? – madhatterx Jan 27 '15 at 13:28