18

私は小さな Web アプリに取り組んでおり、データベースの決定を開始する必要がある開発のポイントに到達しました。私の当初の計画は、Azure 上の MSSQL で EF Code First を使用することでした。しかし、Azure でのデータベース ホスティング機能を調べているときに、NoSQL の世界を開いた Azure Table Storage を発見しました。

インターネットでは NoSQL の機能についての話題が盛り上がっていますが、私が収集した最大の理由の 1 つは、NoSQL がデータをさまざまなテーブルに分割することなく、オブジェクト全体をデータベースに 1 つとして格納するため、パフォーマンスが向上することです。これは魅力的に聞こえますが、EF Code First は、開発者がクエリについて心配する必要なく、オブジェクトを自動的にまとめて SQL データベースに分割することにより、この問題を効果的に排除しました。

ただし、私の主な問題は、NoSQL データベースで EF Code First や ASP.NET Identity などを使用するためのドキュメントが見つからないことです。私のアプリは現在 Identity を使用しているため、他のアプリに切り替える必要はありません。

Q: Azure Tables で Code First や Identity を使用することはできますか?


編集: 私のアプリについて少し極端に単純化すると、私のアプリでは、事前構成されたタイプのデータを組み合わせて一致させることにより、ユーザーがカスタム プロファイルを作成できます。たとえば、ユーザーは自分のプロファイルに任意の数の Quote オブジェクトを追加してから、引用の値を定義できます (つまり、「自分らしく。他のすべての人は既に使用されています。」)。または、Movie オブジェクトを使用して、お気に入りの映画のコレクションを定義することもできます (つまり、"タイトル: インセプション、年: 2010")。平均して、ユーザーは自分のページに 50 以上のそのようなプロパティを持つことができます。持つことができるプロパティの数に制限はありません。

この例を使用すると、Code First を使用して実装する方法を簡単に確認できます (Profile には Quote オブジェクトのリストと Movie オブジェクトのリストがあります)。これが Azure Tables などの NoSQL データベースにどのようにマップされるかはまだわかりません。そのため、私のアプリのニーズでは、Code First から NoSQL に切り替えることが、失われる機能と機能を考慮して合理的な決定であるかどうかはわかりません。

4

4 に答える 4

19

そのため、AzureTable ストレージを UserStore の SQL なしの実装として使用して、まさにこのシナリオを対象としたサンプルを用意します。基本的に、Azure Storage 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);
    }
}
于 2013-10-19T00:15:46.667 に答える
5

現実的には、Azure Table Storage で EF Code First を使用することはできません。そうは言っても、テーブル ストレージの操作は通常、最初にコードを作成する同様のアプローチを使用して行われます。つまり、クラスを作成すると、その場でテーブルが作成されます。

テーブルストレージには関係などがないことに注意してください。テーブル ストレージは、複雑なオブジェクトを単一のテーブル "行" に格納できないという点で、他の NoSQL ソリューションよりもさらに単純です。

おそらく、テーブルやブロブ ストレージのみを使用する .net ID プロバイダーを作成できますが、そこに例が見つかりません。以前は codeplex プロジェクトがあったはずですが、今は見つかりません。

Gert Arnold が意味することは、SQL Azure と Table Storage の両方を使用することです (EF は SQL Azure 部分のみ)。このようにして、それぞれが最も得意とするものに使用できます-単純に構造化されたデータを大量に格納するテーブルストレージ、より複雑なデータの部分に使用するSQL azure (つまり、リレーションシップが必要)

于 2013-10-18T08:16:32.423 に答える
1

今後の参考のために。Identity と Azure Table Storage を使用する github プロジェクトがあります。 James RandallのAccidental Fish . 役割がすでに実装されているかどうかはわかりません。

于 2014-08-18T06:45:09.820 に答える
0

最新のエンティティ フレームワーク コアでは、EF を使用して Azure ストレージ テーブルに接続できるようになりました: EntityFramework.AzureTableStorage 7.0.0-beta1

Dbcontext を構成する場合は、私の投稿を参照してください。

UserManagerそれを使用して、クラスを実装できます。

于 2016-02-02T01:30:11.800 に答える