C# 中流畅的界面模式允许像自然语言一样进行方法链接,使代码更加直观和富有表现力。此模式在 .NET 生态系统中很普遍,它增强了 API 在各种库和框架中的可用性。从配置数据库映射和验证数据到编写单元测试和定义迁移,Fluent Interfaces 可简化开发、减少错误并提高工作效率。在这篇文章中,我将讨论如何在 C# 中使用 Fluent 接口,并通过实际示例来说明它们的好处。
Fluent Interface 是一种设计模式,用于通过链接方法调用来提供一种简单易读的方式与 API 进行交互。此模式对于以类似于自然语言的方式构造复杂的查询、配置或设置属性特别有用。
假设我们有一个用于构建 SQL 查询的类。QueryBuilder
public class QueryBuilder
{
private string _select;
private string _from;
private string _where;
private string _orderBy;
public QueryBuilder Select(params string[] columns)
{
_select = "SELECT " + string.Join(", ", columns);
return this;
}
public QueryBuilder From(string table)
{
_from = "FROM " + table;
return this;
}
public QueryBuilder Where(string condition)
{
_where = "WHERE " + condition;
return this;
}
public QueryBuilder OrderBy(string column)
{
_orderBy = "ORDER BY " + column;
return this;
}
public string Execute()
{
return $"{_select} {_from} {_where} {_orderBy}";
}
}
var query = new QueryBuilder()
.Select("name", "age")
.From("users")
.Where("age > 18")
.OrderBy("name")
.Execute();
// Output: SELECT name, age FROM users WHERE age > 18 ORDER BY name
在此示例中,该类允许您链接方法以可读的方式生成 SQL 查询。QueryBuilder
Fluent Assertions 是一个 .NET 库,旨在帮助以更具可读性和表现力的方式在单元测试中编写断言。此库允许您编写明确传达测试意图的断言。
使用 Fluent Assertions,您可以编写如下测试:
using FluentAssertions;
using Xunit;
public class MyTests
{
[Fact]
public void TestValue(+)
{
int value = 5;
value.Should().BeGreaterThan(3);
value.Should().BeLessThan(10);
}
[Fact]
public void TestString()
{
string name = "John";
name.Should().StartWith("J")
.And.EndWith("n")
.And.HaveLength(4);
}
}
这些断言易于阅读和理解,使测试更易于维护。
Fluent NHibernate 是 NHibernate 的 XML 映射文件的替代方法,NHibernate 是 .NET 中流行的 ORM(对象关系映射器)。Fluent NHibernate 允许您使用 Fluent 接口在代码中定义映射,这比 XML 更易于管理和阅读。
下面介绍如何定义类的映射:Employee
public class Employee
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Department Department { get; set; }
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName).Not.Nullable();
Map(x => x.LastName).Not.Nullable();
References(x => x.Department).Not.Nullable();
}
}
在 NHibernate 会话设置中,将包含以下映射:
var sessionFactory = Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString("..."))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<EmployeeMap>())
.BuildSessionFactory();
此配置将 XML 映射文件替换为流畅代码的需要。
Entity Framework Core 中的 Fluent API 允许对模型进行更精确和更复杂的配置。当数据注释不足以表达所需的映射和配置时,它特别有用。
请考虑以下模型:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasKey(b => b.BlogId);
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired()
.HasMaxLength(200);
modelBuilder.Entity<Post>()
.HasKey(p => p.PostId);
modelBuilder.Entity<Post>()
.Property(p => p.Title)
.IsRequired()
.HasMaxLength(100);
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId);
}
}
在此示例中,Fluent API 用于配置主键、属性约束以及 和 之间的关系。BlogPost
Builder Pattern 可以使用流畅的界面实现,以逐步构建复杂的对象,使构建过程更具可读性和灵活性。
以下是如何使用 Fluent 接口构建对象的方法:Car
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
}
public class CarBuilder
{
private Car _car = new Car();
public CarBuilder WithMake(string make)
{
_car.Make = make;
return this;
}
public CarBuilder WithModel(string model)
{
_car.Model = model;
return this;
}
public CarBuilder WithYear(int year)
{
_car.Year = year;
return this;
}
public Car Build()
{
return _car;
}
}
var car = new CarBuilder()
.WithMake("Toyota")
.WithModel("Corolla")
.WithYear(2020)
.Build();
该类提供了一个用于构造对象的流畅接口,使代码易于阅读和理解。CarBuilderCar
FluentMigrator 是 .NET 的迁移框架,允许您使用 Fluent 接口定义数据库迁移。它提供了一种可读的方法来定义数据库架构应如何随时间推移而演变。
下面是创建表的简单迁移:Users
[Migration(202105031200)]
public class AddUserTable : Migration
{
public override void Up()
{
Create.Table("Users")
.WithColumn("Id").AsInt32().PrimaryKey().Identity()
.WithColumn("Username").AsString(255).NotNullable()
.WithColumn("Email").AsString(255).NotNullable();
}
public override void Down()
{
Delete.Table("Users");
}
}
若要应用迁移,通常使用命令行工具或将其集成到生成过程中。迁移以流畅的方式定义,使其易于阅读和写入。
Fluent Validation 是一个常用的 .NET 库,用于使用 Fluent 界面为业务对象构建强类型验证规则。
下面介绍了如何定义类的验证规则:Person
using FluentValidation;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(x => x.FirstName).NotEmpty().WithMessage("First name is required.");
RuleFor(x => x.LastName).NotEmpty().WithMessage("Last name is required.");
RuleFor(x => x.Age).InclusiveBetween(18, 60).WithMessage("Age must be between 18 and 60.");
RuleFor(x => x.Email).EmailAddress().WithMessage("A valid email is required.");
}
}
您可以将此验证逻辑集成到应用程序中,例如,在 ASP.NET Core 控制器中:
using Microsoft.AspNetCore.Mvc;
using FluentValidation.Results;
public class PersonController(IValidator<Person> validator) : Controller
{
[HttpPost]
public IActionResult Create(Person person)
{
ValidationResult result = validator.Validate(person);
if (!result.IsValid)
{
return BadRequest(result.Errors);
}
return Ok();
}
}
C# 中的 LINQ(语言集成查询)使用流畅的 API 以可读且富有表现力的方式启用查询集合。
下面是使用 LINQ 查询数字列表的简单示例:
using System;
using System.Collections.Generic;
using System.Linq;
public class LINQExample
{
public void Run()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbers = numbers
.Where(n => n % 2 == 0)
.Select(n => new { Number = n, IsEven = true })
.OrderBy(n => n.Number);
foreach (var item in evenNumbers)
{
Console.WriteLine($"{item.Number} is even: {item.IsEven}");
}
}
}
流畅的接口模式通常用于配置库或框架,使配置代码更具可读性。
AutoMapper 是 .NET 中流行的对象到对象映射库,它使用流畅的界面进行配置。
using AutoMapper;
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Source, Destination>()
.ForMember(dest => dest.DestProperty, opt => opt.MapFrom(src => src.SourceProperty));
}
}
public class Source
{
public string SourceProperty { get; set; }
}
public class Destination
{
public string DestProperty { get; set; }
}
日志记录库(如 Serilog)使用流畅的界面来配置日志记录行为。
using Serilog;
public class LoggingExample
{
public void ConfigureLogging()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Log.Information("This is a test log message");
}
}
以下是使用 Fluent 模式的关键领域的摘要:
以上类库被广泛用于各种库和框架中,以创建易于阅读和编写的 API。这些流畅的界面增强了代码的可读性,减少了出错的可能性,并通过允许与 API 进行更直观的交互来提高我们的工作效率。