Fluentx:一个特殊的 .NET 库






4.80/5 (63投票s)
本文介绍 Fluentx .NET 库
引言
Fluentx 是我创建的一个 .NET 库,旨在帮助我进行任何开发项目。您可以将其视为一个实用工具库,但它实际上比这更强大,它包含各种实用方法、辅助方法和设计模式,将帮助任何使用 .NET 框架的开发人员。我还从网上收集了一些扩展方法(有些经过优化,有些保持原样),这些方法是我之前在参与的项目中使用过的。因此,如果您认为您有一个可以帮助他人的辅助方法、扩展方法、模式或类,并且您认为它可以集成到 Fluentx 中,请随时提出。主要限制是依赖性,因为我尽可能希望 Fluentx 具有可移植性,并且不依赖于 Web、Windows 或任何其他库或框架。
其目标是 Fluentx 能够帮助开发人员进行编码,并使事情变得越来越简单。
Fluentx 涵盖了所有主要的 C# 控制语句,消除了其中的限制,并增加了更多功能。该程序集包含 5 个主要类别:C# 控制语句、辅助类、扩展方法、规范模式、对象到对象映射器。随着时间的推移,该程序集会越来越大,我们将不断添加更多内容,以便它能被广泛使用并帮助所有人。
它还实现了规范模式,用于验证任何类型的代码,无论是业务验证还是其他任何类型的验证。
功能类别
因此,Fluentx 拥有许多功能,截至撰写本文时,我可以将其分为五个主要类别:
- C# 控制语句
- 辅助类
- 扩展方法
- 模式
- Fluentx Mapper(对象到对象映射器)
使用 Fluentx 非常直接简单:添加引用,然后就可以使用该库了。对于 C# 控制语句和一些辅助方法,您需要从 Fluentx 的主类 Fx 开始(Fx 也代表数学表达式 F(x) => x 的函数)。
下面您将找到使用 Fluentx 不同类别功能的代码片段。
1. C# 控制语句
C# 控制语句,如 if、switch、try catch、while、for、foreach 等... 都被转换为 lambda 表达式,并可用作函数式语句。要使用它们,请从声明主类 Fx 开始,然后是您要使用的控制语句。下面是一些示例,其余的也可以在库本身中找到文档。
Switch 语句
C# switch 语句是一个非常有用的语句,它提供了比 if 语句更高的可读性,尤其是在条件越来越多的时候。switch 语句的主要限制是 switch case 应该包含一个常量值,您不能将其用于任何您想要的变量。一个很好的例子是与类型一起使用,当您想对类型实例执行 switch 语句时。
Fx.Switch("Fluentx".GetType()) .Case<int>().Execute(() => { result = 1; }) .Case<string>().Execute(() => { result = 2; }) .Default(() => { result = 0; });
ForEach/ForEvery(带项目和索引)
很多时候我发现自己想要使用 foreach 语句并获取当前项目的索引。
IList<Custom> customers = new List<Customer>();
customers.Foreach((item, index)=>{
//item is the current item in the loop, and index of the current item in the loop
});
当然,foreach 在 Fluentx 中有一个同义词 ForEvery,它与 foreach 的功能完全相同。我添加这个是因为一些 .NET 库使用 ForEach,这可能会发生冲突,您可以通过使用 ForEvery 来解决。
WhileFalse、WhileTrue、WhileTrueFor 和 WhileFalseFor
int result = 0; Fx.WhileTrue(() => { ++result; return result != 6; }); int result = 0; Fx.WhileFalse(() => { ++result; return result == 6; }); Fx.WhileTrueFor(()=>IsProcessComplete(), 4); Fx.WhileFalseFor(()=>IsProcessComplete(), 5);
失败时重试(长轮询)
int result=0; Fx.RetryOnFail(() => { ++result; return result > 5; }); //Or int result=0; Fx.RetryOnFail(() => { ++result; return result > 5; }, attemptSleepInMilliSeconds: 10);
Try - SwallowIf
int result = 0; Fx.Try(() => { throw new NotImplementedException(); ++result; }).SwallowIf<NotImplementedException>(); //Swallows a certain exception and prevent throwing it.
using语句
Fx.Using(new PrivateDisposableTestEntity(), (instance) => { });
Fx.To(Value)
//All primitive types are supported to parse a string with an optional default value var birthDate = Fx.ToDateTime("Some Text", DateTime.Now); var age = Fx.ToInt32("Some Text", -1); var age = Fx.ToInt32("Some Text");
2. 辅助类
Period 类
Period 类表示两个 datetime 实例之间的时间段。有时我们有一组不同的日期,我们需要确定它们之间是否存在重叠。
var firstPeriod = new Period(date1, date2); var secondPeriod = new Period(date3, date4); bool isOverlap = firstPeriod.IsOverlap(secondPeriod);//Edges NOT considered in the overlap bool isOverlap1 = firstPeriod.IsOverlap(secondPeriod, true); //Edges are considered in the overla bool flag = firstPeriod.IsWrap(DateTime.Now);//Checks if the current date is within that period
Guard 类
使用此类以更简洁的方式验证某些条件和断言,而无需使用 IF 语句。
Guard.Against<NotImplementedException>(myFlag);
PredicateBuilder 类
Predicate Builder 是使用表达式构建布尔逻辑的地方。它一个很好的用途是与 linq 查询一起使用,因为它们缺少 Or 功能。使用此谓词生成器,您可以轻松执行 Or 和 AND 操作。它还有一个起始点值 TRUE 或 FALSE。网上有很多实现,我认为这个实现很好。
var predicate = PredicateBuilder.True<Customer>(); foreach (var product in products) { predicate = predicate.And(c => c.Products.Any(x => x.Id == productId)); }
3. 扩展方法
可空性检查
var customer = GetCustomerById(id); var flag= customer.IsNull(); var flag = customer.IsNotNull();
IfTrue 和 IfFlase
bool flag = GetBooleanValueMethod();
flag.IfTrue(()=>{ //Do Anything here });
flag.IfFlase(()=>{ //Do Anything here });
随机
IList<Customer> customers = GetCustomers(); var randomCustomer = customers.Random();
Is
var customer = GetCustomer(); var flag = customer.Is<Customer>();
逻辑运算符
var flag = flag1.Or(flag2).Xor(flag3).And(flag4).And(()=> {return true;}).Not();
之间
var value = 7; var flag = value.Between(5, 9);
In/NotIn
string parameter = null; var result = parameter.In(new string[] { "one", null, "three" }); var result = parameter.NotIn(new string[] { "one", null, "three" });
MinBy/MaxBy
根据指定的谓词,找出 IEnumerable 中的最小/最大项。
DateTime[] dates = new DateTime[] { DateTime.Now, DateTime.Now.AddYears(3) }; var min = dates.MinBy(x => x.Year);
随机
执行指定列表中项的随机返回的扩展方法。
DateTime[] dates = new DateTime[] { DateTime.Now, DateTime.Now.AddYears(3) }; var randomDate = dates.Random();
To[PremitiveType]
ToInt、ToInt16、ToInt32、ToInt64、ToUInt、ToUInt16、ToUInt32、ToUInt64 ToDateTime、ToDouble、ToFloat、ToLong、ToULong、ToByte、ToSByte、ToBool、ToGuid、ToDateTime
ToCSV
执行从指定列表生成逗号分隔字符串的扩展方法。
DateTime[] dates = new DateTime[] { DateTime.Now, DateTime.Now.AddYears(3) }; var csvDates = dates.ToCSV();
与 DateTime 相关的扩展方法
DateTime date = 24.September(2014);
其他扩展方法
以及您可以使用的许多其他扩展方法。
4. 模式
规范模式
规范模式是通过布尔逻辑将多个业务规则组合在一起。我有一个很好的这种模式的实现,并且我在网上找到了 Govindaraj Rangaraj 编写的一个非常有用的有趣的入门级实现。这是原始文章的链接,位于 codeproject:
https://codeproject.org.cn/Articles/670115/Specification-pattern-in-Csharp
我对它做了一些微调,但最终核心概念仍然存在。
ISpecification<int> rule1 = new ExpressionSpecification<int>(x => x == 1, "rule1 failed"); ISpecification<int> rule2 = new ExpressionSpecification<int>(x => x == 2, "rule2 failed"); ISpecification<int> rule3 = new ExpressionSpecification<int>(x => x == 3, "rule3 failed"); ISpecification<int> rule4 = rule1.Or(rule2).Or(rule3); var result = rule4.ValidateWithMessages(4);
5. Fluentx Mapper(对象到对象映射器)
Fluentx Mapper 是一个对象到对象映射器。它非常简单、易用、直接。它提供了您期望映射器执行所需映射的最常用功能。它还为您提供了手动映射属性和字段的选项。
以下是一个使用映射器并附带说明的示例:
var mapper = new Mapper<DTOCustomer, Customer>() .UserMapper<DTOProfile, Profile>() .Conditional(x => x.Description, src => src.Description != string.Empty) .Ignore(dest => dest.BirthDate) .IgnoreIf(dest => dest.Serial, src=> { return src.Serial == string.Empty;}) .For(dest => dest.Order, src => RepoAccount.LoadOrder(src.OrderId)) .ForIf(dest => dest.Number, src => src.Number, src => src.AutoGenerate) .Resolve((src, dest)=>{ .... do whatever you want here ... }); var customer = mapper.Map(dtoCustomer);
基本约定
首先,我们需要知道 Fluentx Mapper 使用约定来映射属性。因此,它假设并尝试将源属性映射到目标属性,如果属性名称匹配。因此,一旦您创建
var mapper = new Mapper<DTOCustomer, Customer>();
那么基本功能就已启用并运行,这就是对象到对象映射器的默认行为。
嵌套映射器
现在,您可以将另一个映射器的使用注入到您拥有的映射器中,如下所示:
var mapper = new Mapper<DTOCustomer, Customer>() .UserMapper<DTOProfile, Profile>();
在这里,我们指示 fluentx mapper,我们正在将 DTOCustomer 映射到 Customer,并且每当找到类型为 DTOProfile 的 Customer 中的属性时,我们都会使用 DTOProfile 的内部映射器来映射到 Profile。
条件映射
属性的条件映射是为了覆盖基本的映射约定,即在条件不满足时 *不* 映射指定属性。因此,如果条件为真,则会映射该属性。
.Conditional(x => x.Description, src => src.Description != string.Empty)
Ignore
忽略指定属性的映射(不要映射它)
.Ignore(dest => dest.BirthDate)
IgnoreIf
如果条件评估为真,则忽略指定属性的映射。
.IgnoreIf(dest => dest.Serial, src=> { return src.Serial == string.Empty;})
For
使用指定的动作映射指定属性,该动作以源作为参数。
.For(dest => dest.Order, src => RepoAccount.LoadOrder(src.OrderId))
ForIf
使用指定的动作映射指定属性,该动作以源作为参数,前提是指定动作的评估为真。
.ForIf(dest => dest.Number, src => src.Number, src => src.AutoGenerate)
解析
当所有映射完成后,要执行的自定义操作,可用于手动操作映射,因为它具有源和目标作为参数。
.Resolve((src, dest)=>{ .... do whatever you want here ... });
集中式映射
有时您想在代码的不同位置使用相同的映射。为了做到这一点,Fluentx Mapper 可以通过继承以这种方式使用,使其集中化或可重用:
public class CustomerMapper : Mapper<DTOCustomer, Customer> { public CustomerMapper() { this.UserMapper<DTOProfile, Profile>(); this.Conditional(x => x.Description, src => src.Description != string.Empty); this.Ignore(dest => dest.BirthDate); this.IgnoreIf(dest => dest.Serial, src=> { return src.Serial == string.Empty;}); this.For(dest => dest.Order, src => RepoAccount.LoadOrder(src.OrderId)); this.ForIf(dest => dest.Number, src => src.Number, src => src.AutoGenerate); this.Resolve((src, dest)=>{ .... do whatever you want here ... }); //Or you can chain the methods after the first call } } var mapper = new CustomerMapper(); var customer = mapper.Map(dtoCustomer);
当然,您现在可以缓存该映射器并在代码中的任何地方按您期望的方式重用它。
摘要
我打算让 Fluentx 越来越强大。如果您有任何我认为可以包含在其中的代码片段,请告诉我,这样我们就可以共享它,使其变得更大更好。