From 7ada54dbf57c4fcc72e7861fbf4f0fbc6f72a03d Mon Sep 17 00:00:00 2001 From: Alejandro Sarmiento Date: Sat, 17 Feb 2024 13:35:47 +0100 Subject: [PATCH] BaseRepository implementado Siguiente video: 50 Implementar metodos personalizados --- CleanArchitecture/.editorconfig | 4 + .../CleanArchitecture.API.csproj | 18 ++ .../CleanArchitecture.API.http | 6 + .../Controllers/StreamerController.cs | 51 +++++ .../Controllers/VideoController.cs | 29 +++ .../CleanArchitecture.API/Program.cs | 23 ++ .../Properties/launchSettings.json | 31 +++ .../appsettings.Development.json | 8 + .../CleanArchitecture.API/appsettings.json | 9 + .../ApplicationServiceRegistration.cs | 24 ++ .../Behaviours/UnhandledExceptionBehaviour.cs | 28 +++ .../Behaviours/ValidationBehaviour.cs | 32 +++ .../CleanArchitecture.Application.csproj | 26 +++ .../Contracts/Infrastructure/IEmailService.cs | 11 + .../Contracts/Persistence/IAsyncRepository.cs | 25 ++ .../Persistence/IStreamerRepository.cs | 14 ++ .../Contracts/Persistence/IVideoRepository.cs | 10 + .../Exceptions/NotFoundException.cs | 16 ++ .../Exceptions/ValidationException.cs | 18 ++ .../CreateStreamer/CreateStreamerCommand.cs | 10 + .../CreateStreamerCommandHandler.cs | 48 ++++ .../CreateStreamerCommandValidator.cs | 18 ++ .../DeleteStreamer/DeleteStreamerCommand.cs | 9 + .../DeleteStreamerCommandHandler.cs | 38 ++++ .../UpdateStreamer/UpdateStreamerCommand.cs | 11 + .../UpdateStreamerCommandHandler.cs | 42 ++++ .../UpdateStreamerCommandValidator.cs | 25 ++ .../GetVideosList/GetVideosListQuery.cs | 10 + .../GetVideosListQueryHandler.cs | 20 ++ .../Videos/Queries/GetVideosList/VideosVm.cs | 8 + .../Mappings/MappingProfile.cs | 17 ++ .../Models/Email.cs | 9 + .../Models/EmailSettings.cs | 9 + .../CleanArchitecture.ConsoleApp.csproj | 21 ++ .../CleanArchitecture.ConsoleApp/Program.cs | 114 ++++++++++ .../CleanArchitecture.Infrastructure.csproj | 27 +++ ...0240215100525_InitialMigration.Designer.cs | 73 ++++++ .../20240215100525_InitialMigration.cs | 72 ++++++ ...0240215190923_Second-Migration.Designer.cs | 163 +++++++++++++ .../20240215190923_Second-Migration.cs | 148 ++++++++++++ ...15201317_BaseDomainModelUpdate.Designer.cs | 215 ++++++++++++++++++ .../20240215201317_BaseDomainModelUpdate.cs | 187 +++++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 212 +++++++++++++++++ .../Persistence/StreamerDbContext.cs | 89 ++++++++ .../Persistence/StreamerDbContextSeed.cs | 29 +++ .../Repositories/RepositoryBase.cs | 84 +++++++ .../CleanArchitecture.Domain/Actor.cs | 17 ++ .../CleanArchitecture.Domain.csproj | 9 + .../Common/BaseDomainModel.cs | 18 ++ .../Common/ValueObject.cs | 41 ++++ .../CleanArchitecture.Domain/Director.cs | 13 ++ .../CleanArchitecture.Domain/Streamer.cs | 12 + .../CleanArchitecture.Domain/Video.cs | 22 ++ .../CleanArchitecture.Domain/VideoActor.cs | 12 + CleanArchitecture/CleanArchitecture.sln | 64 ++++++ 55 files changed, 2299 insertions(+) create mode 100644 CleanArchitecture/.editorconfig create mode 100644 CleanArchitecture/CleanArchitecture.API/CleanArchitecture.API.csproj create mode 100644 CleanArchitecture/CleanArchitecture.API/CleanArchitecture.API.http create mode 100644 CleanArchitecture/CleanArchitecture.API/Controllers/StreamerController.cs create mode 100644 CleanArchitecture/CleanArchitecture.API/Controllers/VideoController.cs create mode 100644 CleanArchitecture/CleanArchitecture.API/Program.cs create mode 100644 CleanArchitecture/CleanArchitecture.API/Properties/launchSettings.json create mode 100644 CleanArchitecture/CleanArchitecture.API/appsettings.Development.json create mode 100644 CleanArchitecture/CleanArchitecture.API/appsettings.json create mode 100644 CleanArchitecture/CleanArchitecture.Application/ApplicationServiceRegistration.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Behaviours/UnhandledExceptionBehaviour.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Behaviours/ValidationBehaviour.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/CleanArchitecture.Application.csproj create mode 100644 CleanArchitecture/CleanArchitecture.Application/Contracts/Infrastructure/IEmailService.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IAsyncRepository.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IStreamerRepository.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IVideoRepository.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Exceptions/NotFoundException.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Exceptions/ValidationException.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Streamers/Commands/CreateStreamer/CreateStreamerCommand.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Streamers/Commands/CreateStreamer/CreateStreamerCommandHandler.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Streamers/Commands/CreateStreamer/CreateStreamerCommandValidator.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Streamers/Commands/DeleteStreamer/DeleteStreamerCommand.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Streamers/Commands/DeleteStreamer/DeleteStreamerCommandHandler.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Streamers/Commands/UpdateStreamer/UpdateStreamerCommand.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Streamers/Commands/UpdateStreamer/UpdateStreamerCommandHandler.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Streamers/Commands/UpdateStreamer/UpdateStreamerCommandValidator.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Videos/Queries/GetVideosList/GetVideosListQuery.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Videos/Queries/GetVideosList/GetVideosListQueryHandler.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Features/Videos/Queries/GetVideosList/VideosVm.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Mappings/MappingProfile.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Models/Email.cs create mode 100644 CleanArchitecture/CleanArchitecture.Application/Models/EmailSettings.cs create mode 100644 CleanArchitecture/CleanArchitecture.ConsoleApp/CleanArchitecture.ConsoleApp.csproj create mode 100644 CleanArchitecture/CleanArchitecture.ConsoleApp/Program.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/CleanArchitecture.Infrastructure.csproj create mode 100644 CleanArchitecture/CleanArchitecture.Data/Migrations/20240215100525_InitialMigration.Designer.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Migrations/20240215100525_InitialMigration.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Migrations/20240215190923_Second-Migration.Designer.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Migrations/20240215190923_Second-Migration.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Migrations/20240215201317_BaseDomainModelUpdate.Designer.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Migrations/20240215201317_BaseDomainModelUpdate.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Migrations/ApplicationDbContextModelSnapshot.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Persistence/StreamerDbContext.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Persistence/StreamerDbContextSeed.cs create mode 100644 CleanArchitecture/CleanArchitecture.Data/Repositories/RepositoryBase.cs create mode 100644 CleanArchitecture/CleanArchitecture.Domain/Actor.cs create mode 100644 CleanArchitecture/CleanArchitecture.Domain/CleanArchitecture.Domain.csproj create mode 100644 CleanArchitecture/CleanArchitecture.Domain/Common/BaseDomainModel.cs create mode 100644 CleanArchitecture/CleanArchitecture.Domain/Common/ValueObject.cs create mode 100644 CleanArchitecture/CleanArchitecture.Domain/Director.cs create mode 100644 CleanArchitecture/CleanArchitecture.Domain/Streamer.cs create mode 100644 CleanArchitecture/CleanArchitecture.Domain/Video.cs create mode 100644 CleanArchitecture/CleanArchitecture.Domain/VideoActor.cs create mode 100644 CleanArchitecture/CleanArchitecture.sln diff --git a/CleanArchitecture/.editorconfig b/CleanArchitecture/.editorconfig new file mode 100644 index 0000000..8922af1 --- /dev/null +++ b/CleanArchitecture/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CA1860: Evitar usar el método de extensión "Enumerable.Any()" +dotnet_diagnostic.CA1860.severity = silent diff --git a/CleanArchitecture/CleanArchitecture.API/CleanArchitecture.API.csproj b/CleanArchitecture/CleanArchitecture.API/CleanArchitecture.API.csproj new file mode 100644 index 0000000..7b2a188 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.API/CleanArchitecture.API.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/CleanArchitecture/CleanArchitecture.API/CleanArchitecture.API.http b/CleanArchitecture/CleanArchitecture.API/CleanArchitecture.API.http new file mode 100644 index 0000000..6012c14 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.API/CleanArchitecture.API.http @@ -0,0 +1,6 @@ +@CleanArchitecture.API_HostAddress = http://localhost:5124 + +GET {{CleanArchitecture.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/CleanArchitecture/CleanArchitecture.API/Controllers/StreamerController.cs b/CleanArchitecture/CleanArchitecture.API/Controllers/StreamerController.cs new file mode 100644 index 0000000..f0c6cec --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.API/Controllers/StreamerController.cs @@ -0,0 +1,51 @@ +using CleanArchitecture.Application.Features.Streamers.Commands.CreateStreamer; +using CleanArchitecture.Application.Features.Streamers.Commands.DeleteStreamer; +using CleanArchitecture.Application.Features.Streamers.Commands.UpdateStreamer; +using MediatR; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System.Net; + +namespace CleanArchitecture.API.Controllers +{ + [Route("api/v1/[controller]")] + [ApiController] + public class StreamerController : ControllerBase + { + private readonly IMediator mediator; + public StreamerController(IMediator _mediator) + { + mediator = _mediator; + } + + [HttpPost(Name = "CreateStreamer")] + [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] + public async Task> CreateStreamer([FromBody] CreateStreamerCommand command) + { + var response = await mediator.Send(command); + return Ok(response); + } + + [HttpPut(Name = "UpdateStreamer")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesDefaultResponseType] + public async Task UpdateStreamer([FromBody] UpdateStreamerCommand command) + { + await mediator.Send(command); + return NoContent(); + } + + [HttpDelete("{id}", Name = "DeleteStreamer")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesDefaultResponseType] + public async Task DeleteStreamer(int id) + { + var request = new DeleteStreamerCommand() { Id = id }; + await mediator.Send(request); + return NoContent(); + } + + } +} diff --git a/CleanArchitecture/CleanArchitecture.API/Controllers/VideoController.cs b/CleanArchitecture/CleanArchitecture.API/Controllers/VideoController.cs new file mode 100644 index 0000000..3d4e48e --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.API/Controllers/VideoController.cs @@ -0,0 +1,29 @@ +using CleanArchitecture.Application.Features.Videos.Queries.GetVideosList; +using MediatR; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System.Net; + +namespace CleanArchitecture.API.Controllers +{ + [Route("api/v1/[controller]")] + [ApiController] + public class VideoController : ControllerBase + { + private readonly IMediator mediator; + + public VideoController(IMediator _mediator) + { + mediator = _mediator; + } + + [HttpGet("{username}", Name = "GetVideo")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task>> GetVideosByUserName(string username) + { + var query = new GetVideosListQuery(username); + var videos = await mediator.Send(query); + return Ok(videos); + } + } +} diff --git a/CleanArchitecture/CleanArchitecture.API/Program.cs b/CleanArchitecture/CleanArchitecture.API/Program.cs new file mode 100644 index 0000000..6e3fd1b --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.API/Program.cs @@ -0,0 +1,23 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/CleanArchitecture/CleanArchitecture.API/Properties/launchSettings.json b/CleanArchitecture/CleanArchitecture.API/Properties/launchSettings.json new file mode 100644 index 0000000..88c1e6c --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.API/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:32905", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5124", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/CleanArchitecture/CleanArchitecture.API/appsettings.Development.json b/CleanArchitecture/CleanArchitecture.API/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/CleanArchitecture/CleanArchitecture.API/appsettings.json b/CleanArchitecture/CleanArchitecture.API/appsettings.json new file mode 100644 index 0000000..4d56694 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/CleanArchitecture/CleanArchitecture.Application/ApplicationServiceRegistration.cs b/CleanArchitecture/CleanArchitecture.Application/ApplicationServiceRegistration.cs new file mode 100644 index 0000000..1708f38 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/ApplicationServiceRegistration.cs @@ -0,0 +1,24 @@ +using CleanArchitecture.Application.Behaviours; +using FluentValidation; +using MediatR; +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; + +namespace CleanArchitecture.Application +{ + public static class ApplicationServiceRegistration + { + public static IServiceCollection AddApplicationServices(this IServiceCollection services) + { + services.AddAutoMapper(Assembly.GetExecutingAssembly()); + services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); + + services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>)); + + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>)); + + return services; + } + } +} diff --git a/CleanArchitecture/CleanArchitecture.Application/Behaviours/UnhandledExceptionBehaviour.cs b/CleanArchitecture/CleanArchitecture.Application/Behaviours/UnhandledExceptionBehaviour.cs new file mode 100644 index 0000000..157fe52 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/Behaviours/UnhandledExceptionBehaviour.cs @@ -0,0 +1,28 @@ +using MediatR; +using Microsoft.Extensions.Logging; + +namespace CleanArchitecture.Application.Behaviours +{ + public class UnhandledExceptionBehaviour : IPipelineBehavior where TRequest : IRequest + { + private readonly ILogger logger; + + public UnhandledExceptionBehaviour(ILogger _logger) + { + logger = _logger; + } + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + try + { + return await next(); + }catch (Exception ex) + { + var requestName = typeof(TRequest).Name; + logger.LogError(ex, "CleanArchitecture Request: Unhandled Exception for Request {Name} {@Request}", requestName, request); + throw; + } + } + } +} diff --git a/CleanArchitecture/CleanArchitecture.Application/Behaviours/ValidationBehaviour.cs b/CleanArchitecture/CleanArchitecture.Application/Behaviours/ValidationBehaviour.cs new file mode 100644 index 0000000..c980240 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/Behaviours/ValidationBehaviour.cs @@ -0,0 +1,32 @@ +using ValidationException = CleanArchitecture.Application.Exceptions.ValidationException; +using FluentValidation; +using MediatR; + +namespace CleanArchitecture.Application.Behaviours +{ + public class ValidationBehaviour : IPipelineBehavior where TRequest : IRequest + { + private readonly IEnumerable> validators; + + public ValidationBehaviour(IEnumerable> _validators) + { + this.validators = _validators; + } + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + if (validators.Any()) + { + var context = new ValidationContext(request); + var validationResults = await Task.WhenAll(validators.Select(v => v.ValidateAsync(context, cancellationToken))); + var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList(); + if (failures.Any()) + { + throw new ValidationException(failures); + } + } + + return await next(); + } + } +} diff --git a/CleanArchitecture/CleanArchitecture.Application/CleanArchitecture.Application.csproj b/CleanArchitecture/CleanArchitecture.Application/CleanArchitecture.Application.csproj new file mode 100644 index 0000000..00f94ca --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/CleanArchitecture.Application.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + diff --git a/CleanArchitecture/CleanArchitecture.Application/Contracts/Infrastructure/IEmailService.cs b/CleanArchitecture/CleanArchitecture.Application/Contracts/Infrastructure/IEmailService.cs new file mode 100644 index 0000000..03639e9 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/Contracts/Infrastructure/IEmailService.cs @@ -0,0 +1,11 @@ +using CleanArchitecture.Application.Models; + +namespace CleanArchitecture.Application.Contracts.Infrastructure +{ + public interface IEmailService + { + + Task SendEmail(Email email); + + } +} diff --git a/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IAsyncRepository.cs b/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IAsyncRepository.cs new file mode 100644 index 0000000..0dc3232 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IAsyncRepository.cs @@ -0,0 +1,25 @@ +using CleanArchitecture.Domain.Common; +using System.Linq.Expressions; + +namespace CleanArchitecture.Application.Contracts.Persistence +{ + public interface IAsyncRepository where T: BaseDomainModel + { + Task> GetAllAsync(); + Task> GetAsync(Expression>? predicate); + Task> GetAsync(Expression>? predicate = null, + Func, IOrderedQueryable>? orderBy = null, + string includeString = null, + bool disableTracking = true); + Task> GetAsync(Expression>? predicate = null, + Func, IOrderedQueryable>? orderBy = null, + List>>? includes = null, + bool disableTracking = true); + Task GetByIdAsync(int id); + + Task AddAsync(T entity); + Task UpdateAsync(T entity); + Task DeleteAsync(T entity); + + } +} diff --git a/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IStreamerRepository.cs b/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IStreamerRepository.cs new file mode 100644 index 0000000..63900d9 --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IStreamerRepository.cs @@ -0,0 +1,14 @@ +using CleanArchitecture.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CleanArchitecture.Application.Contracts.Persistence +{ + public interface IStreamerRepository: IAsyncRepository + { + + } +} diff --git a/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IVideoRepository.cs b/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IVideoRepository.cs new file mode 100644 index 0000000..443546f --- /dev/null +++ b/CleanArchitecture/CleanArchitecture.Application/Contracts/Persistence/IVideoRepository.cs @@ -0,0 +1,10 @@ +using CleanArchitecture.Domain; + +namespace CleanArchitecture.Application.Contracts.Persistence +{ + public interface IVideoRepository : IAsyncRepository