Parcourir la source

Result模式

master
qdjjx il y a 3 ans
Parent
révision
096b046ab2

+ 5
- 0
专题/后端/DDD/4、Endpoint替换Controller.md Voir le fichier

@@ -0,0 +1,5 @@
1
+当在`Controller`中使用`MediatR`后,业务逻辑被封装了单独的`IRequest`和`IRequestHandler`。但是`Controller`层面违反了单一职责原则。使用`Endpoint`可以做到一个接口一次封装。
2
+
3
+
4
+
5
+源代码:`F:\demos\CleanArchitecture\UseEndpoint\WebApplication1`

+ 233
- 0
专题/后端/DDD/5、Result模式.md Voir le fichier

@@ -0,0 +1,233 @@
1
+# 引出`Result`模式
2
+
3
+
4
+
5
+通常的方式
6
+
7
+```
8
+public Customer GetCustomer(int customerId)
9
+{
10
+  // more logic
11
+  return customer;
12
+}
13
+
14
+public Customer CreateCustomer(string firstName, string lastName)
15
+{
16
+  // more logic
17
+  return customer;
18
+}
19
+```
20
+
21
+在逻辑中,我们可能会判断`customerId`不存在,判断`lastName`没有提供,甚至当前用户是否有权限创建用户。
22
+
23
+我们在处理正常和异常这两种情况。
24
+
25
+`Result`模式用来对这两种情况进行封装,并且统一了返回结果的格式。
26
+
27
+```
28
+public async Task<Result<BlogCategory>> UpdateAsync(BlogCategory blogCategory)
29
+{
30
+    if (Guid.Empty == blogCategory.BlogCategoryId) return Result<BlogCategory>.NotFound();//没有找到
31
+
32
+    var validator = new BlogCategoryValidator();
33
+    var validation = await validator.ValidateAsync(blogCategory);
34
+    if (!validation.IsValid)
35
+    {
36
+        return Result<BlogCategory>.Invalid(validation.AsErrors());//验证异常
37
+    }
38
+
39
+    var itemToUpdate = (await GetByIdAsync(blogCategory.BlogCategoryId)).Value;
40
+    if (itemToUpdate == null)
41
+    {
42
+        return Result<BlogCategory>.NotFound();
43
+    }
44
+
45
+    itemToUpdate.Update(blogCategory.Name, blogCategory.ParentId);
46
+
47
+    return Result<BlogCategory>.Success(await _blogCategoryRepository.UpdateAsync(itemToUpdate));
48
+}
49
+```
50
+
51
+结果
52
+
53
+![ddd12](F:\SourceCodes\DDWiki\专题\后端\DDD\ddd12.png)
54
+
55
+# 接口
56
+
57
+```
58
+    [ApiController]
59
+    [Route("mediatr/[controller]")]
60
+    public class WeatherForecastController : ControllerBase
61
+    {
62
+        private readonly IMediator _mediator;
63
+        private readonly ILogger<WeatherForecastController> _logger;
64
+
65
+        public WeatherForecastController(
66
+            IMediator mediator,
67
+            ILogger<WeatherForecastController> logger)
68
+        {
69
+            _mediator = mediator;
70
+            _logger = logger;
71
+        }
72
+
73
+        /// <summary>
74
+        /// This uses a filter to convert an Ardalis.Result return type to an ActionResult.
75
+        /// This filter could be used per controller or globally!
76
+        /// </summary>
77
+        /// <param name="model"></param>
78
+        /// <returns></returns>
79
+        [TranslateResultToActionResult]//把Result的结果转换成ActionResult的结果,可以定义我们自己的特性,这点特别重要
80
+        [HttpPost("Create")]
81
+        public Task<Result<IEnumerable<WeatherForecast>>> CreateForecast([FromBody] NewForecastCommand model)
82
+        {
83
+            // One might try to perform translation from Result<T> to an appropriate IActionResult from within a MediatR pipeline
84
+            // Unfortunately without having Result<T> depend on IActionResult there doesn't appear to be a way to do this, so this
85
+            // example is still using the TranslateResultToActionResult filter.
86
+            return _mediator.Send(model);
87
+        }
88
+
89
+        public class NewForecastCommand : IRequest<Result<IEnumerable<WeatherForecast>>>
90
+        {
91
+            [Required]
92
+            public string PostalCode { get; set; }
93
+        }
94
+
95
+        public class NewForecastHandler : IRequestHandler<NewForecastCommand, Result<IEnumerable<WeatherForecast>>>
96
+        {
97
+            private readonly WeatherService _weatherService;
98
+
99
+            public NewForecastHandler(WeatherService weatherService)
100
+            {
101
+                _weatherService = weatherService;
102
+            }
103
+            public Task<Result<IEnumerable<WeatherForecast>>> Handle(NewForecastCommand request, CancellationToken cancellationToken)
104
+            {
105
+                var result = _weatherService.GetForecastAsync(new ForecastRequestDto { PostalCode = request.PostalCode });
106
+                return result;
107
+            }
108
+        }
109
+    }
110
+```
111
+
112
+在`WeatherService`中
113
+
114
+```
115
+    public class WeatherService
116
+    {
117
+        public WeatherService(IStringLocalizer<WeatherService> stringLocalizer)
118
+        {
119
+            _stringLocalizer = stringLocalizer;
120
+        }
121
+        private static readonly string[] Summaries = new[]
122
+{
123
+            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
124
+        };
125
+
126
+        private IStringLocalizer<WeatherService> _stringLocalizer;
127
+
128
+        public Task<Result<IEnumerable<WeatherForecast>>> GetForecastAsync(ForecastRequestDto model)
129
+        {
130
+            return Task.FromResult(GetForecast(model));
131
+        }
132
+
133
+        public Result<IEnumerable<WeatherForecast>> GetForecast(ForecastRequestDto model)
134
+        {
135
+            if (model.PostalCode == "NotFound") return Result<IEnumerable<WeatherForecast>>.NotFound();//没有找到
136
+
137
+            // validate model
138
+            if (model.PostalCode.Length > 10)
139
+            {
140
+                return Result<IEnumerable<WeatherForecast>>.Invalid(new List<ValidationError> {
141
+                    new ValidationError
142
+                    {
143
+                        Identifier = nameof(model.PostalCode),
144
+                        ErrorMessage = _stringLocalizer["PostalCode cannot exceed 10 characters."].Value }
145
+                });//模型验证失败
146
+            }
147
+
148
+            // test value
149
+            if (model.PostalCode == "55555")
150
+            {
151
+                return new Result<IEnumerable<WeatherForecast>>(Enumerable.Range(1, 1)//返回成功的结果
152
+                    .Select(index =>
153
+                    new WeatherForecast
154
+                    {
155
+                        Date = DateTime.Now,
156
+                        TemperatureC = 0,
157
+                        Summary = Summaries[0]
158
+                    }));
159
+            }
160
+
161
+            var rng = new Random();
162
+            return new Result<IEnumerable<WeatherForecast>>(Enumerable.Range(1, 5)//返回成功的结果
163
+                .Select(index => new WeatherForecast
164
+                {
165
+                    Date = DateTime.Now.AddDays(index),
166
+                    TemperatureC = rng.Next(-20, 55),
167
+                    Summary = Summaries[rng.Next(Summaries.Length)]
168
+                })
169
+            .ToArray());
170
+        }
171
+    }
172
+```
173
+
174
+如何影射`Result`结果呢?
175
+
176
+```
177
+   public class TranslateResultToActionResultAttribute : ActionFilterAttribute
178
+    {
179
+        public override void OnActionExecuted(ActionExecutedContext context)
180
+        {
181
+            if (!((context.Result as ObjectResult)?.Value is IResult result)) return;
182
+
183
+            if (!(context.Controller is ControllerBase controller)) return;
184
+
185
+			//判断IResult的ResultStatus
186
+            if (result.Status == ResultStatus.NotFound)
187
+                context.Result = controller.NotFound();
188
+
189
+            if (result.Status == ResultStatus.Invalid)
190
+            {
191
+                foreach (var error in result.ValidationErrors)
192
+                {
193
+                    // TODO: Fix after updating to 3.0.0
194
+                    (context.Controller as ControllerBase)?.ModelState.AddModelError(error.Identifier, error.ErrorMessage);
195
+                }
196
+
197
+                context.Result = controller.BadRequest(controller.ModelState);
198
+            }
199
+
200
+            if (result.Status == ResultStatus.Ok)
201
+            {
202
+                context.Result = new OkObjectResult(result.GetValue());
203
+            }
204
+        }
205
+    }
206
+```
207
+
208
+`IResult`
209
+
210
+```
211
+    public interface IResult
212
+    {
213
+        ResultStatus Status { get; }
214
+        IEnumerable<string> Errors { get; }
215
+        List<ValidationError> ValidationErrors { get; }
216
+        Type ValueType { get; }
217
+        Object GetValue();
218
+    }
219
+```
220
+
221
+`ResultStatus`
222
+
223
+```
224
+    public enum ResultStatus
225
+    {
226
+        Ok,
227
+        Error,
228
+        Forbidden,
229
+        Invalid,
230
+        NotFound
231
+    }
232
+```
233
+

BIN
专题/后端/DDD/ddd12.png Voir le fichier


BIN
决策/1.png Voir le fichier


+ 6
- 0
决策/决策.md Voir le fichier

@@ -0,0 +1,6 @@
1
+> 购买云厂商MySql数据库
2
+
3
+决策:购买云厂商MySql数据库
4
+发起人:
5
+
6
+目前我们用的是自建MySQL服务器,好处是免费的,但是后期如果考虑维护性、安全性等方面的话,购买云厂商MySql数据库更合适。

BIN
决策/决策模板.docx Voir le fichier


+ 8
- 0
团队/解决一次git问题.md Voir le fichier

@@ -0,0 +1,8 @@
1
+出现非常混乱的情况,首先本地回滚到某个版本:
2
+```
3
+git reset xxx --hard
4
+```
5
+然后让远程也回滚
6
+```
7
+git push origin master -f
8
+```

Chargement…
Annuler
Enregistrer