Первый код и идентификация с табличным хранилищем - программирование
Подтвердить что ты не робот

Первый код и идентификация с табличным хранилищем

Я работаю над небольшим веб-приложением, и я только что нашел момент в разработке, где мне нужно начать принимать решения по базе данных. Мой первоначальный план состоял в том, чтобы сначала запустить EF Code с MSSQL на Azure, потому что он просто упрощает процесс работы с базой данных. Однако, исследуя возможности моего размещения базы данных на Azure, я обнаружил хранилище таблиц Azure, которое открыло мне мир NoSQL.

В то время как Интернет горят болтовней об особенностях NoSQL, одной из самых больших причин, которые я смог собрать, является то, что NoSQL хранит целые объекты как один в базе данных, не разбивая данные на разные таблицы, что хорошо для производительности, Хотя это звучит привлекательно, EF Code First эффективно устранил эту проблему, автоматически вытаскивая объекты и разделяя объекты в базу данных SQL без разработчика, каждый из которых должен беспокоиться о запросах.

Моя главная проблема, однако, в том, что я не могу найти какую-либо документацию для использования таких вещей, как EF Code First и ASP.NET Identity с базами данных NoSQL. Поскольку в моем приложении в настоящее время используется Identity, я бы хотел не переключиться на что-то еще.

Q: Можно ли использовать Code First и/или Identity с таблицами Azure?


Изменить: Немного о моем приложении. В качестве крайней упрощения мое приложение позволяет моим пользователям создавать собственные профили, смешивая и сопоставляя предварительно настроенные типы данных. Например, пользователь может добавить любое количество объектов Quote в свой профиль, а затем определить значение цитаты (т.е. "Будьте самим, все остальные уже приняты" ). Или они могут использовать объект Movie для определения коллекции своих любимых фильмов (т.е. "Title: Inception, Year: 2010" ). В среднем, пользователь может легко иметь 50 или более таких свойств на своей странице; нет ограничений на количество свойств, которые они могут иметь.

Используя этот пример, я могу с легкостью понять, как я буду реализовывать его с помощью Code First (у профиля есть список объектов Quote и список объектов Movie). Я еще не уверен, как это будет отображаться в базе данных NoSQL, например в Azure Tables. Таким образом, с моими приложениями, я не уверен, что переключение с Code First на NoSQL на разумное решение с функциями и функциями, которые я бы потерял.

4b9b3361

Ответ 1

Таким образом, у нас будет образец, нацеленный именно на этот сценарий, используя хранилище AzureTable в качестве без sql-реализации UserStore. В основном вы реализуете IUserStore с использованием API-интерфейсов Azure Storage. Вот базовая реализация, которая реализует методы входа/пароля, но не все:

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

В реальности вы не можете использовать EF Code First с хранилищем таблиц Azure. Говоря о том, что работа с хранилищем таблиц обычно выполняется с использованием аналогичного подхода к коду в первую очередь - т.е. Вы создаете свои классы, и они создают таблицы на лету.

Заметьте, что при хранении таблиц нет никаких отношений или что-то в этом роде. Хранилище таблиц еще проще, чем другие решения NoSQL, поскольку вы не можете хранить сложные объекты в одной таблице "строка".

Вероятно, вы можете создать провайдер идентификации .net, который использует только хранилище таблиц и/или blob, но я не могу найти какие-либо примеры там - я уверен, что раньше был проект Codeplex, но я не могу его найти сейчас.

Что означает Герт Арнольд, используется как SQL Azure, так и Table Storage (EF только с sql-лазурной частью). Таким образом, вы можете использовать каждый, для чего лучше всего - хранить таблицу при хранении большого количества просто структурированных данных, sql azure для частей более сложных (т.е. Требует отношений)

Ответ 3

Для любых будущих ссылок. Существует проект github, использующий Identity with Azure Table storage. Джеймс Рэндалл Случайные рыбы. Я не уверен, что роли уже реализованы.

Ответ 4

С новейшим ядром структуры сущностей теперь вы можете подключиться к таблице хранения azure с помощью EF: EntityFramework.AzureTableStorage 7.0.0-beta1

См. мой пост, если вы хотите настроить свой Dbcontext.

Используя его, вы можете реализовать свой класс UserManager.