Browse Source

添加项目文件。

master
Nice 3 years ago
parent
commit
29b04fd169
  1. 37
      Easy.sln
  2. 7
      src/Easy.Api/ApiService.cs
  3. 17
      src/Easy.Api/Easy.Api.csproj
  4. 43
      src/Easy.Api/Extensions/RouteHandlerBuilderExtensions.cs
  5. 42
      src/Easy.Api/Extensions/ServiceCollectionServiceExtensions.cs
  6. 24
      src/Easy.Api/FirstTemplateAttribute.cs
  7. 16
      src/Easy.Api/IgnoreAttribute.cs
  8. 140
      src/Easy.Api/Middlewares/AnnotationsOperationFilter.cs
  9. 55
      src/Easy.Api/Realization/ApiMapAuto.cs
  10. 25
      src/Easy.Api/Realization/AppServiceActivator.cs
  11. 16
      src/Easy.Api/Realization/AppServiceControllerFeatureProvider.cs
  12. 149
      src/Easy.Api/Realization/ApplicationConvention.cs
  13. 2
      src/Easy.Api/_Imports.cs
  14. 13
      src/Easy.DI/Easy.DI.csproj
  15. 28
      src/Easy.DI/Extensions/ServiceCollectionServiceExtensions.cs
  16. 20
      src/Easy.DI/ILazyServiceProvider.cs
  17. 8
      src/Easy.DI/IScopedDependency.cs
  18. 8
      src/Easy.DI/ISingletonDependency.cs
  19. 8
      src/Easy.DI/ITransientDependency.cs
  20. 52
      src/Easy.DI/Realization/DependencyInterface.cs
  21. 73
      src/Easy.DI/Realization/LazyServiceProvider.cs
  22. 2
      src/Easy.DI/_Imports.cs
  23. 27
      src/Easy.Result/ApiResult.cs
  24. 13
      src/Easy.Result/ApiResultConsts.cs
  25. 18
      src/Easy.Result/ApiResultPaged.cs
  26. 11
      src/Easy.Result/ApiResultValue.cs
  27. 11
      src/Easy.Result/ApiResultValues.cs
  28. 24
      src/Easy.Result/Common/AutoMapReadonly.cs
  29. 16
      src/Easy.Result/Easy.Result.csproj
  30. 18
      src/Easy.Result/Exceptions/SvcException.cs
  31. 77
      src/Easy.Result/Extensions/ApiResultExtensions.cs
  32. 22
      src/Easy.Result/Extensions/ApplicationBuilderExtensions.cs
  33. 36
      src/Easy.Result/FluentValidation/FluentValidationRegister.cs
  34. 65
      src/Easy.Result/IEntities.cs
  35. 10
      src/Easy.Result/IEntity.cs
  36. 9
      src/Easy.Result/IModel.cs
  37. 51
      src/Easy.Result/Middlewares/ErrApiResMiddleware.cs
  38. 38
      src/Easy.Result/PagingRequest.cs
  39. 20
      src/Easy.Result/When.cs

37
Easy.sln

@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Easy.Api", "src\Easy.Api\Easy.Api.csproj", "{689367DF-1216-40A5-91CE-412EE8FE6C8F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Easy.DI", "src\Easy.DI\Easy.DI.csproj", "{A269FF3D-00BC-499D-A496-46D97D51FE92}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Easy.Result", "src\Easy.Result\Easy.Result.csproj", "{82677F0B-E9CF-4491-8B04-9BF9B04B1534}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{689367DF-1216-40A5-91CE-412EE8FE6C8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{689367DF-1216-40A5-91CE-412EE8FE6C8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{689367DF-1216-40A5-91CE-412EE8FE6C8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{689367DF-1216-40A5-91CE-412EE8FE6C8F}.Release|Any CPU.Build.0 = Release|Any CPU
{A269FF3D-00BC-499D-A496-46D97D51FE92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A269FF3D-00BC-499D-A496-46D97D51FE92}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A269FF3D-00BC-499D-A496-46D97D51FE92}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A269FF3D-00BC-499D-A496-46D97D51FE92}.Release|Any CPU.Build.0 = Release|Any CPU
{82677F0B-E9CF-4491-8B04-9BF9B04B1534}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82677F0B-E9CF-4491-8B04-9BF9B04B1534}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82677F0B-E9CF-4491-8B04-9BF9B04B1534}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82677F0B-E9CF-4491-8B04-9BF9B04B1534}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7260112B-0232-4725-AB84-EF4DCCABFD77}
EndGlobalSection
EndGlobal

7
src/Easy.Api/ApiService.cs

@ -0,0 +1,7 @@
using Microsoft.AspNetCore.Routing;
namespace Easy.Api;
public abstract class ApiService
{
}

17
src/Easy.Api/Easy.Api.csproj

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Easy.DI\Easy.DI.csproj" />
</ItemGroup>
</Project>

43
src/Easy.Api/Extensions/RouteHandlerBuilderExtensions.cs

@ -0,0 +1,43 @@
using Easy.Api.Realization;
using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations;
namespace Easy.Api.Extensions;
/// <summary>
/// A class containing extension methods for the <see cref="RouteHandlerBuilder"/> class. This class cannot be inherited.
/// </summary>
public static class RouteHandlerBuilderExtensions
{
/// <summary>
/// Adds the <see cref="SwaggerOperationAttribute"/> to the metadata for all builders produced by builder.
/// </summary>
/// <param name="builder">The <see cref="RouteHandlerBuilder"/>.</param>
/// <param name="summary">The operation summary.</param>
/// <param name="description">The optional operation description.</param>
/// <returns>
/// A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.
/// </returns>
public static RouteHandlerBuilder WithDescription(
this RouteHandlerBuilder builder,
string summary,
string description = null)
{
return builder.WithMetadata(new SwaggerOperationAttribute(summary, description));
}
/// <summary>
/// 路由模板 App名称带头
/// </summary>
//public static ApiMapAuto AutoApi(this IEndpointRouteBuilder endpoint, string template)
//{
// if (string.IsNullOrEmpty(template))
// {
// template = "app";
// }
// if (!template.StartsWith("api"))
// {
// template = "api/" + template;
// }
// return new ApiMapAuto(template, endpoint);
//}
}

42
src/Easy.Api/Extensions/ServiceCollectionServiceExtensions.cs

@ -0,0 +1,42 @@
using Easy.Api;
using Easy.Api.Realization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Easy.Api.Extensions;
public static class ServiceCollectionServiceExtensions
{
/// <summary>
/// 动态代理Api
/// </summary>
public static IMvcBuilder AddDynamicWebApi(this IServiceCollection services)
{
var build = services.AddControllers();
build.ConfigureApplicationPartManager(applicationPartManager =>
{
applicationPartManager.FeatureProviders.Add(new AppServiceControllerFeatureProvider());
});
ControllerFeature feature = new();
build.PartManager.PopulateFeature(feature);
foreach (var controller in feature.Controllers.Select(c => c.AsType()).Where(o => o != typeof(ApiService)))// 禁用父类
{
services.TryAddTransient(controller, controller);
}
services.Replace(ServiceDescriptor.Transient<IControllerActivator, AppServiceActivator>());
services.Configure<MvcOptions>(options =>
{
options.Conventions.Add(new ApplicationConvention());
});
return build;
}
}

24
src/Easy.Api/FirstTemplateAttribute.cs

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Easy.Api;
/// <summary>
/// api定义?
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class FirstTemplateAttribute : Attribute
{
public string FirstTemplate { get; }
public FirstTemplateAttribute()
{
FirstTemplate = "";
}
public FirstTemplateAttribute(string firstTemplate)
{
FirstTemplate = firstTemplate;
}
}

16
src/Easy.Api/IgnoreAttribute.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Easy.Api;
/// <summary>
/// 忽略
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public class ParamIgnoreAttribute : Attribute
{
}

140
src/Easy.Api/Middlewares/AnnotationsOperationFilter.cs

@ -0,0 +1,140 @@
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Globalization;
namespace Easy.Api.Middlewares;
public class AnnotationsOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
IEnumerable<object> controllerAttributes = Array.Empty<object>();
IEnumerable<object> actionAttributes = Array.Empty<object>();
IEnumerable<object> metadataAttributes = Array.Empty<object>();
if (context.MethodInfo is not null)
{
controllerAttributes = context.MethodInfo.DeclaringType!.GetCustomAttributes(true);
actionAttributes = context.MethodInfo.GetCustomAttributes(true);
}
if (context.ApiDescription.ActionDescriptor.EndpointMetadata is not null)
{
metadataAttributes = context.ApiDescription.ActionDescriptor.EndpointMetadata;
}
// NOTE: When controller and action attributes are applicable, action attributes should take precendence.
// Hence why they're at the end of the list (i.e. last one wins).
// Distinct() is applied due to an ASP.NET Core issue: https://github.com/dotnet/aspnetcore/issues/34199.
var allAttributes = controllerAttributes
.Union(actionAttributes)
.Union(metadataAttributes)
.Distinct();
var actionAndEndpointAttribtues = actionAttributes
.Union(metadataAttributes)
.Distinct();
ApplySwaggerOperationAttribute(operation, actionAndEndpointAttribtues);
ApplySwaggerOperationFilterAttributes(operation, context, allAttributes);
ApplySwaggerResponseAttributes(operation, context, allAttributes);
}
private static void ApplySwaggerOperationAttribute(
OpenApiOperation operation,
IEnumerable<object> actionAttributes)
{
var swaggerOperationAttribute = actionAttributes
.OfType<SwaggerOperationAttribute>()
.FirstOrDefault();
if (swaggerOperationAttribute == null)
{
return;
}
if (swaggerOperationAttribute.Summary != null)
{
operation.Summary = swaggerOperationAttribute.Summary;
}
if (swaggerOperationAttribute.Description != null)
{
operation.Description = swaggerOperationAttribute.Description;
}
if (swaggerOperationAttribute.OperationId != null)
{
operation.OperationId = swaggerOperationAttribute.OperationId;
}
if (swaggerOperationAttribute.Tags != null)
{
operation.Tags = swaggerOperationAttribute.Tags
.Select(tagName => new OpenApiTag { Name = tagName })
.ToList();
}
}
private static void ApplySwaggerOperationFilterAttributes(
OpenApiOperation operation,
OperationFilterContext context,
IEnumerable<object> controllerAndActionAttributes)
{
var swaggerOperationFilterAttributes = controllerAndActionAttributes
.OfType<SwaggerOperationFilterAttribute>();
foreach (var swaggerOperationFilterAttribute in swaggerOperationFilterAttributes)
{
var filter = (IOperationFilter)Activator.CreateInstance(swaggerOperationFilterAttribute.FilterType)!;
filter.Apply(operation, context);
}
}
private static void ApplySwaggerResponseAttributes(
OpenApiOperation operation,
OperationFilterContext context,
IEnumerable<object> controllerAndActionAttributes)
{
var swaggerResponseAttributes = controllerAndActionAttributes.OfType<SwaggerResponseAttribute>();
foreach (var swaggerResponseAttribute in swaggerResponseAttributes)
{
string statusCode = swaggerResponseAttribute.StatusCode.ToString(CultureInfo.InvariantCulture);
if (operation.Responses == null)
{
operation.Responses = new OpenApiResponses();
}
if (!operation.Responses.TryGetValue(statusCode, out OpenApiResponse response))
{
response = new OpenApiResponse();
}
if (swaggerResponseAttribute.Description != null)
{
response.Description = swaggerResponseAttribute.Description;
}
operation.Responses[statusCode] = response;
if (swaggerResponseAttribute.ContentTypes != null)
{
response.Content.Clear();
foreach (string contentType in swaggerResponseAttribute.ContentTypes)
{
var schema = (swaggerResponseAttribute.Type != null && swaggerResponseAttribute.Type != typeof(void))
? context.SchemaGenerator.GenerateSchema(swaggerResponseAttribute.Type, context.SchemaRepository)
: null;
response.Content.Add(contentType, new OpenApiMediaType { Schema = schema });
}
}
}
}
}

55
src/Easy.Api/Realization/ApiMapAuto.cs

@ -0,0 +1,55 @@
namespace Easy.Api.Realization;
public class ApiMapAuto
{
public readonly static string[] CommonPostfixes = { "AppService", "Service" };
public static string FiterControllerName(string controllerName) // 过滤控制器
{
foreach (var commonPostfixe in CommonPostfixes)
{
if (controllerName.EndsWith(commonPostfixe))
{
controllerName = controllerName.Remove(controllerName.IndexOf(commonPostfixe));
break;
}
}
return controllerName;
}
public readonly static List<string> ActionPostfixes = new()
{
"GetAll", "GetList", "Get",
"Post", "Create", "Add", "Insert",
"Put", "Update",
"Delete", "Remove",
"Patch"
};
private const string AsyncStr = "Async";
internal static string FiterActionName(string actionName)
{
string actionPostfixe = ActionPostfixes.FirstOrDefault(actionPostfixe => actionName.StartsWith(actionPostfixe)) ?? "";
if (actionName.StartsWith(actionPostfixe))
actionName = actionName.Remove(actionName.IndexOf(actionPostfixe), actionPostfixe.Length);
if (actionName.EndsWith(AsyncStr))
actionName = actionName.Remove(actionName.IndexOf(AsyncStr));
return actionName;
}
public static string GetHttpMethod(string actionName)
{
if (actionName.StartsWith("Get"))
return "GET";
if (actionName.StartsWith("Put") || actionName.StartsWith("Update"))
return "PUT";
if (actionName.StartsWith("Delete") || actionName.StartsWith("Remove"))
return "DELETE";
if (actionName.StartsWith("Patch"))
return "PATCH";
return "POST";
}
}

25
src/Easy.Api/Realization/AppServiceActivator.cs

@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
namespace Easy.Api.Realization;
internal class AppServiceActivator : IControllerActivator
{
public object Create(ControllerContext actionContext)
{
if (actionContext == null)
{
throw new ArgumentNullException(nameof(actionContext));
}
var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();
var controller = actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
return controller;
}
public virtual void Release(ControllerContext context, object controller)
{
}
}

16
src/Easy.Api/Realization/AppServiceControllerFeatureProvider.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc.Controllers;
using System.Reflection;
namespace Easy.Api.Realization;
internal class AppServiceControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
var result =
typeof(ApiService).IsAssignableFrom(typeInfo) && typeof(ApiService) != typeInfo &&
typeInfo.IsClass && typeInfo.IsPublic && !typeInfo.IsAbstract;
//if (result){}
return result;
}
}

149
src/Easy.Api/Realization/ApplicationConvention.cs

@ -0,0 +1,149 @@

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Easy.Api.Realization;
internal class ApplicationConvention : IApplicationModelConvention
{
public void Apply(ApplicationModel application)
{
application.Controllers.ToList().ForEach(controller =>
{
controller.ControllerName = ApiMapAuto.FiterControllerName(controller.ControllerName);
ConfigureSelector(controller);
ConfigureParameters(controller);
ConfigureApiExplorer(controller);
});
}
//配置记录应用: 这可用于使用 Swagger 等工具为 Web API 生成帮助页。
private static void ConfigureApiExplorer(ControllerModel controller)
{
if (!controller.ApiExplorer.IsVisible.HasValue)
{
controller.ApiExplorer.IsVisible = true;
}
foreach (var action in controller.Actions)
{
if (!action.ApiExplorer.IsVisible.HasValue)
{
action.ApiExplorer.IsVisible = true;
}
}
}
//配置选择器:方法路由 HttpMethod
private static void ConfigureSelector(ControllerModel controller)
{
foreach (var action in controller.Actions)
{
action.Selectors[0].AttributeRouteModel = new(new RouteAttribute(RouteTemplate(action)));
action.Selectors[0].ActionConstraints.Add(new HttpMethodActionConstraint(new[] { ApiMapAuto.GetHttpMethod(action.ActionName) }));
}
}
//配置方法参数
private static void ConfigureParameters(ControllerModel controller)
{
foreach (var action in controller.Actions)
{
foreach (var parameter in action.Parameters)
{
if (parameter.BindingInfo != null)
continue;
if (parameter.ParameterType.IsClass &&
parameter.ParameterType != typeof(string) &&
parameter.ParameterType != typeof(IFormFile))
{
var httpMethods = action.Selectors.SelectMany(temp => temp.ActionConstraints).OfType<HttpMethodActionConstraint>().SelectMany(temp => temp.HttpMethods);
if (new[] { "GET", "DELETE", "TRACE", "HEAD" }.Any(value => httpMethods.Contains(value)))
continue;
parameter.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromBodyAttribute() });
}
}
}
}
//获得一个自定义的路由
private static string RouteTemplate(ActionModel action)
{
var routeAttribute = action.Controller.Attributes.FirstOrDefault(o => o?.GetType() == typeof(FirstTemplateAttribute));
string url = "";
if (routeAttribute != null)
{
url = ((FirstTemplateAttribute)routeAttribute).FirstTemplate;
}
else
{
url += "api";
}
//控制器部分
url += $"/{ApiMapAuto.FiterControllerName(action.Controller.ControllerType.Name)}";
// id 部分
var idParameterModel = action.Parameters.FirstOrDefault(p => p.ParameterName == "id");
if (idParameterModel != null)
{
if (action.Parameters.Any(temp => temp.ParameterName == "id"))
url += "/{id}";
else
{
var properties = idParameterModel.ParameterType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
url += "/{" + property.Name + "}";
}
}
}
//方法部分
string actionName = ApiMapAuto.FiterActionName(action.ActionMethod.Name);
if (!string.IsNullOrEmpty(actionName))
{
url += $"/{actionName}";
// id 部分
var secondaryIds = action.Parameters.Where(p =>
p.Attributes.Any(o => o.GetType() != typeof(ParamIgnoreAttribute)) &&
p.ParameterName.EndsWith("Id", StringComparison.Ordinal)).ToList();
if (secondaryIds.Count == 1)
{
url += $"/{{{secondaryIds[0].Name}}}";
}
}
return url;
}
//清空Selectors
//private static void RemoveSelectors(IList<SelectorModel> selectors)
//{
// for (var i = selectors.Count - 1; i >= 0; i--)
// {
// selectors.Remove(selectors[i]);
// }
//}
}

2
src/Easy.Api/_Imports.cs

@ -0,0 +1,2 @@
global using Microsoft.AspNetCore.Builder;
global using Microsoft.Extensions.DependencyInjection;

13
src/Easy.DI/Easy.DI.csproj

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<!--<Nullable>enable</Nullable>-->
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.0" />
</ItemGroup>
</Project>

28
src/Easy.DI/Extensions/ServiceCollectionServiceExtensions.cs

@ -0,0 +1,28 @@
using Easy.DI.Realization;
namespace Easy.DI.Extensions;
public static class ServiceCollectionServiceExtensions
{
public static IServiceCollection AddDependencyAbstracts(this IServiceCollection s, params Action<IServiceCollection, Type[]>[] abstractConfigs)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var types = assembly.GetTypes().Where(o => !o.IsInterface && !o.IsAbstract && o.IsClass).ToArray();
foreach (var config in abstractConfigs)
{
config(s, types);
}
}
return s;
}
public static IServiceCollection AddDependency(this IServiceCollection s)
{
s.AddDependencyAbstracts(
DependencyInterface.Singleton,
DependencyInterface.Transient,
DependencyInterface.Scoped
);
return s;
}
}

20
src/Easy.DI/ILazyServiceProvider.cs

@ -0,0 +1,20 @@
namespace Easy.DI;
public interface ILazyServiceProvider
{
T LazyGetRequiredService<T>();
object LazyGetRequiredService(Type serviceType);
T LazyGetService<T>();
object LazyGetService(Type serviceType);
T LazyGetService<T>(T defaultValue);
object LazyGetService(Type serviceType, object defaultValue);
object LazyGetService(Type serviceType, Func<IServiceProvider, object> factory);
T LazyGetService<T>(Func<IServiceProvider, object> factory);
}

8
src/Easy.DI/IScopedDependency.cs

@ -0,0 +1,8 @@
namespace Easy.DI;
/// <summary>
/// 只 new 一次
/// </summary>
public interface IScopedDependency
{
}

8
src/Easy.DI/ISingletonDependency.cs

@ -0,0 +1,8 @@
namespace Easy.DI;
/// <summary>
/// 全局只NEW 一次对象
/// </summary>
public interface ISingletonDependency
{
}

8
src/Easy.DI/ITransientDependency.cs

@ -0,0 +1,8 @@
namespace Easy.DI;
/// <summary>
/// 依赖注入瞬态生命周期
/// </summary>
public interface ITransientDependency
{
}

52
src/Easy.DI/Realization/DependencyInterface.cs

@ -0,0 +1,52 @@
using Easy.DI;
namespace Easy.DI.Realization;
internal static class DependencyInterface
{
public static void Scoped(this IServiceCollection s, Type[] types)
{
foreach (var type in types.Where(o => IsServiceLifetime<IScopedDependency>(o)))
{
Type interfaceType = GetInterface(type);
if (interfaceType != null)
s.TryAddScoped(interfaceType, type);
else
s.TryAddScoped(type, type);
}
}
public static void Singleton(this IServiceCollection s, Type[] types)
{
foreach (var type in types.Where(o => IsServiceLifetime<ISingletonDependency>(o)))
{
Type interfaceType = GetInterface(type);
if (interfaceType != null)
s.TryAddSingleton(interfaceType, type);
else
s.TryAddSingleton(type, type);
}
}
public static void Transient(this IServiceCollection s, Type[] types)
{
foreach (var type in types.Where(o => IsServiceLifetime<ITransientDependency>(o)))
{
Type interfaceType = GetInterface(type);
if (interfaceType != null)
s.TryAddTransient(interfaceType, type);
else
s.TryAddTransient(type, type);
}
}
private static bool IsServiceLifetime<ServiceLifetime>(Type o) => typeof(ServiceLifetime).IsAssignableFrom(o);
private static Type GetInterface(Type type)
{
return type.GetInterfaces().FirstOrDefault(interfaceType => interfaceType.Name.EndsWith(type.Name));
}
}

73
src/Easy.DI/Realization/LazyServiceProvider.cs

@ -0,0 +1,73 @@
namespace Easy.DI.Realization;
public class LazyServiceProvider : ILazyServiceProvider, ITransientDependency
{
protected IDictionary<Type, object> CachedServices { get; set; }
protected IServiceProvider ServiceProvider { get; set; }
public LazyServiceProvider(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
CachedServices = new Dictionary<Type, object>();
}
public virtual T LazyGetRequiredService<T>()
{
return (T)LazyGetRequiredService(typeof(T));
}
public virtual object LazyGetRequiredService(Type serviceType)
{
if (CachedServices.TryGetValue(serviceType, out object obj))
{
return obj;
}
return CachedServices[serviceType] = ServiceProvider?.GetService(serviceType);
}
public virtual T LazyGetService<T>()
{
return (T)LazyGetService(typeof(T));
}
public virtual object LazyGetService(Type serviceType)
{
if (CachedServices.TryGetValue(serviceType, out object obj))
{
return obj;
}
return CachedServices[serviceType] = ServiceProvider?.GetService(serviceType);
}
public virtual T LazyGetService<T>(T defaultValue)
{
return (T)LazyGetService(typeof(T), defaultValue);
}
public virtual object LazyGetService(Type serviceType, object defaultValue)
{
return LazyGetService(serviceType) ?? defaultValue;
}
public virtual T LazyGetService<T>(Func<IServiceProvider, object> factory)
{
return (T)LazyGetService(typeof(T), factory);
}
public virtual object LazyGetService(Type serviceType, Func<IServiceProvider, object> factory)
{
if (CachedServices.TryGetValue(serviceType, out object obj))
{
return obj;
}
return CachedServices[serviceType] = factory(ServiceProvider);
}
}

2
src/Easy.DI/_Imports.cs

@ -0,0 +1,2 @@
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.DependencyInjection.Extensions;

27
src/Easy.Result/ApiResult.cs

@ -0,0 +1,27 @@
namespace Easy.Result;
/// <summary>
/// ApiResult
/// </summary>
public class ApiResult : IModel
{
/// <summary>
/// 消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 状态
/// </summary>
public string Status { get; set; }
public static ApiResultValue<TValue> Value<TValue>(TValue value)
{
return new ApiResultValue<TValue>(value);
}
public static ApiResultValues<TValue> Value<TValue>(List<TValue> value)
{
return new ApiResultValues<TValue>(value);
}
}

13
src/Easy.Result/ApiResultConsts.cs

@ -0,0 +1,13 @@
namespace Easy.Result;
public class ApiResultConsts
{
/// <summary>
/// 正确返回状态
/// </summary>
public const string SUCCESS = "SUCCESS";
/// <summary>
/// 需要用户重试 基本是报错了 或者业务问题
/// </summary>
public const string RETRY = "RETRY";
}

18
src/Easy.Result/ApiResultPaged.cs

@ -0,0 +1,18 @@
namespace Easy.Result;
public class ApiResultPaged<TSource> : ApiResult
{
public ApiResultPaged() { }
/// <summary>
/// 页面数量
/// </summary>
public long PageCount { get; set; }
/// <summary>
/// 页面大小
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 返回结果
/// </summary>
public List<TSource> Data { get; set; }
}

11
src/Easy.Result/ApiResultValue.cs

@ -0,0 +1,11 @@
namespace Easy.Result;
public class ApiResultValue<TValue> : ApiResult
{
public ApiResultValue() { }
public ApiResultValue(TValue value)
{
Result = value;
}
public TValue Result { get; set; }
}

11
src/Easy.Result/ApiResultValues.cs

@ -0,0 +1,11 @@
namespace Easy.Result;
public class ApiResultValues<TValue> : ApiResult
{
public ApiResultValues() { }
public ApiResultValues(List<TValue> value)
{
Result = value;
}
public List<TValue> Result { get; set; }
}

24
src/Easy.Result/Common/AutoMapReadonly.cs

@ -0,0 +1,24 @@
using AutoMapper;
using Microsoft.Extensions.DependencyModel;
using System.Reflection;
using System.Runtime.Loader;
namespace Easy.Result.Common;
public class AutoMapReadonly
{
private static IEnumerable<Assembly> Assemblies = DependencyContext.Default.CompileLibraries.
Where(lib => !lib.Serviceable && lib.Type != "package" && lib.Type != "referenceassembly").
Select(lib => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)));
public readonly static IConfigurationProvider ConfigurationProvider = new MapperConfiguration(cfg =>
{
foreach (var assembly in Assemblies)
{
foreach (var profileType in assembly.GetTypes().Where(o => typeof(Profile).IsAssignableFrom(o)))
{
cfg.AddProfile(profileType);
}
}
});
}

16
src/Easy.Result/Easy.Result.csproj

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<!--<Nullable>enable</Nullable>-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="10.3.6" />
<PackageReference Include="AutoMapper" Version="11.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.0" />
<PackageReference Include="SqlSugarCore" Version="5.0.5.4" />
<FrameworkReference Include="Microsoft.AspNetCore.App" Version="6.0.0" />
</ItemGroup>
</Project>

18
src/Easy.Result/Exceptions/SvcException.cs

@ -0,0 +1,18 @@
namespace Easy.Result.Exceptions;
/// <summary>
/// 服务异常
/// </summary>
internal class SvcException : Exception
{
/// <summary>
/// 服务异常
/// </summary>
/// <param name="message">消息</param>
public SvcException(string message)
: base(message)
{
}
}

77
src/Easy.Result/Extensions/ApiResultExtensions.cs

@ -0,0 +1,77 @@
using AutoMapper;
using Easy.Result.Common;
namespace Easy.Result.Extensions;
public static class ApiResultExtensions
{
/// <summary>
/// 是成功
/// </summary>
static public bool IsSuccess<TSource>(this TSource source)
where TSource : ApiResult
{
return source.Status.Equals(ApiResultConsts.SUCCESS);
}
/// <summary>
/// 是重试
/// </summary>
static public bool IsRETRY<TSource>(this TSource source)
where TSource : ApiResult
{
return source.Status.Equals(ApiResultConsts.RETRY);
}
/// <summary>
/// 成功
/// </summary>
static public TSource SUCCESS<TSource>(this TSource source, string message = null)
where TSource : ApiResult
{
return source.CustomStatusMessage("SUCCESS", message ?? "操作成功");
}
/// <summary>
/// 重试
/// </summary>
static public TSource RETRY<TSource>(this TSource source, string message = null)
where TSource : ApiResult
{
return source.CustomStatusMessage("RETRY", message ?? "出错了,请稍后再试。");
}
/// <summary>
/// 自定义状态消息
/// </summary>
static public TSource CustomStatusMessage<TSource>(this TSource source, string status, string message)
where TSource : ApiResult
{
source.Message = message;
source.Status = status;
return source;
}
static public TEntity ToEntity<TEntity>(this IModel model)
where TEntity : IEntity, new()
{
return new Mapper(AutoMapReadonly.ConfigurationProvider).Map<TEntity>(model);
}
static public ApiResultValue<TModel> ToModel<TModel>(this IEntity entity)
where TModel : IModel, new()
{
return ApiResult.Value(new Mapper(AutoMapReadonly.ConfigurationProvider).Map<TModel>(entity) ?? new());
}
static public ApiResultValues<TModel> ToModels<TModel>(this IEntities entities)
{
return ApiResult.Value(new Mapper(AutoMapReadonly.ConfigurationProvider).Map<List<TModel>>(entities.Data) ?? new());
}
static public ApiResultPaged<TModel> ToPagedModel<TModel>(this IPaged paged)
{
return new ApiResultPaged<TModel>()
{
Data = new Mapper(AutoMapReadonly.ConfigurationProvider).Map<List<TModel>>(paged.Data) ?? new(),
PageSize = paged.PageSize,
PageCount = paged.PageCount
};
}
}

22
src/Easy.Result/Extensions/ApplicationBuilderExtensions.cs

@ -0,0 +1,22 @@
using Easy.Result.FluentValidation;
using Easy.Result.Middlewares;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Easy.Result.Extensions;
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseErrApiResult(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ErrApiResultMiddleware>();
}
public static IMvcBuilder AddEasyFluentValidation(this IMvcBuilder builder)
{
return builder.AddMvcOptions(options =>
{
options.Filters.Add<FluentValidationFilter>();
});
}
}

36
src/Easy.Result/FluentValidation/FluentValidationRegister.cs

@ -0,0 +1,36 @@
using FluentValidation;
using FluentValidation.Internal;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Easy.Result.FluentValidation;
public class FluentValidationFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
foreach (var arg in context.ActionArguments)
{
var requestType = arg.Value?.GetType();
if (requestType != null)
{
var abstractValidatorType = typeof(AbstractValidator<>).MakeGenericType(requestType);
//var name = abstractValidatorType.Name + requestType.Name;
var classValidatorType = requestType.Assembly.GetTypes().First(o => abstractValidatorType.IsAssignableFrom(o));
if (classValidatorType != null)
{
IValidator validator = (IValidator)Activator.CreateInstance(classValidatorType);
//new ValidationContext<T>(instance, new PropertyChain(), ValidatorOptions.Global.ValidatorSelectors.DefaultValidatorSelectorFactory())
var validationContextType = typeof(ValidationContext<>).MakeGenericType(requestType);
IValidationContext ValidationContext = (IValidationContext)Activator.CreateInstance(validationContextType, arg.Value, new PropertyChain(), ValidatorOptions.Global.ValidatorSelectors.DefaultValidatorSelectorFactory());
var validationResult = await validator.ValidateAsync(ValidationContext);
When.Is(!validationResult.IsValid, validationResult.Errors?.First()?.ErrorMessage);
}
}
}
}
}

65
src/Easy.Result/IEntities.cs

@ -0,0 +1,65 @@
namespace Easy.Result;
public interface IEntities
{
/// <summary>
/// 返回结果
/// </summary>
public object Data { get; set; }
public static IEntities Create<TSource>(List<TSource> Data)
{
return new Entites()
{
Data = Data ?? new()
};
}
}
public sealed class Entites : IEntities
{
/// <summary>
/// 返回结果
/// </summary>
public object Data { get; set; }
}
public interface IPaged : IEntities
{
/// <summary>
/// 页面数量
/// </summary>
public long PageCount { get; set; }
/// <summary>
/// 页面大小
/// </summary>
public int PageSize { get; set; }
public static IPaged Create<TSource>(long PageCount, int PageSize, List<TSource> Data)
{
return new Paged()
{
PageCount = PageCount,
Data = Data ?? new(),
PageSize = PageSize,
};
}
}
public sealed class Paged : IPaged
{
/// <summary>
/// 页面数量
/// </summary>
public long PageCount { get; set; }
/// <summary>
/// 页面大小
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 返回结果
/// </summary>
public object Data { get; set; }
}

10
src/Easy.Result/IEntity.cs

@ -0,0 +1,10 @@
using SqlSugar;
namespace Easy.Result;
public interface IEntity
{
[SugarColumn(IsPrimaryKey = true)]
public long Id { get; set; }
}

9
src/Easy.Result/IModel.cs

@ -0,0 +1,9 @@
using AutoMapper;
using Easy.Result.Common;
namespace Easy.Result;
public interface IModel
{
public TEntity ToEntity<TEntity>() where TEntity : IEntity, new() => new Mapper(AutoMapReadonly.ConfigurationProvider).Map<TEntity>(this);
}

51
src/Easy.Result/Middlewares/ErrApiResMiddleware.cs

@ -0,0 +1,51 @@
using Easy.Result.Exceptions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Easy.Result.Middlewares;
public class ErrApiResultMiddleware
{
private readonly RequestDelegate _next;
public ErrApiResultMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, ILoggerFactory loggerFactory)
{
if (context.Request.Path.Value.StartsWith("/api"))
{
var logger = loggerFactory.CreateLogger(nameof(ErrApiResultMiddleware));
try
{
await _next(context);
}
catch (SvcException ex)
{
await context.Response.WriteAsJsonAsync(new ApiResult()
{
Message = ex.Message,
Status = "RETRY",
});
}
catch (Exception ex)
{
string message = $"ErrMessage = {ex.Message}";
#pragma warning disable CA2254 // 模板应为静态表达式
logger.LogError(new EventId(ex.HResult), ex, message);
#pragma warning restore CA2254 // 模板应为静态表达式
await context.Response.WriteAsJsonAsync(new ApiResult()
{
Message = "出错了,请稍后再试。",
Status = "RETRY",
});
}
}
else
{
await _next(context);
}
}
}

38
src/Easy.Result/PagingRequest.cs

@ -0,0 +1,38 @@
namespace Easy.Result;
public record PagingRequest : IModel
{
private int _pageIndex;
private int _pageSize;
/// <summary>
/// 当前第几页
/// </summary>
public int PageIndex
{
get => _pageIndex;
set
{
if (value <= 0) value = 1;
_pageIndex = value;
}
}
/// <summary>
/// 页面数量
/// </summary>
public int PageSize
{
get => _pageSize;
set
{
if (value <= 9) value = 10;
_pageSize = value;
}
}
/// <summary>
/// 默认是降序
/// </summary>
public bool OrderByDescending { get; set; } = true;
}

20
src/Easy.Result/When.cs

@ -0,0 +1,20 @@
using Easy.Result.Exceptions;
namespace Easy.Result;
/// <summary>
/// when 语句
/// </summary>
public class When
{
/// <summary>
/// 是
/// </summary>
/// <param name="isOk">是否</param>
/// <param name="message">消息</param>
/// <exception cref="SvcException">服务异常会被中间件拦截</exception>
public static void Is(bool isOk, string message)
{
if (isOk) throw new SvcException(message);
}
}
Loading…
Cancel
Save