Cause sentence expression problems
I am experimenting with F # and as such decided to use F # as a service layer for a new project. Now I am trying to map an entity to an F # type, but I am not getting anything! The problem seems to be in the proposal Select
.
In the example below, the IDataContext represents the entity structure unit for working with human DbSet
(defined in the project C#
)
interface IDataContext
{
DbSet<Person> Persons { get; }
}
Method one
[<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
The problem that seems to come up here is that linq is complaining that a parameterless constructor is required. So I tried another way
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
and now i get
could not convert the following f# quotation to a linq expression tree
Do I need to convert fun
to an expression? I thought I F# 3.0
already did it?
Edit
For the last example I just tried Select(fun p -> new Person())
it and it works. So how does this initialize properties, bad? What would match C#
of fun p -> new Person(Id = p.Id, Name = p.Name)
?
source to share
If you want to use LINQ and asynchronous queries, you need a job as LINQ selection does not support writes and you need to use an asynchronous 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 not null if obj.ReferenceEquals(person, null) then return None else return Some ({ Id = person .Id; Name = person .Name }) } |> Async.StartAsTask
It's worth noting that LINQ and query expressions can return null, you need to handle nulls at some point. I prefer to translate them into parameters as soon as possible. Also, I am converting an Async object to a hot task using Async.StartAsTask
.
Edit:
To limit the size of the returned object, I think this will work (I don't have time right now to fully test this):
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 not null
if obj.ReferenceEquals(person, null)
then return None
else return person |> (fun (id, name) -> Some ({ Id = id; Name = name }))
} |> Async.StartAsTask
source to share
Have you tried defining Person
like this, with properties instead of fields:
type Person() =
member val Id = Unchecked.defaultof<int> with get, set
member val Name = Unchecked.defaultof<String> with get, set
You can use an F # query expression to query EF, for example:
let person =
query {
for p in context.Persons do
where (p.Id = personId)
select p }
|> Seq.head
(You may have to play around with the above to get it to work, but that should be the gist of it.)
source to share