From 8026912eeb33a61ebc791b830f404119a9b5c05c Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 9 Jan 2020 07:57:33 -0500 Subject: [PATCH] fixes for databasecontext and json types --- src/Core/Core.csproj | 2 +- src/Core/Models/EntityFramework/Cipher.cs | 2 + .../Models/EntityFramework/Organization.cs | 3 +- src/Core/Models/EntityFramework/User.cs | 1 + .../BaseEntityFrameworkRepository.cs | 12 ++- .../EntityFramework/DatabaseContext.cs | 22 +---- .../EntityFramework/OrganizationRepository.cs | 29 +++++-- .../EntityFramework/Repository.cs | 49 +++++++---- .../EntityFramework/UserRepository.cs | 84 +++++++++++++------ 9 files changed, 130 insertions(+), 74 deletions(-) diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index dd750a49ca..1ecfd3ec1e 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/Core/Models/EntityFramework/Cipher.cs b/src/Core/Models/EntityFramework/Cipher.cs index 72ae917346..4d4986ecdc 100644 --- a/src/Core/Models/EntityFramework/Cipher.cs +++ b/src/Core/Models/EntityFramework/Cipher.cs @@ -10,6 +10,7 @@ namespace Bit.Core.Models.EntityFramework public User User { get; set; } public Organization Organization { get; set; } + [IgnoreMap] public JsonDocument DataJson { get => _dataJson; @@ -19,6 +20,7 @@ namespace Bit.Core.Models.EntityFramework _dataJson = value; } } + [IgnoreMap] public JsonDocument AttachmentsJson { get => _attachmentsJson; diff --git a/src/Core/Models/EntityFramework/Organization.cs b/src/Core/Models/EntityFramework/Organization.cs index 5660fa125d..3532f52dac 100644 --- a/src/Core/Models/EntityFramework/Organization.cs +++ b/src/Core/Models/EntityFramework/Organization.cs @@ -9,7 +9,8 @@ namespace Bit.Core.Models.EntityFramework private JsonDocument _twoFactorProvidersJson; public ICollection Ciphers { get; set; } - + + [IgnoreMap] public JsonDocument TwoFactorProvidersJson { get => _twoFactorProvidersJson; diff --git a/src/Core/Models/EntityFramework/User.cs b/src/Core/Models/EntityFramework/User.cs index ed896df604..c945cbd0d4 100644 --- a/src/Core/Models/EntityFramework/User.cs +++ b/src/Core/Models/EntityFramework/User.cs @@ -10,6 +10,7 @@ namespace Bit.Core.Models.EntityFramework public ICollection Ciphers { get; set; } + [IgnoreMap] public JsonDocument TwoFactorProvidersJson { get => _twoFactorProvidersJson; diff --git a/src/Core/Repositories/EntityFramework/BaseEntityFrameworkRepository.cs b/src/Core/Repositories/EntityFramework/BaseEntityFrameworkRepository.cs index bf8a059a8d..fd20430111 100644 --- a/src/Core/Repositories/EntityFramework/BaseEntityFrameworkRepository.cs +++ b/src/Core/Repositories/EntityFramework/BaseEntityFrameworkRepository.cs @@ -1,16 +1,22 @@ using AutoMapper; +using Microsoft.Extensions.DependencyInjection; namespace Bit.Core.Repositories.EntityFramework { public abstract class BaseEntityFrameworkRepository { - public BaseEntityFrameworkRepository(DatabaseContext databaseContext, IMapper mapper) + public BaseEntityFrameworkRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) { - DatabaseContext = databaseContext; + ServiceScopeFactory = serviceScopeFactory; Mapper = mapper; } - protected DatabaseContext DatabaseContext { get; private set; } + protected IServiceScopeFactory ServiceScopeFactory { get; private set; } protected IMapper Mapper { get; private set; } + + public DatabaseContext GetDatabaseContext(IServiceScope serviceScope) + { + return serviceScope.ServiceProvider.GetRequiredService(); + } } } diff --git a/src/Core/Repositories/EntityFramework/DatabaseContext.cs b/src/Core/Repositories/EntityFramework/DatabaseContext.cs index df28f1e35a..69ca779254 100644 --- a/src/Core/Repositories/EntityFramework/DatabaseContext.cs +++ b/src/Core/Repositories/EntityFramework/DatabaseContext.cs @@ -6,34 +6,20 @@ namespace Bit.Core.Repositories.EntityFramework { public class DatabaseContext : DbContext { - private readonly GlobalSettings _globalSettings; - - public DatabaseContext( - DbContextOptions options, - GlobalSettings globalSettings) + public DatabaseContext(DbContextOptions options) : base(options) - { - _globalSettings = globalSettings; - } + { } public DbSet Users { get; set; } public DbSet Ciphers { get; set; } public DbSet Organizations { get; set; } - protected override void OnConfiguring(DbContextOptionsBuilder builder) - { - if(!string.IsNullOrWhiteSpace(_globalSettings.PostgreSql?.ConnectionString)) - { - builder.UseNpgsql(_globalSettings.PostgreSql.ConnectionString); - } - } - protected override void OnModelCreating(ModelBuilder builder) { builder.Entity().Ignore(e => e.Data); - builder.Entity().Property(e => e.Data).HasColumnName("Data"); + builder.Entity().Property(e => e.DataJson).HasColumnName("Data"); builder.Entity().Ignore(e => e.Attachments); - builder.Entity().Property(e => e.Attachments).HasColumnName("Attachments"); + builder.Entity().Property(e => e.AttachmentsJson).HasColumnName("Attachments"); builder.Entity().Ignore(e => e.TwoFactorProviders); builder.Entity().Property(e => e.TwoFactorProvidersJson).HasColumnName("TwoFactorProviders"); diff --git a/src/Core/Repositories/EntityFramework/OrganizationRepository.cs b/src/Core/Repositories/EntityFramework/OrganizationRepository.cs index fe16ceb5bd..8cbc27b481 100644 --- a/src/Core/Repositories/EntityFramework/OrganizationRepository.cs +++ b/src/Core/Repositories/EntityFramework/OrganizationRepository.cs @@ -7,19 +7,24 @@ using System.Linq; using System.Collections.Generic; using AutoMapper; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Bit.Core.Repositories.EntityFramework { public class OrganizationRepository : Repository, IOrganizationRepository { - public OrganizationRepository(DatabaseContext databaseContext, IMapper mapper) - : base(databaseContext, mapper, () => databaseContext.Organizations) + public OrganizationRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) + : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Organizations) { } public async Task> GetManyByEnabledAsync() { - var organizations = await GetDbSet().Where(e => e.Enabled).ToListAsync(); - return Mapper.Map>(organizations); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var organizations = await GetDbSet(dbContext).Where(e => e.Enabled).ToListAsync(); + return Mapper.Map>(organizations); + } } public async Task> GetManyByUserIdAsync(Guid userId) @@ -31,13 +36,17 @@ namespace Bit.Core.Repositories.EntityFramework public async Task> SearchAsync(string name, string userEmail, bool? paid, int skip, int take) { - // TODO: more filters - var organizations = await GetDbSet() + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + // TODO: more filters + var organizations = await GetDbSet(dbContext) .Where(e => name == null || e.Name.StartsWith(name)) .OrderBy(e => e.Name) .Skip(skip).Take(take) .ToListAsync(); - return Mapper.Map>(organizations); + return Mapper.Map>(organizations); + } } public async Task UpdateStorageAsync(Guid id) @@ -47,7 +56,10 @@ namespace Bit.Core.Repositories.EntityFramework public async Task> GetManyAbilitiesAsync() { - return await GetDbSet() + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + return await GetDbSet(dbContext) .Select(e => new DataModel.OrganizationAbility { Enabled = e.Enabled, @@ -57,6 +69,7 @@ namespace Bit.Core.Repositories.EntityFramework UsersGetPremium = e.UsersGetPremium, Using2fa = e.Use2fa && e.TwoFactorProviders != null, }).ToListAsync(); + } } } } diff --git a/src/Core/Repositories/EntityFramework/Repository.cs b/src/Core/Repositories/EntityFramework/Repository.cs index e325947c03..64915f1cc5 100644 --- a/src/Core/Repositories/EntityFramework/Repository.cs +++ b/src/Core/Repositories/EntityFramework/Repository.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Bit.Core.Models.Table; using AutoMapper; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Bit.Core.Repositories.EntityFramework { @@ -11,35 +12,47 @@ namespace Bit.Core.Repositories.EntityFramework where T : class, ITableObject where TEntity : class, ITableObject { - public Repository(DatabaseContext databaseContext, IMapper mapper, Func> getDbSet) - : base(databaseContext, mapper) + public Repository(IServiceScopeFactory serviceScopeFactory, IMapper mapper, Func> getDbSet) + : base(serviceScopeFactory, mapper) { GetDbSet = getDbSet; } - protected Func> GetDbSet { get; private set; } + protected Func> GetDbSet { get; private set; } public virtual async Task GetByIdAsync(TId id) { - var entity = await GetDbSet().FindAsync(id); - return entity as T; + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var entity = await GetDbSet(dbContext).FindAsync(id); + return entity as T; + } } public virtual async Task CreateAsync(T obj) { - var entity = Mapper.Map(obj); - DatabaseContext.Add(entity); - await DatabaseContext.SaveChangesAsync(); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var entity = Mapper.Map(obj); + dbContext.Add(entity); + await dbContext.SaveChangesAsync(); + } } public virtual async Task ReplaceAsync(T obj) { - var entity = await GetDbSet().FindAsync(obj.Id); - if(entity != null) + using(var scope = ServiceScopeFactory.CreateScope()) { - var mappedEntity = Mapper.Map(obj); - DatabaseContext.Entry(entity).CurrentValues.SetValues(mappedEntity); - await DatabaseContext.SaveChangesAsync(); + var dbContext = GetDatabaseContext(scope); + var entity = await GetDbSet(dbContext).FindAsync(obj.Id); + if(entity != null) + { + var mappedEntity = Mapper.Map(obj); + dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity); + await dbContext.SaveChangesAsync(); + } } } @@ -57,9 +70,13 @@ namespace Bit.Core.Repositories.EntityFramework public virtual async Task DeleteAsync(T obj) { - var entity = Mapper.Map(obj); - DatabaseContext.Entry(entity).State = EntityState.Deleted; - await DatabaseContext.SaveChangesAsync(); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var entity = Mapper.Map(obj); + dbContext.Entry(entity).State = EntityState.Deleted; + await dbContext.SaveChangesAsync(); + } } } } diff --git a/src/Core/Repositories/EntityFramework/UserRepository.cs b/src/Core/Repositories/EntityFramework/UserRepository.cs index 973055d7b8..0285033c89 100644 --- a/src/Core/Repositories/EntityFramework/UserRepository.cs +++ b/src/Core/Repositories/EntityFramework/UserRepository.cs @@ -7,54 +7,80 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.DependencyInjection; namespace Bit.Core.Repositories.EntityFramework { public class UserRepository : Repository, IUserRepository { - public UserRepository(DatabaseContext databaseContext, IMapper mapper) - : base(databaseContext, mapper, () => databaseContext.Users) + public UserRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) + : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Users) { } public async Task GetByEmailAsync(string email) { - return await GetDbSet().FirstOrDefaultAsync(e => e.Email == email); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + return await GetDbSet(dbContext).FirstOrDefaultAsync(e => e.Email == email); + } } public async Task GetKdfInformationByEmailAsync(string email) { - return await GetDbSet().Where(e => e.Email == email) - .Select(e => new DataModel.UserKdfInformation - { - Kdf = e.Kdf, - KdfIterations = e.KdfIterations - }).SingleOrDefaultAsync(); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + return await GetDbSet(dbContext).Where(e => e.Email == email) + .Select(e => new DataModel.UserKdfInformation + { + Kdf = e.Kdf, + KdfIterations = e.KdfIterations + }).SingleOrDefaultAsync(); + } } public async Task> SearchAsync(string email, int skip, int take) { - var users = await GetDbSet() - .Where(e => email == null || e.Email.StartsWith(email)) - .OrderBy(e => e.Email) - .Skip(skip).Take(take) - .ToListAsync(); - return Mapper.Map>(users); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var users = await GetDbSet(dbContext) + .Where(e => email == null || e.Email.StartsWith(email)) + .OrderBy(e => e.Email) + .Skip(skip).Take(take) + .ToListAsync(); + return Mapper.Map>(users); + } } public async Task> GetManyByPremiumAsync(bool premium) { - var users = await GetDbSet().Where(e => e.Premium == premium).ToListAsync(); - return Mapper.Map>(users); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var users = await GetDbSet(dbContext).Where(e => e.Premium == premium).ToListAsync(); + return Mapper.Map>(users); + } } public async Task GetPublicKeyAsync(Guid id) { - return await GetDbSet().Where(e => e.Id == id).Select(e => e.PublicKey).SingleOrDefaultAsync(); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + return await GetDbSet(dbContext).Where(e => e.Id == id).Select(e => e.PublicKey).SingleOrDefaultAsync(); + } } public async Task GetAccountRevisionDateAsync(Guid id) { - return await GetDbSet().Where(e => e.Id == id).Select(e => e.AccountRevisionDate).SingleOrDefaultAsync(); + using(var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + return await GetDbSet(dbContext).Where(e => e.Id == id).Select(e => e.AccountRevisionDate) + .SingleOrDefaultAsync(); + } } public async Task UpdateStorageAsync(Guid id) @@ -64,15 +90,19 @@ namespace Bit.Core.Repositories.EntityFramework public async Task UpdateRenewalReminderDateAsync(Guid id, DateTime renewalReminderDate) { - var user = new EFModel.User + using(var scope = ServiceScopeFactory.CreateScope()) { - Id = id, - RenewalReminderDate = renewalReminderDate - }; - var set = GetDbSet(); - set.Attach(user); - DatabaseContext.Entry(user).Property(e => e.RenewalReminderDate).IsModified = true; - await DatabaseContext.SaveChangesAsync(); + var dbContext = GetDatabaseContext(scope); + var user = new EFModel.User + { + Id = id, + RenewalReminderDate = renewalReminderDate + }; + var set = GetDbSet(dbContext); + set.Attach(user); + dbContext.Entry(user).Property(e => e.RenewalReminderDate).IsModified = true; + await dbContext.SaveChangesAsync(); + } } } }