.net - 在 F# 中,是否有使用用于相等和排序比较的主键创建记录类型的快捷方式?

标签 .net orm f# dto

我知道 F# 编译器可以生成 IEquatable<T> 的默认实现。和 IComparable<T> ,以及覆盖默认值 GetHashCodeEquals记录和类的方法。但相比之下,这些实现使用了所有字段。我需要创建一些 DTO 类型,其中相等性和排序顺序应仅基于主键字段。有没有一些方便的机制,比如这个属性?

这是我的课:
(我可能犯了一些 F# 语法错误。还是有点新的。)

type Customer() =
    member val Id = 0 with get, set
    member val Name = "" with get, set
    member val PhoneNumber = "" with get, set

我可以做这样的事情:
[<CompareByKey>]
type Customer() =
    [<PrimaryKey>] member val Id = 0 with get, set
    member val Name = "" with get, set
    member val PhoneNumber = "" with get, set

而不是这个:
type Customer() =
    member val Id = 0 with get, set
    member val Name = "" with get, set
    member val PhoneNumber = "" with get, set

    override Equals(obj) = 
        match obj with
        | :? Customer as c -> this.Id = c.Id
        | _ -> false

    override GetHashCode() = hash this.Id

    interface IEquatable<Customer> with
        member Equals(c) = this.Id = c.Id

    interface IComparable<Customer> with
        member CompareTo(c) = this.Id.CompareTo(c.Id)

编辑:

我认为这可能是使用抽象基类在 C# 中更好地解决的问题。 (也可以在 F# 中实现,但它有点开始变得不习惯了。)
public abstract class EntityBase<TEntity> : 
    IEquatable<TEntity>, 
    IComparable<TEntity>
    where TEntity : EntityBase<TEntity> {

    protected EntityBase(int id) { 
        this.Id = id; 
    }

    public int Id { get; }

    public sealed override int GetHashCode() => Id.GetHashCode();

    public sealed override bool Equals(object obj) => 
        Equals(this, obj as TEntity);

    public bool Equals(TEntity other) => 
        (other != null) && (this.Id == other.Id);

    public int CompareTo(TEntity other) => 
        (other == null) ? 1 : this.Id.CompareTo(other.Id);

}

最佳答案

您可以使用反射来拉出主键。反射可能很昂贵,因此请确保您只收到一次调用来搜索每种类型的键(我在下面的代码中留下了一些 printfn 以便我可以仔细检查)

open System
[<AttributeUsage(AttributeTargets.Property)>]
type PrimaryKeyAttribute() =
    inherit Attribute()
[<CLIMutable()>]
type Customer =
    {
        [<PrimaryKey>] 
        Id: int
        Name: string
    }
let getPrimaryKey<'T, 'U when 'U:> IComparable> (): 'T -> 'U =
    printfn "Starting to search for keys."
    typeof<'T>.GetProperties()
    |> Seq.tryFind (fun p -> 
        let attr = p.GetCustomAttributes(typeof<PrimaryKeyAttribute>, false)
        attr.Length > 0
    )
    |> function 
        | Some p -> 
            (fun t -> p.GetMethod.Invoke(t, [| |]) :?> 'U)
        | _ -> failwith "No PrimaryKey attribute found"

对客户列表进行排序现在变为:
let customers = 
    [
        { Id = 4; Name = "Alice" }
        { Id = 1; Name = "Eve" }
        { Id = 1; Name = "Charlie" }
        { Id = 1; Name = "Bob" }
    ]
let sorted = 
    customers
    |> List.sortBy (getPrimaryKey())

你会得到:
val sorted : Customer list =
 [{Id = 1;
 Name = "Eve";}; {Id = 1;
                 Name = "Charlie";}; {Id = 1;
                                      Name = "Bob";}; {Id = 4;
                                                       Name = "Alice";}]

而正常排序会给你鲍勃;查理;前夕;爱丽丝。您可能需要预先计算每种类型的 key getter:
let getCustomerKey<'U when 'U:> IComparable> : Customer -> 'U = 
    getPrimaryKey() 
let sorted2 = 
    customers
    |> List.sortBy getCustomerKey

而且,正如其他人已经指出的,[<CLIMutable()>]在处理依赖于默认构造函数和变异的代码时很有帮助。见 documentation here

关于.net - 在 F# 中,是否有使用用于相等和排序比较的主键创建记录类型的快捷方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38257283/

相关文章:

f# - map 函数的后置条件

.net - 如何快速将大量 XML 数据放入我的数据库中?

python - 弄清楚 Django ORM 的问题,查询内部联接的正确方法是什么?

delphi - 关于在 Delphi 中分离数据访问、业务逻辑和 GUI 的任何建议

grails - 如何正确使用一对多关系中的mappedBy

parsing - 解析项的 FParsec 和后缀修饰符

c# - 使用 Graph API 将更新发布到 Facebook 粉丝页面?

c# - 我们如何在 C# 中创建参数化属性

.net - MEF 从不在执行程序集而是另一个 dll 中的文件夹中加载插件(dll)。你能帮忙吗?

hash - 我可以对此代码使用折叠(或其他类型的缩减)吗?