14

我正在開發一個小型的網絡應用程序,並且我剛開始着手開發需要開始制定數據庫決策的時間點。我最初的計劃是在Azure上將EF Code First與MSSQL結合使用,因爲它簡化了使用數據庫的過程。但是,在調查Azure上的數據庫託管功能時,我發現了Azure Table Storage,它爲我打開了NoSQL的世界。用Azure表存儲代碼優先和身份驗證

雖然互聯網上充斥着關於NoSQL特性的喋喋不休,但是我設法收集的最大原因之一是NoSQL將整個對象作爲一個存儲在數據庫中,而不會將數據分解成各種表格,這對於性能。雖然這聽起來很吸引人,但是EF Code First通過自動將對象拉到一起並將對象分離到SQL數據庫中而無需開發人員擔心查詢,從而有效地消除了此問題。

我的主要問題,但問題在於,我無法找到任何文件使用的東西像EF代碼第一次和ASP.NET身份與NoSQL數據庫。由於我的應用程序當前使用Identity,因此我想避免切換到其他位置。

問:是否可以使用代碼第一次和/或身份與Azure的表?


編輯:我的應用程序作爲一個極度簡化了一點點,我的應用程序可以讓我的用戶通過混合和匹配預配置類型的數據創建自定義配置文件。例如,用戶可以將任意數量的Quote對象添加到他們的配置文件中,然後定義該報價的值(即「做你自己;其他人已經被拿走了。」)。或者他們可以使用電影對象來定義他們喜歡的電影的集合(即「標題:入門,年份:2010」)。平均而言,用戶可以在其頁面上輕鬆獲得50個或更多此類屬性;他們可以擁有的房產數量沒有限制。使用

這個例子中,我可以很容易地看到我怎麼會去使用的Code First(資料報價有對象的列表和電影對象的列表)實現它。我還不確定這將如何映射到Azure Tables等NoSQL數據庫。所以,根據我的應用程序的需求,我不確定是否從Code First切換到NoSQL是否會導致我失去的功能和功能的合理決策。

+0

EF是一個對象_relational_ mapper。但爲什麼不同時使用? –

+0

我很喜歡,但因爲我找不到有關如何使用EF與表的任何文檔,我目前正在考慮使用EF /身份與SQL進行身份驗證和表存儲我的數據,但我目前不當然,如果這增加了複雜性是值得的。 – Dragonseer

回答

16

因此,我們將有一個針對此場景的示例,使用AzureTable存儲作爲UserStore的no sql實現。基本上你使用Azure存儲API來實現一個IUserStore。下面是一個實現登錄/密碼方法的基本實現,但不是一切:

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<string>(); 
     Claims = new List<Claim>(); 
     Logins = new List<AzureLogin>(); 
    } 

    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<string> Roles { get; set; } 
    public IList<AzureLogin> Logins { get; set; } 
    public IList<Claim> Claims { get; set; } 
} 

public static class Constants { 
    public const string IdentityPartitionKey = "ASP.NET Identity"; 
} 

public class AzureStore : IUserStore<AzureUser>, IUserClaimStore<AzureUser>, IUserLoginStore<AzureUser>, IUserRoleStore<AzureUser>, IUserPasswordStore<AzureUser> { 
    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<IList<Claim>> 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<AzureUser>.CreateAsync(AzureUser user) { 
     TableOperation op = TableOperation.Insert(user); 
     var result = Table.Execute(op); 
     return Task.FromResult(0); 
    } 

    Task IUserStore<AzureUser>.UpdateAsync(AzureUser user) { 
     TableOperation op = TableOperation.Replace(user); 
     var result = Table.Execute(op); 
     return Task.FromResult(0); 
    } 

    public Task<AzureUser> FindByIdAsync(string userId) { 
     TableOperation op = TableOperation.Retrieve<AzureUser>(Constants.IdentityPartitionKey, userId); 
     var result = Table.Execute(op); 
     return Task.FromResult<AzureUser>(result.Result as AzureUser); 
    } 

    public Task<AzureUser> FindByNameAsync(string userName) { 
     TableQuery<AzureUser> query = new TableQuery<AzureUser>().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<IList<UserLoginInfo>> GetLoginsAsync(AzureUser user) { 
     TableQuery<AzureLogin> query = new TableQuery<AzureLogin>() 
      .Where(TableQuery.GenerateFilterCondition("UserId", QueryComparisons.Equal, user.Id)) 
      .Select(new string[] { "LoginProvider", "ProviderKey" }); 
     var results = Table.ExecuteQuery(query); 
     IList<UserLoginInfo> logins = new List<UserLoginInfo>(); 
     foreach (var al in results) { 
      logins.Add(new UserLoginInfo(al.LoginProvider, al.ProviderKey)); 
     } 
     return Task.FromResult(logins); 
    } 

    private AzureLogin Find(UserLoginInfo login) { 
     TableQuery<AzureLogin> query = new TableQuery<AzureLogin>() 
      .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<AzureUser> FindAsync(UserLoginInfo login) { 
     var al = Find(login); 
     if (al != null) { 
      return FindByIdAsync(al.UserId); 
     } 
     return Task.FromResult<AzureUser>(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<IList<string>> GetRolesAsync(AzureUser user) { 
     return Task.FromResult(user.Roles); 
    } 

    public Task<bool> IsInRoleAsync(AzureUser user, string role) { 
     return Task.FromResult(false); 
    } 


    public Task DeleteAsync(AzureUser user) { 
     throw new NotImplementedException(); 
    } 

    public Task<string> GetPasswordHashAsync(AzureUser user) { 
     return Task.FromResult(user.PasswordHash); 
    } 

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

    public Task SetPasswordHashAsync(AzureUser user, string passwordHash) { 
     user.PasswordHash = passwordHash; 
     return Task.FromResult(0); 
    } 
} 
+2

謝謝!微軟官方的實施將會非常棒。這會在11月13日發佈VS2013嗎? – Dragonseer

+0

嘿,近兩年了,我沒有發現Azure表的角色存儲和用戶存儲的任何非正式實現 –

+0

Azure表存儲不允許集合...你是序列化它們還是什麼? –

5

實際上,您無法將EF Code First與Azure表存儲配合使用。說,使用表存儲通常是使用類似的方法來首先編寫代碼 - 即創建類,並且它們可以即時創建表。

請注意,與表存儲沒有關係之類的東西。表存儲比其他NoSQL解決方案更簡單,因爲您無法將複雜對象存儲在單個表「行」中。

您可能可以創建一個僅使用表格和/或blob存儲的.net標識提供程序,但我無法找到任何示例 - 我確定曾經有一個codeplex項目,但現在無法找到它。

格特·阿諾德的意思是使用SQL Azure和表存儲(僅與SQL Azure的部分EF)。這樣,您就可以使用每個什麼他們最擅長的 - 在存儲大量簡單的結構化數據的表存儲,對數據的更復雜的零件SQL Azure的(即需要關係)

0

與最新的實體框架的核心,你可以使用EF現在連接到Azure存儲表:EntityFramework.AzureTableStorage 7.0.0-beta1

見我,如果你想配置你的DbContext post

使用它,你可以實現你的UserManager類。

+2

EF表存儲提供程序目前處於暫掛狀態:http://stackoverflow.com/a/35188991/310446 – BenV

+0

EF Core團隊不會執行Azure表存儲提供程序:*關閉此功能,因爲我們正在集中精力Cosmos DB(最初是作爲文件存儲)而不是ATS。我們不打算讓EF團隊實施和維護ATS提供商,但我們會完全支持社區的努力。*從[Azure表存儲提供商](https://github.com/aspnet/EntityFrameworkCore/issues/1142#issuecomment-366396069)GitHub問題。 – 0xced

+0

@ 0xced好點。這也是一個老帖子。你應該寫一個關於這個的答案 – Thomas