select子句表达式问题

时间:2014-11-16 19:18:41

标签: c# linq entity-framework f#

我正在尝试使用F#,因此决定使用F#作为新项目的服务层。现在我正在尝试将实体映射到F#类型,但我无处可去!问题似乎是Select条款。

在下面的示例中,IDataContext是一个实体框架工作单元,与人DbSet(在C#项目中定义)

interface IDataContext
{
     DbSet<Person> Persons { get; }
}

方法一

[<CLIMutable>]
type Person = {
    Id: int
    Name: string
}

type PersonService(context: IDataContext)
    member this.GetPerson(personId) = 
        let person = context.Persons
                            .Where(fun p -> p.Id = personId)
                            .Select(fun p -> { Id = p.Id; Name = p.Name })
                            .FirstOrDefaultAsync()
        person

这里似乎发生的问题是linq抱怨需要无参数构造函数。所以我尝试了另一种方式

type public Person() =
    [<DefaultValue>] val mutable Id : int
    [<DefaultValue>] val mutable Name : string

type PersonService(context: IDataContext)
    member this.GetPerson(personId) = 
        let person = context.Persons
                            .Where(fun p -> p.Id = personId)
                            .Select(fun p -> new Person(Id = p.Id, Name = p.Name))
                            .FirstOrDefaultAsync()
        person

现在我得到了

could not convert the following f# quotation to a linq expression tree

我是否必须将fun转换为表达式?我以为F# 3.0已经这样做了?

修改

在最后一个例子中,我刚试过Select(fun p -> new Person())并且它有效。那么初始化属性的方式很糟糕? C#的相应fun p -> new Person(Id = p.Id, Name = p.Name)会是什么?

2 个答案:

答案 0 :(得分:1)

如果你想使用LINQ和异步查询,你需要一个解决方法,因为LINQ select不支持记录,你需要使用async workflow

type PersonService(context: IDataContext)
member this.GetPerson(personId) = 
    async {
        // get the actual context object
        let! person = Async.AwaitTask  
                         (context.Persons
                                 .Where(fun p -> p.Id = personId)
                                 .FirstOrDefaultAsync()))

        // map context object, if it's not null
        if obj.ReferenceEquals(person, null)
            then return None
            else return Some ({ Id = person .Id; Name = person .Name })
    } |> Async.StartAsTask

值得注意的是,LINQ和查询表达式可以返回null,您必须在某个时刻处理空值。我更喜欢尽快将它们翻译成选项。此外,我正在使用Async.StartAsTask将Async对象转换为热门任务。

编辑:

为了限制退货实体的规模,我认为这样可行(我现在没有时间对此进行全面测试):

type PersonService(context: IDataContext) =
    member this.GetPerson(personId) = 
        async {
            // get the actual context object
            let! person = Async.AwaitTask
                           (query { for p in context.Persons do
                                    where (p.Id = personId)
                                    select (p.Id, p.Name)
                            }).FirstOrDefaultAsync()

            // map context object, if it's not null
            if obj.ReferenceEquals(person, null)
                then return None
                else return person |> (fun (id, name) -> Some ({ Id = id; Name = name }))
        } |> Async.StartAsTask

答案 1 :(得分:0)

您是否尝试使用属性而不是字段来定义Person

type Person() =
    member val Id = Unchecked.defaultof<int> with get, set    
    member val Name = Unchecked.defaultof<String> with get, set

您可以使用F#查询表达式查询EF,如下所示:

let person =
    query {
        for p in context.Persons do
        where (p.Id = personId)
        select p }
    |> Seq.head

(你可能不得不使用上述方法来实现它,但这应该是它的主旨。)