The following code does not work, and I can't explain why... My user manager is causing significant distress in that it creates users and roles just fine but when I run this code userManager.IsInRole is always returning false, so the second time I run my seed I am hitting errors because it is trying to create the record despite the fact it already exists!
Please note that this is occurring when I am running update-database against my migrations project, is the fact this is a non ASP project causing issues, if so why? shouldn't an error be thrown.
This is the first project I have used Identity and although when it works it seems good, there is very little up to date good quality documentation available, so if anyone has any sources for this I would be grateful.
public void Run(BlogContext blogContext)
{
var userStore = new UserStore<User>((BlogContext) blogContext);
var userManager = new UserManager<User>(userStore);
var userRoles = new List<UserRole>()
{
new UserRole() {Username = "SysAdmin#test.com", Role = "SysAdmin"},
new UserRole() {Username = "testAdmin#test.com", Role = "Admin"},
new UserRole() {Username = "testAuthor#test.com", Role = "Author"}
};
foreach (var userRole in userRoles)
{
var userId = userManager.FindByName(userRole.Username).Id;
if (!userManager.IsInRole(userId, userRole.Role))
userManager.AddToRole(userId, userRole.Role);
}
blogContext.SaveChanges();
}
So I will answer this myself to save anyone the hours of pain I suffered because of this.
The reason for this occurring was that I had lazy loading disabled, I have enabled this to be on in my Migrations project like so.
protected override void Seed(BlogContext blogContext)
{
AutomaticMigrationsEnabled = true;
blogContext.Configuration.LazyLoadingEnabled = true;
//Add seed classes here!
}
Related
Microsoft Dynamics CRM 2015.
I test Asp.Net Core controller's action. When I create new Lead record some plugin generates new Guid for lead.new_master_id field (it's type is string). Therefore after creating I retrive the record to get it's generated new_master_id value. How can I emulate this plugin behaviour through Fake Xrm Easy?
var fakedContext = new XrmFakedContext();
fakedContext.ProxyTypesAssembly = typeof(Lead).Assembly;
var entities = new Entity[]
{
// is empty array
};
fakedContext.Initialize(entities);
var orgService = fakedContext.GetOrganizationService();
var lead = new Lead { FirstName = "James", LastName = "Bond" };
var leadId = orgService.Create(lead);
var masterId = orgService.Retrieve(Lead.EntityLogicalName, leadId,
new Microsoft.Xrm.Sdk.Query.ColumnSet(Lead.Fields.new_master_id))
.ToEntity<Lead>().new_master_id;
In v1.x of FakeXrmEasy you'll need to enable PipelineSimulation and register the plugin steps you would like to be fired on Create manually by registering their steps.
fakedContext.UsePipelineSimulation = true;
Once enabled, you'll need to enable the necessary steps via calling RegisterPluginStep. In your example you'll need to at least register something along the lines of:
fakedContext.RegisterPluginStep<LeadPlugin>("Create", ProcessingStepStage.Preoperation);
Where LeadPlugin would be the name of your plugin that generates the new_master_id property.
Keep in mind v1.x is limited in that it supports pipeline simulation for basic CRUD requests only.
Later versions (2.x and/or 3.x) come with a brand new middleware implementation allowing registering plugin steps for any message. Soon we'll be implementing automatic registration of plugin steps based on an actual environment and/or custom attributes.
Here's an example using the new middleware
public class FakeXrmEasyTestsBase
{
protected readonly IXrmFakedContext _context;
protected readonly IOrganizationServiceAsync2 _service;
public FakeXrmEasyTestsBase()
{
_context = MiddlewareBuilder
.New()
.AddCrud()
.AddFakeMessageExecutors()
.AddPipelineSimulation()
.UsePipelineSimulation()
.UseCrud()
.UseMessages()
.Build();
_service = _context.GetAsyncOrganizationService2();
}
}
You can find more info on the QuickStart guide here
Disclaimer: I'm the author of FakeXrmEasy :)
I am uploading a excel file containing all required users into my website using ASP.NET Identity and OwinContext and EF 6.
My code looks like below:
foreach (var bulkUserDetail in bulkUser.BulkUserDetails)
{
var userManager = owinContext.GetUserManager<ApplicationUserManager>();
var userProfile = new UserProfile();
userProfile.Username = bulkUserDetail.Username;
AspNetUser newUser = new AspNetUser
{
UserName = userProfile.Username,
Email = bulkUserDetail.Email,
LastPasswordChangedDate = null,
};
var creationResult = userManager.Create(newUser);
if (creationResult.Succeeded)
{
string token = userManager.GeneratePasswordResetToken(newUser.Id);
}
}
The issue is that the performance of following two lines is pretty disappointing
userManager.Create(newUser) -- (900 milliseconds)
userManager.GeneratePasswordResetToken(newUser.Id) --(1800 milliseconds)
In large quantity, i.e 2000 users, the performance become a serious issue.
Is there better a practice to speed up this process? I am open to suggestions but I have to keep the OwinContext library.
Thanks in advance
You could try doing the user creation inside a parallel for which might speed up the overall time, however there is an issue with this:
The call to Create and GeneratePasswordResetToken are slow because they call the database
Doing the work in parallel would increase the number of concurrent calls to the database potentially slowing it down even more, this really depends on how good the hardware hosting your database is.
var userManager = owinContext.GetUserManager<ApplicationUserManager>();
Parallel.ForEach (bulkUser.BulkUserDetails, bulkUserDetail =>
{
//Do you really need to make this userProfile as its not used
var userProfile = new UserProfile();
userProfile.Username = bulkUserDetail.Username;
AspNetUser newUser = new AspNetUser
{
UserName = userProfile.Username,
Email = bulkUserDetail.Email,
LastPasswordChangedDate = null,
};
var creationResult = userManager.Create(newUser);
if (creationResult.Succeeded)
{
string token = userManager.GeneratePasswordResetToken(newUser.Id);
}
})
I had created my project and configured the user entities.
I had enabled migrations in my project. To test this I simple added a user and then logged into the ASP NET application through the browser. It all worked great.
I added a couple more entities and their corresponding DbSet<T>. I created a controller to manage these entities.
I went to Update-Database using this seed method:
var manager = new UserManager<ChevieUser>(new UserStore<ChevieUser>(new DefaultContext()));
var people = new List<ChevieUser>
{
new ChevieUser { FirstName = "blah", LastName = "blah", UserName="cliningt", Email="blah.blah#blah.co.uk" }
};
people.ForEach(x => manager.Create(x, "password"));
context.SaveChanges();
var projects = new List<Project>
{
new Project { Name= "MLounge", ProposedDuration = DateTime.Now.AddDays(14), Users = new List<ChevieUser>(){context.Users.FirstOrDefault()} }
};
projects.ForEach(x => context.Projects.AddOrUpdate(p => p.Name, x));
context.SaveChanges();
//var targets = new List<Target>
//{
// new Target {Name="Initial Meeting", CompletionDate = DateTime.Now.AddDays(1), ProjectId = context.Projects.FirstOrDefault().Id},
// new Target {Name="Pricing", CompletionDate = DateTime.Now.AddDays(2), ProjectId = context.Projects.FirstOrDefault().Id},
// new Target {Name="Layout", CompletionDate = DateTime.Now.AddDays(3), ProjectId = context.Projects.FirstOrDefault().Id}
//};
//targets.ForEach(x => context.Targets.AddOrUpdate(p => p.Name, x));
//context.SaveChanges();
It created a project, however, the project had an Id of a empty Guid. Even though I am using the same code to for the Id property as I have done with every other project.
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
The reason why the targets code is commented out is that it can't add three targets with the same Id because the above code isn't working properly.
So I thought I would update my MVC project with the latest nuget packages. This was successful. However, when I came to Update-Database there was an error that migrations was not enabled in my project!
Then after trying to Enable-Migrations it came back with an error that there is no context!
From everything working perfectly fine, to the whole project messing up like this is so bizarre. Out shot of it is, migrations seems to have magically disable itself, but even when it was working, the DatabaseGenerated(DatabaseGeneratedOption.Identity) wasn't working properly.
It sounds like EF is looking in the wrong project.
Make sure the "Default Project" dropdown in your Package Manager Console window is set to the project that contains your DbContext class. This tells the package manager console which project to execute commands against.
We are having an issue with searching a custom record through SuiteTalk. Below is a sample of what we are calling. The issue we are having is in trying to set up the search using the internalId of the record. The issue here lies in in our initial development account the internal id of this custom record is 482 but when we deployed it through the our bundle the record was assigned with the internal Id of 314. It would stand to reason that this internal id is not static in a site per site install so we wondered what property to set up to reference the custom record. When we made the record we assigned its “scriptId’ to be 'customrecord_myCustomRecord' but through suitetalk we do not have a “scriptId”. What is the best way for us to allow for this code to work in all environments and not a specific one? And if so, could you give an example of how it might be used.
Code (C#) that we are attempting to make the call from. We are using the 2013.2 endpoints at this time.
private SearchResult NetSuite_getPackageContentsCustomRecord(string sParentRef)
{
List<object> PackageSearchResults = new List<object>();
CustomRecord custRec = new CustomRecord();
CustomRecordSearch customRecordSearch = new CustomRecordSearch();
SearchMultiSelectCustomField searchFilter1 = new SearchMultiSelectCustomField();
searchFilter1.internalId = "customrecord_myCustomRecord_sublist";
searchFilter1.#operator = SearchMultiSelectFieldOperator.anyOf;
searchFilter1.operatorSpecified = true;
ListOrRecordRef lRecordRef = new ListOrRecordRef();
lRecordRef.internalId = sParentRef;
searchFilter1.searchValue = new ListOrRecordRef[] { lRecordRef };
CustomRecordSearchBasic customRecordBasic = new CustomRecordSearchBasic();
customRecordBasic.recType = new RecordRef();
customRecordBasic.recType.internalId = "314"; // "482"; //THIS LINE IS GIVING US THE TROUBLE
//customRecordBasic.recType.name = "customrecord_myCustomRecord";
customRecordBasic.customFieldList = new SearchCustomField[] { searchFilter1 };
customRecordSearch.basic = customRecordBasic;
// Search for the customer entity
SearchResult results = _service.search(customRecordSearch);
return results;
}
I searched all over for a solution to avoid hardcoding internalId's. Even NetSuite support failed to give me a solution. Finally I stumbled upon a solution in NetSuite's knowledgebase, getCustomizationId.
This returns the internalId, scriptId and name for all customRecord's (or customRecordType's in NetSuite terms! Which is what made it hard to find.)
public string GetCustomizationId(string scriptId)
{
// Perform getCustomizationId on custom record type
CustomizationType ct = new CustomizationType();
ct.getCustomizationTypeSpecified = true;
ct.getCustomizationType = GetCustomizationType.customRecordType;
// Retrieve active custom record type IDs. The includeInactives param is set to false.
GetCustomizationIdResult getCustIdResult = _service.getCustomizationId(ct, false);
foreach (var customizationRef in getCustIdResult.customizationRefList)
{
if (customizationRef.scriptId == scriptId) return customizationRef.internalId;
}
return null;
}
you can make the internalid as an external property so that you can change it according to environment.
The internalId will be changed only when you install first time into an environment. when you deploy it into that environment, the internalid will not change with the future deployments unless you choose Add/Rename option during deployment.
I added a custom field to the UserProfile table named ClassOfYear and I'm able to get the data into the profile during registration like this:
var confirmationToken = WebSecurity.CreateUserAndAccount(model.UserName,
model.Password,
propertyValues: new { ClassOfYear = model.ClassOfYear },
requireConfirmationToken: true);
However, now I want to be able to update the profile when I manage it but I can't seem to find a method to do so. Do I need to simply update the UserProfile table myself? If not, what is the appropriate way of doing this?
FYI, I'm using Dapper as my data access layer, just in case it matters. But, like stated, I can just update the UserProfile table via Dapper if that's what I'm supposed to do, I just figured that the WebSecurity class, or something similar, had a way already since the custom user profile fields are integrated with the CreateUserAndAccount method.
Thanks all!
There is nothing in the SimpleMembershipProvider code that does anything with additional fields except upon create.
Simply query the values yourself from your ORM.
You can use the WebSecurity.GetUserId(User.Identity.Name) to get the user's id and then Dapper to query the UserProfile table.
Just in case anyone facing the same problem. After fighting a lot with the SimpleMembership I got a solution that populates both the webpages_Membership and my custom Users table. For clarification follow my code:
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
TUsuario userDTO= new TUSer()
{
Name = model.Name,
Login = model.Login,
Pass = model.Pass.ToString(CultureInfo.InvariantCulture),
Active = true,
IdCompany = model.IdCompany,
IdUserGroup = model.IdUserGroup,
};
try
{
WebSecurity.CreateUserAndAccount(model.Login, model.Pass, new { IdUser = new UserDAL().Seq.NextVal(), Name = userDTO.Name, Login = userDTO.Login, Active = userDTO.Active, Pass = userDTO.Pass, IdCompany = userDTO.IdCompany, IdUserGroup = userDTO.IdUserGroup });
WebSecurity.Login(model.Login, model.Pass);
After cursing the framework a lot, that gave me a bliss of fresh air :)
PS.: The users table is specified in the global.asax file using the WebSecurity.InitializeDatabaseConnection functon.