diff --git a/common.props b/common.props index 0716d5e..97290f1 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 5.2.0 + 5.3.0-preview.1 $(NoWarn);CS1591 true EasyAbp Team diff --git a/src/EasyAbp.FileManagement.Domain.Core/EasyAbp/FileManagement/Files/FileManagerBase.cs b/src/EasyAbp.FileManagement.Domain.Core/EasyAbp/FileManagement/Files/FileManagerBase.cs index 57823b7..37e937d 100644 --- a/src/EasyAbp.FileManagement.Domain.Core/EasyAbp/FileManagement/Files/FileManagerBase.cs +++ b/src/EasyAbp.FileManagement.Domain.Core/EasyAbp/FileManagement/Files/FileManagerBase.cs @@ -67,7 +67,7 @@ public virtual async Task FindByPathAsync(string path, string fileContaine foreach (var fileName in splitFileName) { - foundFile = await FileRepository.FindAsync(fileName, parentId, fileContainerName, ownerUserId); + foundFile = await FileRepository.FindAsync(fileName, parentId, fileContainerName, ownerUserId, false); if (foundFile is null) { @@ -131,7 +131,7 @@ public virtual async Task GetDownloadInfoAsync(File file) if (model.NewFileName != file.FileName) { await CheckFileNotExistAsync(model.NewFileName, file.ParentId, file.FileContainerName, - file.OwnerUserId); + file.OwnerUserId, true); } file.UpdateInfo(model.NewFileName, model.NewMimeType, file.SubFilesQuantity, file.ByteSize, file.Hash, @@ -165,7 +165,7 @@ public virtual async Task GetDownloadInfoAsync(File file) if (model.NewFileName != file.FileName || newParent?.Id != oldParent?.Id) { await CheckFileNotExistAsync(model.NewFileName, newParent?.Id, file.FileContainerName, - file.OwnerUserId); + file.OwnerUserId, true); } if (oldParent != newParent) @@ -296,15 +296,15 @@ protected virtual void CheckFileName(string fileName, IFileContainerConfiguratio [UnitOfWork] protected virtual async Task IsFileExistAsync(string fileName, Guid? parentId, string fileContainerName, - Guid? ownerUserId) + Guid? ownerUserId, bool forceCaseSensitive) { - return await FileRepository.FindAsync(fileName, parentId, fileContainerName, ownerUserId) != null; + return await FileRepository.ExistAsync(fileName, parentId, fileContainerName, ownerUserId, forceCaseSensitive); } protected virtual async Task CheckFileNotExistAsync(string fileName, Guid? parentId, string fileContainerName, - Guid? ownerUserId) + Guid? ownerUserId, bool forceCaseSensitive) { - if (await IsFileExistAsync(fileName, parentId, fileContainerName, ownerUserId)) + if (await IsFileExistAsync(fileName, parentId, fileContainerName, ownerUserId, forceCaseSensitive)) { throw new FileAlreadyExistsException(fileName, parentId); } diff --git a/src/EasyAbp.FileManagement.Domain.Core/EasyAbp/FileManagement/Files/IFileRepository.cs b/src/EasyAbp.FileManagement.Domain.Core/EasyAbp/FileManagement/Files/IFileRepository.cs index 9c11c9d..06d32aa 100644 --- a/src/EasyAbp.FileManagement.Domain.Core/EasyAbp/FileManagement/Files/IFileRepository.cs +++ b/src/EasyAbp.FileManagement.Domain.Core/EasyAbp/FileManagement/Files/IFileRepository.cs @@ -10,9 +10,12 @@ public interface IFileRepository : IRepository { Task> GetListAsync(Guid? parentId, string fileContainerName, Guid? ownerUserId, FileType? specifiedFileType = null, CancellationToken cancellationToken = default); - + Task FindAsync(string fileName, Guid? parentId, string fileContainerName, Guid? ownerUserId, - CancellationToken cancellationToken = default); + bool forceCaseSensitive, CancellationToken cancellationToken = default); + + Task ExistAsync(string fileName, Guid? parentId, string fileContainerName, Guid? ownerUserId, + bool forceCaseSensitive, CancellationToken cancellationToken = default); Task FirstOrDefaultAsync(string fileContainerName, string hash, long byteSize, CancellationToken cancellationToken = default); diff --git a/src/EasyAbp.FileManagement.Domain/EasyAbp/FileManagement/Files/FileManager.cs b/src/EasyAbp.FileManagement.Domain/EasyAbp/FileManagement/Files/FileManager.cs index 7486049..7a822e5 100644 --- a/src/EasyAbp.FileManagement.Domain/EasyAbp/FileManagement/Files/FileManager.cs +++ b/src/EasyAbp.FileManagement.Domain/EasyAbp/FileManagement/Files/FileManager.cs @@ -81,14 +81,15 @@ public class FileManager : FileManagerBase, IFileManager if (configuration.EnableAutoRename) { if (await IsFileExistAsync(model.FileName, model.Parent?.Id, model.FileContainerName, - model.OwnerUserId)) + model.OwnerUserId, false)) { model.FileName = await FileRepository.GetFileNameWithNextSerialNumberAsync(model.FileName, model.Parent?.Id, model.FileContainerName, model.OwnerUserId, cancellationToken); } } - await CheckFileNotExistAsync(model.FileName, model.Parent?.Id, model.FileContainerName, model.OwnerUserId); + await CheckFileNotExistAsync( + model.FileName, model.Parent?.Id, model.FileContainerName, model.OwnerUserId, false); var file = new File(GuidGenerator.Create(), CurrentTenant.Id, model.Parent, model.FileContainerName, model.FileName, model.MimeType, model.FileType, 0, model.GetContentLength(), hashString, diff --git a/src/EasyAbp.FileManagement.EntityFrameworkCore/EasyAbp/FileManagement/Files/FileRepository.cs b/src/EasyAbp.FileManagement.EntityFrameworkCore/EasyAbp/FileManagement/Files/FileRepository.cs index 2bf5b78..02250f0 100644 --- a/src/EasyAbp.FileManagement.EntityFrameworkCore/EasyAbp/FileManagement/Files/FileRepository.cs +++ b/src/EasyAbp.FileManagement.EntityFrameworkCore/EasyAbp/FileManagement/Files/FileRepository.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Threading; @@ -22,25 +21,61 @@ public FileRepository(IDbContextProvider dbContextProv public virtual async Task> GetListAsync(Guid? parentId, string fileContainerName, Guid? ownerUserId, FileType? specifiedFileType = null, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()) + return await (await GetQueryableAsync()) .Where(x => x.ParentId == parentId && x.OwnerUserId == ownerUserId && x.FileContainerName == fileContainerName).WhereIf(specifiedFileType.HasValue, x => x.FileType == specifiedFileType.Value).ToListAsync(cancellationToken); } - public virtual async Task FindAsync(string fileName, Guid? parentId, string fileContainerName, Guid? ownerUserId, - CancellationToken cancellationToken = default) + public virtual async Task FindAsync(string fileName, Guid? parentId, string fileContainerName, + Guid? ownerUserId, bool forceCaseSensitive, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()) - .Where(x => x.ParentId == parentId && x.OwnerUserId == ownerUserId && - x.FileContainerName == fileContainerName && x.FileName == fileName) - .FirstOrDefaultAsync(cancellationToken); + var queryable = await GetQueryableAsync(); + + if (forceCaseSensitive) + { + queryable = queryable.AsNoTracking().Where(x => + x.ParentId == parentId && x.OwnerUserId == ownerUserId && x.FileContainerName == fileContainerName); + + var files = await queryable.ToListAsync(cancellationToken); + + var foundFile = files.FirstOrDefault(x => x.FileName == fileName); + return foundFile is null ? null : await GetAsync(foundFile.Id, cancellationToken: cancellationToken); + } + + queryable = queryable.Where(x => + x.ParentId == parentId && x.OwnerUserId == ownerUserId && + x.FileContainerName == fileContainerName && x.FileName == fileName); + + return await queryable.FirstOrDefaultAsync(cancellationToken); + } + + public virtual async Task ExistAsync(string fileName, Guid? parentId, string fileContainerName, + Guid? ownerUserId, bool forceCaseSensitive, CancellationToken cancellationToken = default) + { + var queryable = await GetQueryableAsync(); + + if (forceCaseSensitive) + { + queryable = queryable.AsNoTracking().Where(x => + x.ParentId == parentId && x.OwnerUserId == ownerUserId && x.FileContainerName == fileContainerName); + + var files = await queryable.ToListAsync(cancellationToken); + + return files.Any(x => x.FileName == fileName); + } + + queryable = queryable.Where(x => + x.ParentId == parentId && x.OwnerUserId == ownerUserId && + x.FileContainerName == fileContainerName && x.FileName == fileName); + + return await queryable.AnyAsync(cancellationToken); } public virtual async Task FirstOrDefaultAsync(string fileContainerName, string hash, long byteSize, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()) + return await (await GetQueryableAsync()) .Where(x => x.Hash == hash && x.ByteSize == byteSize && x.FileContainerName == fileContainerName) .FirstOrDefaultAsync(cancellationToken); } @@ -48,14 +83,15 @@ public FileRepository(IDbContextProvider dbContextProv public virtual async Task FirstOrDefaultAsync(string fileContainerName, string blobName, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()).Where(x => x.BlobName == blobName && x.FileContainerName == fileContainerName) + return await (await GetQueryableAsync()) + .Where(x => x.BlobName == blobName && x.FileContainerName == fileContainerName) .FirstOrDefaultAsync(cancellationToken); } public virtual async Task GetSubFilesStatisticDataAsync(Guid id, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()).Where(x => x.ParentId == id).GroupBy(x => true).Select(x => + return await (await GetQueryableAsync()).Where(x => x.ParentId == id).GroupBy(x => true).Select(x => new SubFilesStatisticDataModel { SubFilesQuantity = x.Count(), @@ -63,8 +99,8 @@ public FileRepository(IDbContextProvider dbContextProv }).FirstOrDefaultAsync(cancellationToken) ?? new SubFilesStatisticDataModel(); } - public virtual async Task GetFileNameWithNextSerialNumberAsync(string fileName, Guid? parentId, string fileContainerName, Guid? ownerUserId, - CancellationToken cancellationToken = default) + public virtual async Task GetFileNameWithNextSerialNumberAsync(string fileName, Guid? parentId, + string fileContainerName, Guid? ownerUserId, CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(fileName, nameof(fileName)); @@ -76,14 +112,15 @@ public FileRepository(IDbContextProvider dbContextProv var part2 = ')' + ext; - var fileNames = await (await GetDbSetAsync()) + var fileNames = await (await GetQueryableAsync()) .Where(x => x.ParentId == parentId && x.OwnerUserId == ownerUserId && x.FileContainerName == fileContainerName && x.FileName.StartsWith(part1) && x.FileName.EndsWith(part2)).Select(x => x.FileName).ToListAsync(cancellationToken); var nextNumber = fileNames - .Select(x => x.Substring(part1.Length, x.LastIndexOf(part2, StringComparison.Ordinal) - part1.Length)) + .Select(x => + x.Substring(part1.Length, x.LastIndexOf(part2, StringComparison.Ordinal) - part1.Length)) .Select(x => int.TryParse(x, out var number) ? number : 0).Where(x => x > 0).OrderBy(x => x) .TakeWhile((x, i) => x == i + 1).LastOrDefault() + 1; diff --git a/test/EasyAbp.FileManagement.Domain.Tests/Files/FileDomainTests.cs b/test/EasyAbp.FileManagement.Domain.Tests/Files/FileDomainTests.cs index fc240ba..1c7f1cd 100644 --- a/test/EasyAbp.FileManagement.Domain.Tests/Files/FileDomainTests.cs +++ b/test/EasyAbp.FileManagement.Domain.Tests/Files/FileDomainTests.cs @@ -209,4 +209,36 @@ public async Task Should_Get_File_By_Path() (await FileManager.GetByPathAsync("dir/sub-dir", "test", null)).Id.ShouldBe(subDir.Id); (await FileManager.GetByPathAsync("dir/sub-dir/file.txt", "test", null)).Id.ShouldBe(file.Id); } + + [Fact] + public async Task Should_Rename_From_abc_To_ABC() + { + var dir = await FileManager.CreateAsync(new CreateFileModel("test", null, "abc", null, + FileType.Directory, null, null)); + + await Should.NotThrowAsync(() => + FileManager.UpdateInfoAsync(dir, new UpdateFileInfoModel("ABC", dir.MimeType))); + + dir.FileName.ShouldBe("ABC"); + + dir = await FileRepository.GetAsync(dir.Id); + + dir.FileName.ShouldBe("ABC"); + } + + [Fact] + public async Task Should_Rename_From_abc_To_abc() + { + var dir = await FileManager.CreateAsync(new CreateFileModel("test", null, "abc", null, + FileType.Directory, null, null)); + + await Should.NotThrowAsync(() => + FileManager.UpdateInfoAsync(dir, new UpdateFileInfoModel("abc", dir.MimeType))); + + dir.FileName.ShouldBe("abc"); + + dir = await FileRepository.GetAsync(dir.Id); + + dir.FileName.ShouldBe("abc"); + } } \ No newline at end of file