Compare commits

...

10 Commits

Author SHA1 Message Date
mmdi 68def54d9f working on course 2025-09-13 20:25:09 +03:30
mmdi d4b01b5c28 ignoring .idea/ direct from rider 2025-09-13 14:18:52 +03:30
mmdi 421df4a379 Merge branch 'master' of github.com:HosseinJavadi/VizhTalk 2025-09-12 22:07:44 +03:30
mmdi 09ef1b979e working on course comment 2025-09-12 22:05:41 +03:30
hosseinJavadi 9e65e7e042 . 2025-09-12 21:28:19 +04:30
hosseinJavadi 54b10d0d02 . 2025-09-12 20:24:44 +04:30
hosseinJavadi c52cdc17a2 . 2025-09-12 19:52:41 +04:30
hosseinJavadi a6d6506e38 . 2025-09-12 19:46:59 +04:30
hosseinJavadi c7d97fe9bd course 2025-09-12 19:04:54 +04:30
hosseinJavadi 338895a1ad CDN 2025-09-12 14:18:08 +04:30
83 changed files with 862 additions and 463 deletions
+3 -1
View File
@@ -358,6 +358,8 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
.idea/
# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
/Vizhtalk.CDN.Presentation/Files/
@@ -5,4 +5,5 @@ public interface IGenericDapper<T> : IDisposable
{
Task<T?> GetAsync(string sql, object? param = null);
Task<IEnumerable<T>> GetAllAsync(string sql, object? param = null);
Task<int> CountAsync(string schema,string tableName, string where, object? param = null, string? alias = null);
}
+1 -3
View File
@@ -7,12 +7,10 @@ namespace VizhTalk.Gateway.Web.BFF.Mappers
{
public static async Task<UploadFile> AsFile(this IFile file)
{
using MemoryStream stream = new MemoryStream();
await file.CopyToAsync(stream);
return new UploadFile()
{
ContentType = file.ContentType,
File = ByteString.FromStream(stream),
File = ByteString.FromStream(file.OpenReadStream()),
Name = file.Name,
};
}
@@ -0,0 +1,6 @@
using VizhTalk.Grpc.Learning;
namespace VizhTalk.Gateway.Web.BFF.Models;
public record CreateCourseRequest(string title, string description, IFile image, Id languageid);
public record UpdateCourseRequest(Id id, string title, string description, Int32 order, IFile image, Id languageid);
@@ -1,4 +1,4 @@
namespace VizhTalk.Gateway.Web.BFF.Models;
public record CreateLanguageRequest(string Title,IFile Icon);
public record UpdateLanguageRequest(IdRequest Id, string Title,IFile Icon);
public record UpdateLanguageRequest(IdRequest Id, string Title, IFile? Icon);
@@ -1,30 +0,0 @@
using VizhTalk.Gateway.Web.BFF.Mappers;
using VizhTalk.Gateway.Web.BFF.Models;
using VizhTalk.Grpc.Learning;
using VizhTalk.Grpc.Learning.Language;
namespace VizhTalk.Gateway.Web.BFF.Mutations;
public class LanguageMutation(LanguageGrpc.LanguageGrpcClient client)
{
[GraphQLName("CreateLanguage")]
public async Task<LanguageDTO> CreateLanguage(CreateLanguageRequest request)
{
return await client.CreateAsync(new CreateLanguageCommand()
{
Title = request.Title,
Icon = await request.Icon.AsFile()
});
}
[GraphQLName("UpdateLanguage")]
public async Task<LanguageDTO> UpdateLanguage(UpdateLanguageRequest request)
{
return await client.UpdateAsync(new UpdateLanguageCommand()
{
Id = new Id(request.Id.Value),
Title = request.Title,
Icon = await request.Icon.AsFile()
});
}
}
@@ -0,0 +1,74 @@
using VizhTalk.Gateway.Web.BFF.Mappers;
using VizhTalk.Gateway.Web.BFF.Models;
using VizhTalk.Grpc.Learning;
using VizhTalk.Grpc.Learning.Course;
using VizhTalk.Grpc.Learning.Language;
namespace VizhTalk.Gateway.Web.BFF.Mutations;
public class Mutation(LanguageGrpc.LanguageGrpcClient languageClient,
CourseGrpc.CourseGrpcClient courseClient)
{
[GraphQLName("CreateLanguage")]
public async Task<LanguageDTO> CreateLanguage(CreateLanguageRequest request)
{
return await languageClient.CreateAsync(new CreateLanguageCommand()
{
Title = request.Title,
Icon = await request.Icon.AsFile()
});
}
[GraphQLName("UpdateLanguage")]
public async Task<LanguageDTO> UpdateLanguage(UpdateLanguageRequest request)
{
UploadFile file = null;
if (request.Icon != null)
{
file = await request.Icon.AsFile();
}
return await languageClient.UpdateAsync(new UpdateLanguageCommand()
{
Id = new Id()
{
Value = request.Id.Value
},
Title = request.Title,
Icon = file
});
}
[GraphQLName("CreateCourse")]
public async Task<CourseDTO> CreateCourse(CreateCourseRequest input)
{
return await courseClient.CreateAsync(new CreateCourseCommand()
{
Title = input.title,
Image = await input.image.AsFile(),
Description = input.description,
Languageid = input.languageid
});
}
[GraphQLName("UpdateCourse")]
public async Task<CourseDTO> UpdateCourse(UpdateCourseRequest input)
{
UploadFile file = null;
if (input.image != null)
{
file = await input.image.AsFile();
}
return await courseClient.UpdateAsync(new UpdateCourseCommand()
{
Id = new Id()
{
Value = input.id.Value
},
Order = input.order,
Title = input.title,
Image = file,
Description = input.description,
Languageid = input.languageid
});
}
}
+13 -5
View File
@@ -1,5 +1,6 @@
using VizhTalk.Gateway.Web.BFF.Mutations;
using VizhTalk.Gateway.Web.BFF.Queries;
using VizhTalk.Grpc.Learning.Course;
using VizhTalk.Grpc.Learning.Language;
var builder = WebApplication.CreateBuilder(args);
@@ -8,13 +9,20 @@ builder.Services.AddGrpcClient<LanguageGrpc.LanguageGrpcClient>(o =>
o.Address = new Uri("https://localhost:5000");
});
builder.Services.AddScoped<LanguageQuery>();
builder.Services.AddScoped<LanguageMutation>();
builder.Services.AddGrpcClient<CourseGrpc.CourseGrpcClient>(o =>
{
o.Address = new Uri("https://localhost:5000");
});
builder.Services.AddScoped<Query>();
builder.Services.AddScoped<Mutation>();
builder.AddGraphQL()
.AddQueryType<LanguageQuery>()
.AddMutationType<LanguageMutation>()
.AddType<UploadType>();
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddType<UploadType>()
.AddMutationConventions();
var app = builder.Build();
@@ -1,15 +0,0 @@
using VizhTalk.Grpc.Learning.Language;
namespace VizhTalk.Gateway.Web.BFF.Queries
{
public class LanguageQuery(LanguageGrpc.LanguageGrpcClient client)
{
[GraphQLName("Languages")]
public async Task<GetAllLanguageDTO> GetAllLanguage(GetAllLanguageQuery request)
=> await client.GetAllAsync(request);
[GraphQLName("LanguageByTitle")]
public async Task<LanguageDTO> GetLanguageByTitle(GetLanguageByTitleQuery request)
=> await client.GetByTitleAsync(request);
}
}
+30
View File
@@ -0,0 +1,30 @@
using VizhTalk.Grpc.Learning.Course;
using VizhTalk.Grpc.Learning.Language;
namespace VizhTalk.Gateway.Web.BFF.Queries;
public class Query(
LanguageGrpc.LanguageGrpcClient languageClient,
CourseGrpc.CourseGrpcClient courseClient
)
{
[GraphQLName("Languages")]
public async Task<GetAllLanguageDTO> GetAllLanguage(GetAllLanguageQuery request)
=> await languageClient.GetAllAsync(request);
[GraphQLName("LanguageByTitle")]
public async Task<LanguageDTO> GetLanguageByTitle(GetLanguageByTitleQuery request)
=> await languageClient.GetByTitleAsync(request);
[GraphQLName("Courses")]
public async Task<GetAllCourseDTO> GetAllCourses(GetAllCourseQuery request)
=> await courseClient.GetAllAsync(request);
[GraphQLName("CourseByTitle")]
public async Task<CourseDTO> GetCourseByTitle(GetCoursesByTitleQuery request)
=> await courseClient.GetByTitleAsync(request);
[GraphQLName("CourseByCourseId")]
public async Task<CourseDTO> GetCourseById(GetCourseByIdQuery request)
=> await courseClient.GetByIdAsync(request);
}
-1
View File
@@ -10,7 +10,6 @@ message UploadFileCommand{
UploadFile file = 1;
Id mediaGroupId = 2;
string clientId = 3;
string fileName = 4;
}
message CreateMediaGroupCommand{
string key = 1;
@@ -0,0 +1,101 @@
using Mapster;
using System.Net.Http.Headers;
using System.Net.Sockets;
using VizhTalk.BuildingBlocks.Application.Configuration.Commnad;
using VizhTalk.Grpc.CDN;
using VizhTalk.Grpc.Learning.Course;
using VizhTalk.Learning.Application.Exceptions;
using VizhTalk.Learning.Domain.Entities;
using VizhTalk.Learning.Domain.TypeIds;
using VizhTalk.Learning.Domain.ValueObjects;
namespace VizhTalk.Learning.Application.CommandHandlers;
public class CreateCourseCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcClient cdnClient) : ICommandHandler<CreateCourseCommand, CourseDTO>
{
public async Task<CourseDTO> Handle(CreateCourseCommand request, CancellationToken cancellationToken)
{
var language = await unitOfWork.Language.GetLanguageWithCoursesById(new LanguageId(request.Languageid), cancellationToken);
if (language == null)
throw new LanguageNotFoundException();
var fileGroup = await unitOfWork.FileGroup.GetByFileGroupKey(FileGroupKey.ImageCourse);
if (fileGroup == null)
{
var group = await cdnClient.CreateMediaGroupAsync(new CreateMediaGroupCommand()
{
IsSecure = false,
Key = FileGroupKey.ImageCourse.Value,
Title = nameof(FileGroupKey.ImageCourse)
});
fileGroup = FileGroup.CreateNew(FileGroupKey.IconLanguage, new FileGroupTitle(nameof(FileGroupKey.IconLanguage)), false, group.Id);
await unitOfWork.FileGroup.AddAsync(fileGroup, cancellationToken);
}
var upload = await cdnClient.UploadFileAsync(new UploadFileCommand()
{
File = new UploadFile()
{
ContentType = request.Image.ContentType,
File = request.Image.File,
Name = request.Image.Name,
},
MediaGroupId = fileGroup.MediaGroupId,
}, cancellationToken: cancellationToken);
var media = Media.CreateNew(request.Image.Name, upload.ContentType, upload.Size, upload.MediaGroupMediaId, new EntityType(EntityTypeEnum.Course));
var course = language.AddCourse(request.Title, media, request.Description);
await unitOfWork.Language.UpdateAsync(language, cancellationToken);
await unitOfWork.SaveChangeAsync(cancellationToken);
return course.Adapt<CourseDTO>();
}
}
public class UpdateCourseCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcClient cdnClient) : ICommandHandler<UpdateCourseCommand, CourseDTO>
{
public async Task<CourseDTO> Handle(UpdateCourseCommand request, CancellationToken cancellationToken)
{
var language = await unitOfWork.Language.GetLanguageWithCoursesById(new LanguageId(request.Languageid), cancellationToken);
if (language == null)
throw new LanguageNotFoundException();
if (!language.Courses.Any(x => x.Id == new CourseId(request.Id)))
throw new CourseNotFoundException();
Media image = null;
if (request.Image != null)
{
var fileGroup = await unitOfWork.FileGroup.GetByFileGroupKey(FileGroupKey.ImageCourse);
var upload = await cdnClient.UploadFileAsync(new UploadFileCommand()
{
File = new UploadFile()
{
ContentType = request.Image.ContentType,
File = request.Image.File,
Name = request.Image.Name,
},
MediaGroupId = fileGroup.MediaGroupId,
}, cancellationToken: cancellationToken);
var media = await unitOfWork.Media.GetByIdAsync(language.Icon.Id);
if (media == null)
media = Media.CreateNew(request.Title, upload.ContentType, upload.Size, upload.MediaGroupMediaId, new EntityType(EntityTypeEnum.Language));
else
{
media.SetName(request.Image.Name);
media.SetContentType(request.Image.ContentType);
media.SetSize(upload.Size);
media.SetFileId(upload.MediaGroupMediaId);
}
image = media;
}
var course = language.UpdateCourse(new CourseId(request.Id), new CourseTitle(request.Title), request.Order, image,
string.IsNullOrEmpty(request.Description) ? null : new CourseDescription(request.Description));
await unitOfWork.Language.UpdateAsync(language, cancellationToken);
return course.Adapt<CourseDTO>();
}
}
@@ -47,16 +47,13 @@ public class CreateLanguageCommandHandler(IUnitOfWork unitOfWork,
File = request.Icon.File,
Name = request.Icon.Name,
},
FileName = title,
MediaGroupId = fileGroup.MediaGroupId
}, cancellationToken: cancellationToken);
var language = Language.CreateNew(new LanguageTitle(title));
var media = Media.CreateNew(title, upload.ContentType, upload.Size, upload.MediaGroupMediaId, new EntityType(EntityTypeEnum.Language));
var language = Language.CreateNew(new LanguageTitle(title), media);
await unitOfWork.Language.AddAsync(language, cancellationToken);
await unitOfWork.SaveChangeAsync(cancellationToken);
var media = Media.CreateNew(title, upload.ContentType, upload.Size, new EntityType(language.Id.Value.ToString(), EntityTypeEnum.Language));
await unitOfWork.Media.AddAsync(media, cancellationToken);
language.SetIcon(media);
return language.Adapt<LanguageDTO>();
}
catch (Exception ex)
@@ -76,7 +73,7 @@ public class UpdateLanguageCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrp
throw new LanguageNotFoundException();
language.SetTitle(new LanguageTitle(request.Title));
if(request.Icon != null)
if (request.Icon != null)
{
var fileGroup = await unitOfWork.FileGroup.GetByFileGroupKey(FileGroupKey.IconLanguage);
@@ -88,11 +85,19 @@ public class UpdateLanguageCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrp
File = request.Icon.File,
Name = request.Icon.Name,
},
FileName = request.Title,
MediaGroupId = fileGroup.MediaGroupId
}, cancellationToken: cancellationToken);
var media = await unitOfWork.Media.GetByIdAsync(language.Icon.Id);
var media = Media.CreateNew(request.Title, upload.ContentType, upload.Size, new EntityType(language.Id.Value.ToString(), EntityTypeEnum.Language));
if(media == null)
media = Media.CreateNew(request.Title, upload.ContentType, upload.Size, upload.MediaGroupMediaId, new EntityType(EntityTypeEnum.Language));
else
{
media.SetName(request.Icon.Name);
media.SetContentType(request.Icon.ContentType);
media.SetSize(upload.Size);
media.SetFileId(upload.MediaGroupMediaId);
}
language.SetIcon(media);
}
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VizhTalk.BuildingBlocks.Application.Exceptions;
namespace VizhTalk.Learning.Application.Exceptions
{
public class CourseNotFoundException : BaseException
{
public CourseNotFoundException() : base("course not found")
{
}
}
}
@@ -1,5 +1,6 @@
using Mapster;
using VizhTalk.Grpc.Learning;
using VizhTalk.Grpc.Learning.Course;
using VizhTalk.Grpc.Learning.Language;
using VizhTalk.Learning.Domain.Entities;
@@ -9,16 +10,20 @@ namespace VizhTalk.Learning.Application.Mappers
{
public static void Process()
{
TypeAdapterConfig<Language, LanguageDTO>.NewConfig()
.MapWith(lan => new LanguageDTO()
{
Id = lan.Id.Value,
Title = lan.Title.Value,
}).Compile();
var config = TypeAdapterConfig<Language, LanguageDTO>.NewConfig()
.MapWith(lan => new LanguageDTO()
{
Id = lan.Id.Value,
Title = lan.Title.Value,
});
TypeAdapterConfig<Course, CourseDTO>.NewConfig()
.MapWith(lan => new CourseDTO()
{
Id = lan.Id.Value,
Title = lan.Title.Value,
}).Compile();
config.Compile();
}
}
}
@@ -9,13 +9,23 @@ public class GetAllLanguageQueryHandler(IGenericDapper<LanguageDTO> languageDapp
{
public async Task<GetAllLanguageDTO> Handle(GetAllLanguageQuery request, CancellationToken cancellationToken)
{
var where = $"@Search is null or lan.{nameof(LanguageDTO.Title)} like N'%@Search%' ";
var result = new GetAllLanguageDTO();
var sql = $"select * from Learning.Languages " +
$"where @Search is null or {nameof(LanguageDTO.Title)} like N'%@Search%' " +
$"order by createOn OFFSET {request.Pagination.Skip()} ROWS FETCH NEXT {request.Pagination.Take()} ROWS ONLY;";
var sql = $"select " +
$"lan.{nameof(Language.Title)} as {nameof(LanguageDTO.Title)}," +
$"lan.{nameof(Language.Id)} as {nameof(LanguageDTO.Id)}," +
$"CONVERT(nvarchar(max),m.FileId) as {nameof(LanguageDTO.Icon)}" +
$" from Learning.Languages as lan " +
$"inner join Learning.Medias as m on m.{nameof(Media.Id)} = lan.iconId " +
$"where {where}" +
$"order by lan.{nameof(Language.CreateOn)} OFFSET {request.Pagination.Skip()} ROWS FETCH NEXT {request.Pagination.Take()} ROWS ONLY";
var language = await languageDapper.GetAllAsync(sql, new { request.Pagination.Search });
result.Languages.AddRange(language);
result.TotalData = await languageDapper.CountAsync("Learning", "Languages",
where,
new { request.Pagination.Search },"lan");
return result;
}
}
@@ -23,7 +33,13 @@ public class GetLanguageByTitleQueryHandler(IGenericDapper<LanguageDTO> language
{
public async Task<LanguageDTO> Handle(GetLanguageByTitleQuery request, CancellationToken cancellationToken)
{
var sql = $"select * from Learning.Languages where Title = @{nameof(Language.Title)}";
var sql = $"select " +
$"lan.{nameof(Language.Title)} as {nameof(LanguageDTO.Title)}," +
$"lan.{nameof(Language.Id)} as {nameof(LanguageDTO.Id)}," +
$"CONVERT(nvarchar(max),m.FileId) as {nameof(LanguageDTO.Icon)}" +
$" from Learning.Languages as lan " +
$"inner join Learning.Medias as m on m.{nameof(Media.Id)} = lan.iconId " +
$"where lan.{nameof(Language.Title)} = @Title";
var language = await languageDapper.GetAsync(sql, new { request.Title });
return language;
@@ -33,8 +49,11 @@ public class GetLanguageByIdQueryHandler(IGenericDapper<LanguageDTO> languageDap
{
public async Task<LanguageDTO> Handle(GetLanguageByIdQuery request, CancellationToken cancellationToken)
{
var sql = $"select * from Learning.Languages where Id = " +
$" @{nameof(Language.Id)}";
var sql = $"select " +
$"lan.{nameof(Language.Title)} as {nameof(LanguageDTO.Title)}," +
$"lan.{nameof(Language.Id)} as {nameof(LanguageDTO.Id)}," +
$"CONVERT(nvarchar(max),m.FileId) as {nameof(LanguageDTO.Icon)}" +
$"from Learning.Languages as lan where lan.Id = @{nameof(Language.Id)}";
var language = await languageDapper.GetAsync(sql, new { request.Id });
return language;
+8 -3
View File
@@ -10,21 +10,26 @@ public class Course : Entity<CourseId>
{
}
private Course(CourseTitle title, int order)
private Course(CourseTitle title, int order, Media image, CourseDescription? description)
{
Title = title;
Order = order;
Image = image;
Description = description;
}
public CourseTitle Title { get; private set; }
public Media Image { get; private set; }
public CourseDescription? Description { get; set; }
public int Order { get; private set; }
internal static Course CreateNew(CourseTitle title, int order)
=> new(title, order);
internal static Course CreateNew(CourseTitle title, int order, Media image, CourseDescription? description)
=> new(title, order, image, description);
public void SetTitle(CourseTitle title)
=> Title = title;
public void SetOrder(int order)
=> Order = order;
public void SetImage(Media media)
=> Image = media;
public void SetDescription(CourseDescription? description)
=> Description = description;
}
@@ -13,7 +13,7 @@ public class FileGroup : Entity<FileGroupId>
private FileGroup(FileGroupKey key,
FileGroupTitle title,
bool isSecure,
long mediaGroupId)
Guid mediaGroupId)
{
Title = title;
MediaGroupId = mediaGroupId;
@@ -22,12 +22,12 @@ public class FileGroup : Entity<FileGroupId>
}
public FileGroupTitle Title { get; set; }
public FileGroupKey Key { get; private set; }
public long MediaGroupId { get; private set; }
public Guid MediaGroupId { get; private set; }
public bool IsSecure { get; set; }
public static FileGroup CreateNew(FileGroupKey key,
FileGroupTitle title,
bool isSecure,
long mediaGroupId)
Guid mediaGroupId)
=> new(key,title, isSecure, mediaGroupId);
}
+15 -13
View File
@@ -12,15 +12,16 @@ public class Language : Entity<LanguageId>, IAggregateRoot
{
// only EF
}
private Language(LanguageTitle title)
private Language(LanguageTitle title, Media icon)
{
Title = title;
Icon = icon;
}
public LanguageTitle Title { get; private set; }
public Media Icon { get; private set; }
public IReadOnlyList<Course> Courses => _courses;
public static Language CreateNew(LanguageTitle title)
=> new(title);
public static Language CreateNew(LanguageTitle title, Media icon)
=> new(title, icon);
public void SetTitle(LanguageTitle title)
=> Title = title;
@@ -31,27 +32,28 @@ public class Language : Entity<LanguageId>, IAggregateRoot
Icon = icon;
}
public Course AddCourse(CourseTitle courseTitle)
public Course AddCourse(CourseTitle courseTitle, Media image, CourseDescription? courseDescription)
{
var order = Courses.Count;
var course = Course.CreateNew(courseTitle, order);
var course = Course.CreateNew(courseTitle, order, image, courseDescription);
_courses.Add(course);
return course;
}
public Course UpdateCourse(Course course)
public Course UpdateCourse(CourseId courseId,CourseTitle courseTitle,int order, Media? image, CourseDescription? courseDescription)
{
var _course = _courses.FirstOrDefault(x => x.Id == course.Id) ?? throw new CourseNotFoundException(Title);
if(course.Image != null)
_course.SetImage(course.Image);
_course.SetTitle(course.Title);
var _course = _courses.FirstOrDefault(x => x.Id == courseId) ?? throw new CourseNotFoundException(Title);
if (image != null)
_course.SetImage(image);
_course.SetTitle(courseTitle);
_course.SetDescription(courseDescription);
if(_course.Order != course.Order)
if (_course.Order != order)
{
var orderCourses = _courses.Where(x => x.Id != course.Id)
var orderCourses = _courses.Where(x => x.Id != courseId)
.OrderBy(x => x.Order)
.ToList();
_course.SetOrder(_course.Order);
orderCourses.Insert(course.Order, _course);
orderCourses.Insert(order, _course);
}
return _course;
}
+18 -3
View File
@@ -8,26 +8,41 @@ public class Media : Entity<MediaId>
{
private Media()
{
}
private Media(string name,
string contentType,
int size,
Guid fileId,
EntityType type)
{
Name = name;
ContentType = contentType;
Size = size;
FileId = fileId;
EntityType = type;
}
public string Name { get; private set; }
public string ContentType { get; private set; }
public string ContentType { get; private set; }
public int Size { get; private set; }
public Guid FileId { get; set; }
public EntityType EntityType { get; private set; }
public static Media CreateNew(string name,
string contentType,
int size,
Guid fileId,
EntityType type)
=> new(name, contentType, size, type);
=> new(name, contentType, size, fileId, type);
public void SetFileId(Guid fileId)
=> FileId = fileId;
public void SetName(string name)
=> Name = name;
public void SetContentType(string contentType)
=> ContentType = contentType;
public void SetSize(int size)
=> Size = size;
public void SetEntityType(EntityType entityType)
=> EntityType = entityType;
}
@@ -6,5 +6,5 @@ namespace VizhTalk.Learning.Domain.Repositories;
public interface ILanguageRepository : IRepositoryBase<Language,LanguageId>
{
Task<Language?> GetLanguageWithCoursesById(LanguageId languageId ,CancellationToken cancellationToken = default);
}
+2 -2
View File
@@ -2,9 +2,9 @@
namespace VizhTalk.Learning.Domain.TypeIds;
public class MediaId : TypeIdValueBase<Guid>
public class MediaId : TypeIdValueBase<long>
{
public MediaId(Guid value) : base(value)
public MediaId(long value) : base(value)
{
}
}
@@ -0,0 +1,19 @@
using VizhTalk.BuildingBlocks.Domain;
using VizhTalk.BuildingBlocks.Domain.Guards;
namespace VizhTalk.Learning.Domain.ValueObjects;
public class CourseDescription : ValueObject
{
public const short MaxLength = 800;
public CourseDescription(string description)
{
Guard.NullOrEmpty(description,nameof(CourseDescription));
Guard.MaxLength(description, nameof(CourseDescription), MaxLength);
Value = description;
}
public string Value { get; set; }
public static implicit operator string(CourseDescription title) => title.Value;
public static implicit operator CourseDescription(string title) => new(title);
}
@@ -14,5 +14,6 @@ public class CourseTitle
public string Value { get; private set; }
public static implicit operator string(CourseTitle title) => title.Value;
public static implicit operator CourseTitle(string title) => new(title);
}
@@ -4,15 +4,14 @@ namespace VizhTalk.Learning.Domain.ValueObjects;
public class EntityType : ValueObject
{
public EntityType(string entityId, EntityTypeEnum type)
public EntityType(EntityTypeEnum type)
{
EntityId = entityId;
Type = type;
}
public string EntityId { get; private set; }
public EntityTypeEnum Type { get; private set; }
}
public enum EntityTypeEnum
{
Language = 0
Language = 0,
Course = 1
}
@@ -11,6 +11,7 @@ namespace VizhTalk.Learning.Domain.ValueObjects
public class FileGroupKey : ValueObject
{
public static FileGroupKey IconLanguage = new("IconLanguage");
public static FileGroupKey ImageCourse = new("ImageCourse");
public FileGroupKey(string entityName, string? entityId = null)
{
Guard.NullOrEmpty(entityName, entityName);
@@ -9,6 +9,7 @@ public class FileGroupTitle : ValueObject
{
Guard.NullOrEmpty(value, nameof(FileGroupTitle));
Guard.MaxLength(value, nameof(FileGroupTitle), 50);
Value = value;
}
public string Value { get; set; }
}
@@ -21,10 +21,18 @@ public class GenericDapper<T> : IGenericDapper<T>
{
return await connection.QueryAsync<T>(sql, param);
}
public async Task<int> CountAsync(string schema,string tableName, string where, object? param = null, string? alias = null)
{
string query = $"select count(1) as count from {schema}.{tableName} " +
(string.IsNullOrEmpty(alias) ? "" : $"as {alias}") +
$" where {where}";
return await connection.QueryFirstAsync<int>(query, param);
}
public void Dispose()
{
GC.SuppressFinalize(this);
connection.Dispose();
}
}
@@ -1,11 +1,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VizhTalk.Learning.Domain.Entities;
using VizhTalk.Learning.Domain.ValueObjects;
namespace VizhTalk.Learning.Infrastructure.EF.Configurations
{
@@ -23,6 +19,12 @@ namespace VizhTalk.Learning.Infrastructure.EF.Configurations
builder.Property(x => x.Title)
.HasConversion(x => x.Value, x => new(x));
builder.Property(x => x.Description)
.HasMaxLength(CourseDescription.MaxLength)
.HasConversion(x => x.Value, x => new(x));
builder.Navigation(x => x.Image)
.AutoInclude();
}
}
}
@@ -15,6 +15,7 @@ internal class FileGroupConfiguration : IEntityTypeConfiguration<FileGroup>
builder.Property(x => x.Id)
.ValueGeneratedOnAdd()
.HasConversion(x => x.Value, x => new Domain.TypeIds.FileGroupId(x));
builder.Property(x => x.Title)
.HasColumnName("Title")
.HasConversion(x => x.Value, x => new FileGroupTitle(x));
@@ -24,6 +24,9 @@ namespace VizhTalk.Learning.Infrastructure.EF.Configurations
builder.Property(x => x.Title)
.HasConversion(x => x.Value, x => new LanguageTitle(x))
.HasMaxLength(60);
builder.Navigation(x => x.Icon)
.AutoInclude();
}
}
}
@@ -12,7 +12,8 @@ internal class MediaConfiguration : IEntityTypeConfiguration<Media>
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaId(x));
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaId(x))
.ValueGeneratedOnAdd();
builder.Property(x => x.Name)
.HasMaxLength(150);
@@ -24,10 +25,6 @@ internal class MediaConfiguration : IEntityTypeConfiguration<Media>
op.Property(x => x.Type)
.HasColumnName("EntityType")
.HasMaxLength(200);
op.Property(x => x.EntityId)
.HasColumnName("EntityId")
.HasMaxLength(50);
});
}
}
@@ -2,6 +2,7 @@
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using VizhTalk.BuildingBlocks.Domain;
using VizhTalk.BuildingBlocks.Infrastructure.EF.Helpers;
using VizhTalk.Learning.Domain.Entities;
using VizhTalk.Learning.Infrastructure.Configuration;
namespace VizhTalk.Learning.Infrastructure.EF;
@@ -22,5 +23,6 @@ public class LearningDbContext : DbContext
modelBuilder.FindEntities(Assemblies.Domain)
.ApplyConfigurationsFromAssembly(Assemblies.Infrastructure, x => x.FullName.Contains("EF.Configurations"))
.PluralizeEntities();
}
}
@@ -10,6 +10,7 @@ using VizhTalk.BuildingBlocks.Application.Outbox;
using VizhTalk.BuildingBlocks.Infrastructure;
using VizhTalk.BuildingBlocks.Infrastructure.DomainEventsDispatching;
using VizhTalk.Grpc.CDN;
using VizhTalk.Grpc.Learning.Language;
using VizhTalk.Learning.Application;
using VizhTalk.Learning.Application.Mappers;
using VizhTalk.Learning.Infrastructure.Configuration;
@@ -29,6 +30,10 @@ public static class LearningStartup
{
op.UseSqlServer(connectionString);
});
services.AddGrpcClient<CDNGrpc.CDNGrpcClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
});
services.AddScoped<DbContext>(sp => sp.GetService<LearningDbContext>());
services.AddScoped<ILearningModule, LearningModule>();
services.AddScoped<IExecutor, LearningModule>();
@@ -1,183 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using VizhTalk.Learning.Infrastructure.EF;
#nullable disable
namespace VizhTalk.Learning.Infrastructure.Migrations
{
[DbContext(typeof(LearningDbContext))]
[Migration("20250905153842_initial")]
partial class initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Learning")
.HasAnnotation("ProductVersion", "8.0.19")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.FileGroup", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<bool>("IsSecure")
.HasColumnType("bit");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("nvarchar(150)")
.HasColumnName("Key");
b.Property<DateTime>("LastUpdate")
.HasColumnType("datetime2");
b.Property<long>("MediaGroupId")
.HasColumnType("bigint");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("Title");
b.HasKey("Id");
b.ToTable("FileGroups", "Attachment");
});
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.Language", b =>
{
b.Property<short>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("smallint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<short>("Id"));
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<Guid>("IconId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime>("LastUpdate")
.HasColumnType("datetime2");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.HasKey("Id");
b.HasIndex("IconId");
b.ToTable("Languages", "Learning");
});
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.Media", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("ContentType")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<DateTime>("LastUpdate")
.HasColumnType("datetime2");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("nvarchar(150)");
b.Property<int>("Size")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Medias", "Learning");
});
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.Language", b =>
{
b.HasOne("VizhTalk.Learning.Domain.Entities.Media", "Icon")
.WithMany()
.HasForeignKey("IconId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Icon");
});
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.Media", b =>
{
b.OwnsOne("VizhTalk.Learning.Domain.ValueObjects.EntityType", "EntityType", b1 =>
{
b1.Property<Guid>("MediaId")
.HasColumnType("uniqueidentifier");
b1.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("EntityId");
b1.Property<int>("Type")
.HasMaxLength(200)
.HasColumnType("int")
.HasColumnName("EntityType");
b1.HasKey("MediaId");
b1.ToTable("Medias", "Learning");
b1.WithOwner()
.HasForeignKey("MediaId");
});
b.Navigation("EntityType")
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -1,69 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace VizhTalk.Learning.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class Course : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Courses",
schema: "Learning",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: false),
ImageId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Order = table.Column<int>(type: "int", nullable: false),
LanguageId = table.Column<short>(type: "smallint", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false),
CreateOn = table.Column<DateTime>(type: "datetime2", nullable: false),
LastUpdate = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Courses", x => x.Id);
table.ForeignKey(
name: "FK_Courses_Languages_LanguageId",
column: x => x.LanguageId,
principalSchema: "Learning",
principalTable: "Languages",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Courses_Medias_ImageId",
column: x => x.ImageId,
principalSchema: "Learning",
principalTable: "Medias",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Courses_ImageId",
schema: "Learning",
table: "Courses",
column: "ImageId");
migrationBuilder.CreateIndex(
name: "IX_Courses_LanguageId",
schema: "Learning",
table: "Courses",
column: "LanguageId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Courses",
schema: "Learning");
}
}
}
@@ -12,8 +12,8 @@ using VizhTalk.Learning.Infrastructure.EF;
namespace VizhTalk.Learning.Infrastructure.Migrations
{
[DbContext(typeof(LearningDbContext))]
[Migration("20250907192005_Course")]
partial class Course
[Migration("20250912083328_initial")]
partial class initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -37,8 +37,8 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<Guid>("ImageId")
.HasColumnType("uniqueidentifier");
b.Property<long>("ImageId")
.HasColumnType("bigint");
b.Property<bool>("IsActive")
.HasColumnType("bit");
@@ -97,8 +97,8 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
b.Property<DateTime>("LastUpdate")
.HasColumnType("datetime2");
b.Property<long>("MediaGroupId")
.HasColumnType("bigint");
b.Property<Guid>("MediaGroupId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Title")
.IsRequired()
@@ -121,8 +121,8 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<Guid>("IconId")
.HasColumnType("uniqueidentifier");
b.Property<long>("IconId")
.HasColumnType("bigint");
b.Property<bool>("IsActive")
.HasColumnType("bit");
@@ -147,8 +147,11 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.Media", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("ContentType")
.IsRequired()
@@ -157,6 +160,9 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<Guid>("FileId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("IsActive")
.HasColumnType("bit");
@@ -209,14 +215,8 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
{
b.OwnsOne("VizhTalk.Learning.Domain.ValueObjects.EntityType", "EntityType", b1 =>
{
b1.Property<Guid>("MediaId")
.HasColumnType("uniqueidentifier");
b1.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("EntityId");
b1.Property<long>("MediaId")
.HasColumnType("bigint");
b1.Property<int>("Type")
.HasMaxLength(200)
@@ -12,10 +12,10 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "Attachment");
name: "Learning");
migrationBuilder.EnsureSchema(
name: "Learning");
name: "Attachment");
migrationBuilder.CreateTable(
name: "FileGroups",
@@ -26,7 +26,7 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: false),
Key = table.Column<string>(type: "nvarchar(150)", maxLength: 150, nullable: false),
MediaGroupId = table.Column<long>(type: "bigint", nullable: false),
MediaGroupId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
IsSecure = table.Column<bool>(type: "bit", nullable: false),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false),
@@ -43,11 +43,12 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
schema: "Learning",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(150)", maxLength: 150, nullable: false),
ContentType = table.Column<string>(type: "nvarchar(max)", nullable: false),
Size = table.Column<int>(type: "int", nullable: false),
EntityId = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
FileId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
EntityType = table.Column<int>(type: "int", maxLength: 200, nullable: false),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false),
@@ -67,7 +68,7 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
Id = table.Column<short>(type: "smallint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(60)", maxLength: 60, nullable: false),
IconId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
IconId = table.Column<long>(type: "bigint", nullable: false),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false),
CreateOn = table.Column<DateTime>(type: "datetime2", nullable: false),
@@ -85,6 +86,52 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Courses",
schema: "Learning",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: false),
ImageId = table.Column<long>(type: "bigint", nullable: false),
Order = table.Column<int>(type: "int", nullable: false),
LanguageId = table.Column<short>(type: "smallint", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false),
CreateOn = table.Column<DateTime>(type: "datetime2", nullable: false),
LastUpdate = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Courses", x => x.Id);
table.ForeignKey(
name: "FK_Courses_Languages_LanguageId",
column: x => x.LanguageId,
principalSchema: "Learning",
principalTable: "Languages",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Courses_Medias_ImageId",
column: x => x.ImageId,
principalSchema: "Learning",
principalTable: "Medias",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Courses_ImageId",
schema: "Learning",
table: "Courses",
column: "ImageId");
migrationBuilder.CreateIndex(
name: "IX_Courses_LanguageId",
schema: "Learning",
table: "Courses",
column: "LanguageId");
migrationBuilder.CreateIndex(
name: "IX_Languages_IconId",
schema: "Learning",
@@ -95,6 +142,10 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Courses",
schema: "Learning");
migrationBuilder.DropTable(
name: "FileGroups",
schema: "Attachment");
@@ -34,8 +34,8 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<Guid>("ImageId")
.HasColumnType("uniqueidentifier");
b.Property<long>("ImageId")
.HasColumnType("bigint");
b.Property<bool>("IsActive")
.HasColumnType("bit");
@@ -94,8 +94,8 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
b.Property<DateTime>("LastUpdate")
.HasColumnType("datetime2");
b.Property<long>("MediaGroupId")
.HasColumnType("bigint");
b.Property<Guid>("MediaGroupId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Title")
.IsRequired()
@@ -118,8 +118,8 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<Guid>("IconId")
.HasColumnType("uniqueidentifier");
b.Property<long>("IconId")
.HasColumnType("bigint");
b.Property<bool>("IsActive")
.HasColumnType("bit");
@@ -144,8 +144,11 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.Media", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("ContentType")
.IsRequired()
@@ -154,6 +157,9 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
b.Property<DateTime>("CreateOn")
.HasColumnType("datetime2");
b.Property<Guid>("FileId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("IsActive")
.HasColumnType("bit");
@@ -206,14 +212,8 @@ namespace VizhTalk.Learning.Infrastructure.Migrations
{
b.OwnsOne("VizhTalk.Learning.Domain.ValueObjects.EntityType", "EntityType", b1 =>
{
b1.Property<Guid>("MediaId")
.HasColumnType("uniqueidentifier");
b1.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)")
.HasColumnName("EntityId");
b1.Property<long>("MediaId")
.HasColumnType("bigint");
b1.Property<int>("Type")
.HasMaxLength(200)
@@ -1,4 +1,5 @@
using VizhTalk.BuildingBlocks.Infrastructure;
using Microsoft.EntityFrameworkCore;
using VizhTalk.BuildingBlocks.Infrastructure;
using VizhTalk.Learning.Domain.Entities;
using VizhTalk.Learning.Domain.Repositories;
using VizhTalk.Learning.Domain.TypeIds;
@@ -8,8 +9,18 @@ namespace VizhTalk.Learning.Infrastructure.Repositories
{
internal class LanguageRepository : RepositoryBase<Language, LanguageId>, ILanguageRepository
{
private readonly LearningDbContext dbContext;
public LanguageRepository(LearningDbContext dbContext) : base(dbContext)
{
this.dbContext = dbContext;
}
public async Task<Language?> GetLanguageWithCoursesById(LanguageId languageId, CancellationToken cancellationToken = default)
{
return await dbContext.Set<Language>()
.Include(x => x.Courses)
.FirstOrDefaultAsync(x => x.Id == languageId, cancellationToken);
}
}
}
@@ -0,0 +1,24 @@
using Grpc.Core.Interceptors;
using Grpc.Core;
namespace VizhTalk.Learning.Presentation.Interceptors;
public class ExceptionInterceptor : Interceptor
{
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
try
{
return await continuation(request, context);
}
catch (Exception ex)
{
throw new RpcException(
new Status(StatusCode.Internal, "Unhandled exception: " + ex.Message)
);
}
}
}
+8 -2
View File
@@ -1,13 +1,18 @@
using VizhTalk.Learning.Infrastructure;
using VizhTalk.Learning.Presentation.Interceptors;
using VizhTalk.Learning.Presentation.Services;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("LearningDB")!;
// Add services to the container.
builder.Services.AddGrpc();
builder.Services.AddGrpc(op =>
{
op.MaxReceiveMessageSize = 200 * 1024 * 1024;
op.MaxSendMessageSize = 200 * 1024 * 1024;
op.Interceptors.Add<ExceptionInterceptor>();
});
builder.Services.Initialize(connectionString);
var app = builder.Build();
@@ -15,4 +20,5 @@ var app = builder.Build();
app.MapGrpcService<LanguageService>();
app.UseLearningModule();
app.Run();
@@ -0,0 +1,32 @@
using Grpc.Core;
using VizhTalk.Grpc.Learning.Course;
using VizhTalk.Learning.Infrastructure.Configuration;
namespace VizhTalk.Learning.Presentation.Services;
public class CourseService(ILearningModule learningModule) : CourseGrpc.CourseGrpcBase
{
public override async Task<CourseDTO> Create(CreateCourseCommand request, ServerCallContext context)
{
return await learningModule.Execute(request, context.CancellationToken);
}
public async override Task<CourseDTO> Update(UpdateCourseCommand request, ServerCallContext context)
{
return await learningModule.Execute(request, context.CancellationToken);
}
public async override Task<GetAllCourseDTO> GetAll(GetAllCourseQuery request, ServerCallContext context)
{
return await learningModule.Execute(request, context.CancellationToken);
}
public async override Task<CourseDTO> GetById(GetCourseByIdQuery request, ServerCallContext context)
{
return await learningModule.Execute(request, context.CancellationToken);
}
public async override Task<CourseDTO> GetByTitle(GetCoursesByTitleQuery request, ServerCallContext context)
{
return await learningModule.Execute(request, context.CancellationToken);
}
}
@@ -17,7 +17,6 @@ public class LanguageService(ILearningModule learningModule) : LanguageGrpc.Lang
}
public override async Task<LanguageDTO> GetByTitle(GetLanguageByTitleQuery request, ServerCallContext context)
{
await Task.Delay(10000);
return await learningModule.Execute(request, context.CancellationToken);
}
public override async Task<GetAllLanguageDTO> GetAll(GetAllLanguageQuery request, ServerCallContext context)
@@ -6,7 +6,7 @@
}
},
"ConnectionStrings": {
"LearningDB": "Server=.; Database=VizhTalk.Learning; Trusted_Connection=True; MultipleActiveResultSets=true;TrustServerCertificate=True;"
"LearningDB": "Server=.\\SQLEXPRESS; Database=VizhTalk.Learning; Trusted_Connection=True; MultipleActiveResultSets=true;TrustServerCertificate=True;"
},
"AllowedHosts": "*",
"Kestrel": {
@@ -0,0 +1,6 @@
using VizhTalk.BuildingBlocks.Domain.Contract;
namespace VizhTalk.Grpc.Learning.Course;
public sealed partial class CreateCourseCommand: ICommand<CourseDTO>;
public sealed partial class UpdateCourseCommand : ICommand<CourseDTO>;
@@ -1,5 +1,4 @@
using VizhTalk.BuildingBlocks.Domain.Contract;
using VizhTalk.Grpc.Learning.Language;
namespace VizhTalk.Grpc.Learning.Language;
@@ -8,11 +8,16 @@ import "Proto/Shared.proto";
message CreateCourseCommand{
string title = 1;
bytes icon = 2;
string description = 2;
UploadFile image = 3;
Id languageid = 4;
}
message UpdateCourseCommand{
Id Id = 1;
string title = 2;
optional bytes icon = 3;
string description = 3;
int32 order = 4;
optional UploadFile image = 5;
Id languageid = 6;
}
@@ -7,7 +7,10 @@ import "Proto/Shared.proto";
message CourseDTO {
Id Id = 1;
string title = 2;
Id languageId = 2;
string title = 3;
string image = 4;
string description = 5;
}
@@ -9,7 +9,7 @@ import "Proto/Shared.proto";
message GetAllCourseQuery{
Pagination pagination = 1;
}
message GetCourseByTitleQuery{
message GetCoursesByTitleQuery{
string title = 1;
}
@@ -17,6 +17,6 @@ service CourseGrpc {
rpc Create (CreateCourseCommand) returns (CourseDTO);
rpc Update (UpdateCourseCommand) returns (CourseDTO);
rpc GetAll (GetAllCourseQuery) returns (GetAllCourseDTO);
rpc GetByTitle (GetCourseByTitleQuery) returns (CourseDTO);
rpc GetByTitle (GetCoursesByTitleQuery) returns (CourseDTO);
rpc GetById (GetCourseByIdQuery) returns (CourseDTO);
}
@@ -8,6 +8,7 @@ import "Proto/Shared.proto";
message LanguageDTO {
Id Id = 1;
string title = 2;
string icon = 3;
}
@@ -0,0 +1,10 @@
using VizhTalk.BuildingBlocks.Domain.Contract;
namespace VizhTalk.Grpc.Learning.Course;
public sealed partial class GetAllCourseQuery : IQuery<GetAllCourseDTO>;
public sealed partial class GetCoursesByTitleQuery : IQuery<CourseDTO>;
public sealed partial class GetCourseByIdQuery : IQuery<CourseDTO>;
@@ -14,25 +14,24 @@ using VizhTalk.Grpc.CDN;
namespace Vizhtalk.CDN.Presentation.Application.CommandHandlers;
public class CreateMediaGroupCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcClient cdnClient) : ICommandHandler<CreateMediaGroupCommand, MediaGroupDTO>
public class CreateMediaGroupCommandHandler(IUnitOfWork unitOfWork) : ICommandHandler<CreateMediaGroupCommand, MediaGroupDTO>
{
public async Task<MediaGroupDTO> Handle(CreateMediaGroupCommand request, CancellationToken cancellationToken)
{
var title = request.Title.Trim();
if (await unitOfWork.MediaGroup.ExistsAsync(a => a.Key == request.Key))
throw new MediaGroupKeyAlreadyExistsException();
var mediaGroup = await unitOfWork.MediaGroup.GetAsync(a => a.Key == request.Key);
if (mediaGroup != null)
return mediaGroup.Adapt<MediaGroupDTO>();
var mediaGroup = MediaGroup.CreateNew(title, request.Key, request.IsSecure);
mediaGroup = MediaGroup.CreateNew(title, request.Key, request.IsSecure);
await unitOfWork.MediaGroup.AddAsync(mediaGroup, cancellationToken);
await unitOfWork.SaveChangeAsync(cancellationToken);
return mediaGroup.Adapt<MediaGroupDTO>();
}
}
public class UploadFileCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcClient cdnClient, IFileManagerService fileManager) : ICommandHandler<UploadFileCommand, UploadFileDTO>
public class UploadFileCommandHandler(IUnitOfWork unitOfWork, IFileManagerService fileManager) : ICommandHandler<UploadFileCommand, UploadFileDTO>
{
public async Task<UploadFileDTO> Handle(UploadFileCommand request, CancellationToken cancellationToken)
{
@@ -57,11 +56,11 @@ public class UploadFileCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcCli
var path = await fileManager.SaveFile(fileBytes, extension, mediaGroup.Id);
media = Media.CreateNew(new ContentType(request.File.ContentType, fileBytes), fileBytes.Length, hash, path);
}
var mediaGroupMedia = mediaGroup.AddMedia(new FileName(request.File.Name), media);
var mediaGroupMedia = mediaGroup.AddMedia(new FileName(request.File.Name), media);
if (!string.IsNullOrEmpty(request.ClientId))
mediaGroupMedia.AddMediaPermission(media, Client.CreateNew(new ClientId(request.ClientId)));
await unitOfWork.MediaGroup.AddAsync(mediaGroup, cancellationToken);
await unitOfWork.MediaGroup.UpdateAsync(mediaGroup, cancellationToken);
await unitOfWork.SaveChangeAsync(cancellationToken);
return new UploadFileDTO()
@@ -7,7 +7,7 @@ namespace Vizhtalk.CDN.Presentation.Application
{
IMediaRepository Media { get; }
IClientRepository Client { get; }
IMediaClientRepository MediaClient { get; }
IMediaPermissionRepository MediaClient { get; }
IMediaGroupRepository MediaGroup{ get; }
IMediaGroupMediaRepository MediaGroupMedia { get; }
@@ -1,20 +1,30 @@
using Microsoft.AspNetCore.Mvc;
using Vizhtalk.CDN.Presentation.Application;
using Vizhtalk.CDN.Presentation.Domain.TypeIds;
namespace Vizhtalk.CDN.Presentation.Controllers
{
[Route("api/[controller]")]
[Route("[controller]")]
[ApiController]
public class FileController : ControllerBase
public class FileController(IUnitOfWork unitOfWork) : ControllerBase
{
[HttpGet("{id}")]
public IActionResult Get(MediaGroupMediaId id)
public async Task<IActionResult> Get(Guid id)
{
var mediaGroupMedia = await unitOfWork.MediaGroupMedia
.GetDetailsById(new MediaGroupMediaId(id));
if (mediaGroupMedia == null)
return NotFound();
return Unauthorized();
if(!mediaGroupMedia.MediaGroup.IsSecure)
{
var fileBytes = System.IO.File.ReadAllBytes(mediaGroupMedia.Media.Path);
return File(fileBytes, mediaGroupMedia.Media.ContentType.Value);
}
return NotFound();
}
}
@@ -7,10 +7,13 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class Media : Entity<MediaId>
{
private Media(ContentType contentType, int size , string hash , string path)
private Media()
{
}
private Media(ContentType contentType, int size , string hash , string path)
{
Id = new MediaId(Guid.NewGuid());
ContentType = contentType;
Size = size;
Hash = hash;
@@ -6,9 +6,13 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class MediaGroup : Entity<MediaGroupId>
{
private MediaGroup()
{
}
private MediaGroup(MediaGroupTitle title, string key, bool isSecure)
{
Id = new MediaGroupId(Guid.NewGuid());
Title = title;
Key = key;
IsSecure = isSecure;
@@ -7,11 +7,15 @@ namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class MediaGroupMedia : Entity<MediaGroupMediaId>
{
private List<MediaPermission> _mediaPermissions = new();
private MediaGroupMedia()
{
}
private MediaGroupMedia(FileName fileName,
Media media,
MediaGroup mediaGroup)
{
Id = new MediaGroupMediaId(Guid.NewGuid());
Name = fileName;
Media = media;
MediaGroup = mediaGroup;
@@ -19,8 +23,8 @@ public class MediaGroupMedia : Entity<MediaGroupMediaId>
public FileName Name { get; private set; }
public MediaId MediaId { get; private set; }
public MediaGroupId MediaGroupId { get; private set; }
public Media Media { get; private set; }
public MediaGroup MediaGroup { get; private set; }
public Media Media { get; set; }
public MediaGroup MediaGroup { get; set; }
public IReadOnlyList<MediaPermission> MediaPermissions => _mediaPermissions;
internal static MediaGroupMedia CreateNew(FileName fileName,
@@ -3,7 +3,7 @@ using VizhTalk.BuildingBlocks.Domain;
namespace Vizhtalk.CDN.Presentation.Domain.Entities;
public class MediaPermission : Entity<MediaClientId>
public class MediaPermission : Entity<MediaPermissionId>
{
private MediaPermission()
{
@@ -11,6 +11,7 @@ public class MediaPermission : Entity<MediaClientId>
}
private MediaPermission(Media media, Client client, MediaGroupMedia mediaGroupMedia)
{
Id = new MediaPermissionId(Guid.NewGuid());
Media = media;
Client = client;
MediaGroupMedia = mediaGroupMedia;
@@ -4,4 +4,7 @@ using VizhTalk.BuildingBlocks.Domain;
namespace Vizhtalk.CDN.Presentation.Domain.Repositories;
public interface IMediaGroupMediaRepository : IRepositoryBase<MediaGroupMedia, MediaGroupMediaId>;
public interface IMediaGroupMediaRepository : IRepositoryBase<MediaGroupMedia, MediaGroupMediaId>
{
Task<MediaGroupMedia?> GetDetailsById(MediaGroupMediaId mediaGroupMediaId,CancellationToken cancellationToken = default);
}
@@ -4,4 +4,4 @@ using VizhTalk.BuildingBlocks.Domain;
namespace Vizhtalk.CDN.Presentation.Domain.Repositories;
public interface IMediaClientRepository : IRepositoryBase<MediaPermission, MediaClientId>;
public interface IMediaPermissionRepository : IRepositoryBase<MediaPermission, MediaPermissionId>;
@@ -2,9 +2,9 @@
namespace Vizhtalk.CDN.Presentation.Domain.TypeIds;
public class MediaId : TypeIdValueBase<long>
public class MediaId : TypeIdValueBase<Guid>
{
public MediaId(long value) : base(value)
public MediaId(Guid value) : base(value)
{
}
}
@@ -2,9 +2,9 @@
namespace Vizhtalk.CDN.Presentation.Domain.TypeIds;
public class MediaClientId : TypeIdValueBase<long>
public class MediaPermissionId : TypeIdValueBase<Guid>
{
public MediaClientId(long value) : base(value)
public MediaPermissionId(Guid value) : base(value)
{
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

@@ -1,6 +1,8 @@
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
using Vizhtalk.CDN.Presentation.Application;
using Vizhtalk.CDN.Presentation.Application.Mappers;
using Vizhtalk.CDN.Presentation.FileManagerServices;
using Vizhtalk.CDN.Presentation.Infrastructure.Configuration;
using Vizhtalk.CDN.Presentation.Infrastructure.EF;
using VizhTalk.BuildingBlocks.Application;
@@ -18,6 +20,9 @@ public static class CDNStartup
{
public static void Initialize(this IServiceCollection services, string connectionString, string databaseName)
{
var domainNotificationsMap = new BiDictionary<string, Type>();
services.AddDbContext<CDNDbContext>(options =>
{
options.UseMongoDB(connectionString, databaseName);
@@ -25,6 +30,46 @@ public static class CDNStartup
services.AddScoped<DbContext>(sp => sp.GetService<CDNDbContext>());
services.Scan(scan => scan.
FromAssemblies(Assemblies.Application, Assemblies.Infrastructure)
.AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<>)), publicOnly: false)
.AsImplementedInterfaces()
.WithScopedLifetime()
.AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<,>)), publicOnly: false)
.AsImplementedInterfaces()
.WithScopedLifetime()
.AddClasses(classes => classes.AssignableTo(typeof(IQueryHandler<,>)), publicOnly: false)
.AsImplementedInterfaces()
.WithScopedLifetime()
.AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<>)), publicOnly: false)
.AsImplementedInterfaces()
.WithScopedLifetime());
services.AddScoped<IFileManagerService, FileManagerService>();
services.AddScoped<DbContext>(sp => sp.GetService<CDNDbContext>());
services.AddScoped<ICDNModule, CDNModule>();
services.AddScoped<IExecutor, CDNModule>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<IDomainEventsAccessor, DomainEventsAccessor>();
services.AddScoped<IDomainEventsDispatcher, DomainEventsDispatcher>();
services.AddScoped<IDomainNotificationsMapper, DomainNotificationsMapper>();
services.AddScoped(sp => domainNotificationsMap);
MapperConfig.Process();
services.AddScoped(sa =>
{
var notifications = sa.GetServices<IDomainEventNotification>();
var notificationDic = new BiDictionary<string, Type>();
foreach (var notification in notifications)
notificationDic.Add(notification!.ToString()!, notification!.GetType());
return notificationDic;
});
services.AddScoped<IOutbox>(sa =>
{
return null; // ToDo
});
}
@@ -9,7 +9,7 @@ namespace Vizhtalk.CDN.Presentation.Infrastructure.EF
public CDNDbContext(DbContextOptions<CDNDbContext> options) : base(options)
{
Database.AutoTransactionBehavior = AutoTransactionBehavior.Never;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -19,6 +19,5 @@ namespace Vizhtalk.CDN.Presentation.Infrastructure.EF
.ApplyConfigurationsFromAssembly(Assemblies.Infrastructure, x => x.FullName.Contains("EF.Configurations"))
.PluralizeEntities();
}
}
}
@@ -12,7 +12,7 @@ public class MediaClientConfiguration : IEntityTypeConfiguration<MediaPermission
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaClientId(x));
.HasConversion(x => x.Value, x => new Domain.TypeIds.MediaPermissionId(x));
builder.Property(x => x.ClientId)
@@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using Vizhtalk.CDN.Presentation.Domain.Entities;
using Vizhtalk.CDN.Presentation.Domain.Repositories;
using Vizhtalk.CDN.Presentation.Domain.TypeIds;
using Vizhtalk.CDN.Presentation.Infrastructure.EF;
using VizhTalk.BuildingBlocks.Infrastructure;
namespace Vizhtalk.CDN.Presentation.Infrastructure.Repositories
{
public class ClientRepository : RepositoryBase<Client, ClientId>, IClientRepository
{
public ClientRepository(CDNDbContext dbContext) : base(dbContext)
{
}
}
}
@@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore;
using Vizhtalk.CDN.Presentation.Domain.Entities;
using Vizhtalk.CDN.Presentation.Domain.Repositories;
using Vizhtalk.CDN.Presentation.Domain.TypeIds;
using Vizhtalk.CDN.Presentation.Infrastructure.EF;
using VizhTalk.BuildingBlocks.Infrastructure;
namespace Vizhtalk.CDN.Presentation.Infrastructure.Repositories
{
public class MediaGroupMediaRepository : RepositoryBase<MediaGroupMedia, MediaGroupMediaId>, IMediaGroupMediaRepository
{
private readonly CDNDbContext dbContext;
public MediaGroupMediaRepository(CDNDbContext dbContext) : base(dbContext)
{
this.dbContext = dbContext;
}
public async Task<MediaGroupMedia?> GetDetailsById(MediaGroupMediaId mediaGroupMediaId, CancellationToken cancellationToken = default)
{
var mediaGroupMedia = await dbContext.Set<MediaGroupMedia>()
.FirstOrDefaultAsync(x => x.Id == mediaGroupMediaId, cancellationToken);
if (mediaGroupMedia == null)
return null;
// پر کردن دستی navigation properties
mediaGroupMedia.MediaGroup = await dbContext.Set<MediaGroup>()
.FirstOrDefaultAsync(mg => mg.Id == mediaGroupMedia.MediaGroupId, cancellationToken);
mediaGroupMedia.Media = await dbContext.Set<Media>()
.FirstOrDefaultAsync(m => m.Id == mediaGroupMedia.MediaId, cancellationToken);
return mediaGroupMedia;
}
}
}
@@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using Vizhtalk.CDN.Presentation.Domain.Entities;
using Vizhtalk.CDN.Presentation.Domain.Repositories;
using Vizhtalk.CDN.Presentation.Domain.TypeIds;
using Vizhtalk.CDN.Presentation.Infrastructure.EF;
using VizhTalk.BuildingBlocks.Infrastructure;
namespace Vizhtalk.CDN.Presentation.Infrastructure.Repositories
{
public class MediaGroupRepository : RepositoryBase<MediaGroup, MediaGroupId>, IMediaGroupRepository
{
public MediaGroupRepository(CDNDbContext dbContext) : base(dbContext)
{
}
}
}
@@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using Vizhtalk.CDN.Presentation.Domain.Entities;
using Vizhtalk.CDN.Presentation.Domain.Repositories;
using Vizhtalk.CDN.Presentation.Domain.TypeIds;
using Vizhtalk.CDN.Presentation.Infrastructure.EF;
using VizhTalk.BuildingBlocks.Infrastructure;
namespace Vizhtalk.CDN.Presentation.Infrastructure.Repositories
{
public class MediaPermissionRepository : RepositoryBase<MediaPermission, MediaPermissionId>, IMediaPermissionRepository
{
public MediaPermissionRepository(CDNDbContext dbContext) : base(dbContext)
{
}
}
}
@@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
using Vizhtalk.CDN.Presentation.Domain.Entities;
using Vizhtalk.CDN.Presentation.Domain.Repositories;
using Vizhtalk.CDN.Presentation.Domain.TypeIds;
using Vizhtalk.CDN.Presentation.Infrastructure.EF;
using VizhTalk.BuildingBlocks.Infrastructure;
namespace Vizhtalk.CDN.Presentation.Infrastructure.Repositories
{
public class MediaRepository : RepositoryBase<Media, MediaId>, IMediaRepository
{
public MediaRepository(CDNDbContext dbContext) : base(dbContext)
{
}
}
}
@@ -3,21 +3,73 @@ using Vizhtalk.CDN.Presentation.Infrastructure.EF;
using Vizhtalk.CDN.Presentation.Domain.Repositories;
using VizhTalk.BuildingBlocks.Infrastructure;
using VizhTalk.BuildingBlocks.Infrastructure.DomainEventsDispatching;
using Vizhtalk.CDN.Presentation.Infrastructure.Repositories;
namespace Vizhtalk.CDN.Presentation.Infrastructure
{
public class UnitOfWork(CDNDbContext cdnDbContext, IDomainEventsDispatcher domainEventsDispatcher)
: UnitOfWorkBase(cdnDbContext, domainEventsDispatcher)
: UnitOfWorkBase(cdnDbContext, domainEventsDispatcher), IUnitOfWork
{
public IMediaRepository Media => throw new NotImplementedException();
private IMediaRepository _mediaRepository;
public IMediaRepository Media
{
get
{
if (_mediaRepository == null)
_mediaRepository = new MediaRepository(cdnDbContext);
public IClientRepository Client => throw new NotImplementedException();
return _mediaRepository;
}
}
public IMediaClientRepository MediaClient => throw new NotImplementedException();
private IClientRepository _clientRepository;
public IClientRepository Client
{
get
{
if(_clientRepository == null)
_clientRepository = new ClientRepository(cdnDbContext);
return _clientRepository;
}
}
public IMediaGroupRepository MediaGroup => throw new NotImplementedException();
private IMediaPermissionRepository _mediaPermissionRepository;
public IMediaPermissionRepository MediaClient
{
get
{
if( _mediaPermissionRepository == null)
_mediaPermissionRepository = new MediaPermissionRepository(cdnDbContext);
public IMediaGroupMediaRepository MediaGroupMedia => throw new NotImplementedException();
return _mediaPermissionRepository;
}
}
private IMediaGroupRepository _mediaGroupRepository;
public IMediaGroupRepository MediaGroup
{
get
{
if(_mediaGroupRepository == null)
_mediaGroupRepository = new MediaGroupRepository(cdnDbContext);
return _mediaGroupRepository;
}
}
private IMediaGroupMediaRepository _mediaGroupMediaRepository;
public IMediaGroupMediaRepository MediaGroupMedia
{
get
{
if(_mediaGroupMediaRepository == null)
_mediaGroupMediaRepository = new MediaGroupMediaRepository(cdnDbContext);
return _mediaGroupMediaRepository;
}
}
public async Task SaveChangeAsync(CancellationToken cancellationToken = default)
=> await cdnDbContext.SaveChangesAsync(cancellationToken);
}
}
+7 -4
View File
@@ -8,18 +8,21 @@ var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("VizhTalk.CDN")!;
var databaseName = builder.Configuration.GetConnectionString("VizhTalk.CDN.DatabaseName")!;
builder.Services.AddScoped<IFileManagerService, FileManagerService>();
builder.Services.AddControllers();
builder.Services.Initialize(connectionString, databaseName);
builder.Services.AddGrpc();
builder.Services.AddGrpc(op =>
{
op.MaxReceiveMessageSize = 500 * 1024 * 1024;
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.UseRouting();
app.MapGrpcService<CDNService>();
app.MapControllers();
//app.UseCDNModule();
app.Run();
+1 -1
View File
@@ -7,7 +7,7 @@
},
"ConnectionStrings": {
"VizhTalk.CDN": "mongodb://localhost:27017",
"VizhTalk.CDN.DatabaseName": "VizhTalk.CDN"
"VizhTalk.CDN.DatabaseName": "VizhTalk_CDN"
},
"AllowedHosts": "*"
}