I\'m working on a small web app and I\'ve just hit the point in development where I need to start making database decisions. My original plan was to go EF Code First with MSSQL
So we will have a sample targeting exactly this scenario, using AzureTable storage as a no sql implementation of a UserStore. Basically you implement an IUserStore using the Azure Storage APIs. Here's a basic implementation that implements the login/password methods, but not everything:
public class AzureRole : TableEntity, IRole {
public string Id { get; set; }
public string Name { get; set; }
}
public class AzureLogin : TableEntity {
public AzureLogin() {
PartitionKey = Constants.IdentityPartitionKey;
RowKey = Guid.NewGuid().ToString();
}
public AzureLogin(string ownerId, UserLoginInfo info) : this() {
UserId = ownerId;
LoginProvider = info.LoginProvider;
ProviderKey = info.ProviderKey;
}
public string UserId { get; set; }
public string ProviderKey { get; set; }
public string LoginProvider { get; set; }
}
public class AzureUser : TableEntity, IUser {
public AzureUser() {
PartitionKey = Constants.IdentityPartitionKey;
RowKey = Guid.NewGuid().ToString();
Id = RowKey;
Roles = new List();
Claims = new List();
Logins = new List();
}
public AzureUser(string userName) : this() {
UserName = userName;
}
public string Id { get; set; }
public string UserName { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public IList Roles { get; set; }
public IList Logins { get; set; }
public IList Claims { get; set; }
}
public static class Constants {
public const string IdentityPartitionKey = "ASP.NET Identity";
}
public class AzureStore : IUserStore, IUserClaimStore, IUserLoginStore, IUserRoleStore, IUserPasswordStore {
public AzureStore() {
// Retrieve the storage account from the connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
// CreateAsync the table client.
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
// CreateAsync the table if it doesn't exist.
CloudTable table = tableClient.GetTableReference("Identity");
table.CreateIfNotExists();
Table = table;
BatchOperation = new TableBatchOperation();
}
public TableBatchOperation BatchOperation { get; set; }
public CloudTable Table { get; set; }
public void Dispose() {
}
public Task> GetClaimsAsync(AzureUser user) {
return Task.FromResult(user.Claims);
}
public Task AddClaimAsync(AzureUser user, System.Security.Claims.Claim claim) {
return Task.FromResult(0);
}
public Task RemoveClaimAsync(AzureUser user, System.Security.Claims.Claim claim) {
return Task.FromResult(0);
}
Task IUserStore.CreateAsync(AzureUser user) {
TableOperation op = TableOperation.Insert(user);
var result = Table.Execute(op);
return Task.FromResult(0);
}
Task IUserStore.UpdateAsync(AzureUser user) {
TableOperation op = TableOperation.Replace(user);
var result = Table.Execute(op);
return Task.FromResult(0);
}
public Task FindByIdAsync(string userId) {
TableOperation op = TableOperation.Retrieve(Constants.IdentityPartitionKey, userId);
var result = Table.Execute(op);
return Task.FromResult(result.Result as AzureUser);
}
public Task FindByNameAsync(string userName) {
TableQuery query = new TableQuery().Where(TableQuery.GenerateFilterCondition("UserName", QueryComparisons.Equal, userName));
return Task.FromResult(Table.ExecuteQuery(query).FirstOrDefault());
}
public Task AddLoginAsync(AzureUser user, UserLoginInfo login) {
TableOperation op = TableOperation.Insert(new AzureLogin(user.Id, login));
var result = Table.Execute(op);
return Task.FromResult(0);
}
public Task RemoveLoginAsync(AzureUser user, UserLoginInfo login) {
var al = Find(login);
if (al != null) {
TableOperation op = TableOperation.Delete(al);
var result = Table.Execute(op);
}
return Task.FromResult(0);
}
public Task> GetLoginsAsync(AzureUser user) {
TableQuery query = new TableQuery()
.Where(TableQuery.GenerateFilterCondition("UserId", QueryComparisons.Equal, user.Id))
.Select(new string[] { "LoginProvider", "ProviderKey" });
var results = Table.ExecuteQuery(query);
IList logins = new List();
foreach (var al in results) {
logins.Add(new UserLoginInfo(al.LoginProvider, al.ProviderKey));
}
return Task.FromResult(logins);
}
private AzureLogin Find(UserLoginInfo login) {
TableQuery query = new TableQuery()
.Where(TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("LoginProvider", QueryComparisons.Equal, login.LoginProvider),
TableOperators.And,
TableQuery.GenerateFilterCondition("ProviderKey", QueryComparisons.Equal, login.ProviderKey)))
.Select(new string[] { "UserId" });
return Table.ExecuteQuery(query).FirstOrDefault();
}
public Task FindAsync(UserLoginInfo login) {
var al = Find(login);
if (al != null) {
return FindByIdAsync(al.UserId);
}
return Task.FromResult(null);
}
public Task AddToRoleAsync(AzureUser user, string role) {
return Task.FromResult(0);
}
public Task RemoveFromRoleAsync(AzureUser user, string role) {
return Task.FromResult(0);
}
public Task> GetRolesAsync(AzureUser user) {
return Task.FromResult(user.Roles);
}
public Task IsInRoleAsync(AzureUser user, string role) {
return Task.FromResult(false);
}
public Task DeleteAsync(AzureUser user) {
throw new NotImplementedException();
}
public Task GetPasswordHashAsync(AzureUser user) {
return Task.FromResult(user.PasswordHash);
}
public Task HasPasswordAsync(AzureUser user) {
return Task.FromResult(user.PasswordHash != null);
}
public Task SetPasswordHashAsync(AzureUser user, string passwordHash) {
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
}