我是一名c#家伙,他最近学习过JavaScript,并考虑沿着f#路走下去。我喜欢JavaScript的功能性,但是很难将它带入我的c#代码中。我看下面的代码块,它真的很难看。对于那些自然倾向于函数式编程的人来说,在c#(甚至f#)中执行此块的更好方法是什么。
(或者我正在吠叫错误的树,希望改善它)
/// <summary>
/// Get the current Tenant based on tenantNameOverride if present,
/// otherwise uses urlHost to figure it out
/// case independent
/// if urlHost and tenantNameOverride empty then
/// throws exception
/// if tenantNameOverride specified and not found
/// throw exception
/// if tenantNameOverride not specified and there
/// is no default tenant, then throw exception
/// </summary>
/// <param name="tenants"></param>
/// <param name="urlHost"></param>
/// <param name="tenantNameOverride"></param>
/// <returns></returns>
private static Tenant GetTenantBasedOnUrlHost(
List<Tenant> tenants, string urlHost=null,
string tenantNameOverride=null)
{
if (String.IsNullOrEmpty(urlHost) &&
String.IsNullOrEmpty(tenantNameOverride))
{
throw new ApplicationException(
"urlHost or tenantName must be specified");
}
Tenant tenant;
if (String.IsNullOrEmpty(tenantNameOverride))
{
tenant = tenants.
FirstOrDefault(a => a.DomainName.ToLower().Equals(urlHost)) ??
tenants.FirstOrDefault(a => a.Default);
if (tenant == null)
{
throw new ApplicationException
("tenantName must be specified, no default tenant found");
}
}
else
{
tenant = tenants.FirstOrDefault
(a => a.Name.ToLower() == tenantNameOverride.ToLower());
if (tenant == null)
{
throw new ApplicationException
("tenantNameOverride specified and not found");
}
}
return tenant;
}
/ *********************更新以下***************** /
Per Jon Skeet的建议,我已经提出了两种方法。对于空字符串违规的顶部错误检查除外,我不确定我们可以在c#中轻松避免。抛出一个异常也觉得有点奇怪但没有找到,但就我的目的而言,这些方法应该总是找到一个租户,如果不是这是意外的,一个例外似乎是合理的。
这似乎更清晰,更贴近单一责任的设计指南。也许这就是我对我的解决方案不喜欢的东西,它与功能性或非功能性无关。
/// <summary>
/// Return tenant based on URL (or return default tenant if exists)
/// </summary>
/// <param name="tenants"></param>
/// <param name="urlHost"></param>
/// <returns></returns>
private static Tenant GetTenantBasedOnUrl(
List<Tenant> tenants, string urlHost)
{
if (String.IsNullOrEmpty(urlHost))
{
throw new ApplicationException(
"urlHost must be specified");
}
var tenant = tenants.
FirstOrDefault(a => a.DomainName.ToLower().Equals(urlHost)) ??
tenants.FirstOrDefault(a => a.Default);
if (tenant == null)
{
throw new ApplicationException
("tenant not found based on URL, no default found");
}
return tenant;
}
/// <summary>
/// Get exact tenant name match and do not return default even
/// if exists.
/// </summary>
/// <param name="tenants"></param>
/// <param name="tenantNameOverride"></param>
/// <returns></returns>
private static Tenant GetTenantByName(List<Tenant> tenants,
string tenantNameOverride)
{
if (String.IsNullOrEmpty(tenantNameOverride))
{
throw new ApplicationException(
"tenantNameOverride or tenantName must be specified");
}
var tenant = tenants.FirstOrDefault
(a => a.Name.ToLower() == tenantNameOverride.ToLower());
if (tenant == null)
{
throw new ApplicationException
("No tenant Found (not checking for default)");
}
return tenant;
}
}
答案 0 :(得分:3)
以下是我如何解决F#中的问题。
首先,如果我正确理解了要求,该函数的调用者必须提供 域名,或要搜索的租户名称。在C#中,这样的排他规则难以建模,这导致必须至少指定一个规则,但如果指定了两个,则其中一个参数优先。
虽然使用C#类型系统难以定义这样的规则,但使用歧视联盟在F#中声明是微不足道的:
type TenantCriterion =
| DomainName of Uri
| Name of string
这意味着搜索租户的标准可以 一个DomainName
或一个名称,但不能两者兼而有之。
在我对DomainName
的定义中,我将类型更改为System.Uri
。当您处理网址时,使用Uri
值通常比string
值更安全。
不是将string
值转换为小写,而是使用StringComparison.OrdinalIgnoreCase
比较它们更安全,如果这是你想要的,那么有各种微妙的本地化如果转换例如问题小写的土耳其语字符串(转换是有损的)。
最后,我将查询更改为返回Tenant option
而不是抛出异常。在功能编程中,我们倾向于避免异常。如果您想要比option
更详细的异常处理,可以use the Either monad。
所有这一切都说明了这里找到租户的功能的可能实现:
let findTenant tenants = function
| DomainName u ->
let t = tenants |> List.tryFind (fun x -> x.DomainName = u)
match t with
| Some t -> Some t
| None -> tenants |> List.tryFind (fun x -> x.IsDefault)
| Name n ->
tenants
|> List.tryFind
(fun x -> n.Equals(x.Name, StringComparison.OrdinalIgnoreCase))
此函数的类型为Tenant list -> TenantCriterion -> Tenant option
。如果您想要更懒惰的评估,可以将List.tryFind
替换为Seq.tryFind
。