F# Async - An item with the same key has already been added - asynchronous

I am trying some async ops in f# but w/o much luck. I am trying to grab records from the db and perform operations on each record in Parallel.
let IsA1 companyId =
query { for comp in db.Company do
join cc in db.CC on (comp.CompanyId = int(cc.CompanyId))
join pp in db.PP on (cc.PartId = pp.PartId)
join tl in db.TL on (pp.CompanyId = tl.CompanyId)
where (comp.CompanyId = companyId)
select (comp.CompanyId > 0)
}
|> Seq.length |> fun len -> len > 0
let IsA2 companyId =
query { for t in db.Title do
join pp in db.PP on (t.Tid = pp.Tid)
join comp in db.Company on (pp.CompanyId = comp.CompanyId)
where (comp.CompanyId = companyId)
select (comp.CompanyId > 0)
}
|> Seq.length |> fun len -> len > 0
let GetAffiliations id =
async {
if (IsA1 id) then return "AffilBBB"
elif (IsA2 id) then return "AffilCCD"
else return Unknown
}
let ProcessCompany (company:dbSchema.ServiceTypes.Company) =
async {
let grp = GetAffiliations company.CompanyId
let result = { Id=company.CompanyId; Name=company.Name; Affiliations=grp; ContactType="ok"; }
return result
}
let GetCompanyNames =
let companies = db.Company |> Seq.distinctBy(fun d -> d.CompanyId)
companies
|> Seq.map(fun co -> ProcessCompany co)
|> Async.Parallel
|> Async.RunSynchronously
When I run the above code, I get error:
System.ArgumentException: An item with the same key has already been added.
The error is occurring as a result of another function call inside async { }:
let grp = GetAffiliations company.CompanyId
I am sure its a newbie issue, but I am not sure what the issue is. I even tried making the call inside of the async{ } another async call and used let! grp = (GetAffiliations company.CompanyId) but that does not resolve.

Because the two concurrent queries are sharing the same context, when the second result is added to the same context, you get an error saying that the context already has an item with the same key.
Using distinct instances of the 'db' context for each of the queries, should solve your issue.

Related

CosmosDb search datetime with millisecond precision not working when ms is .0000000Z

Have the following test data in CosmosDB.
{
"LastSuccessfulDeployment": "2022-10-08T01:30:30.0000000Z",
},
{
"LastSuccessfulDeployment": "2022-10-08T01:30:30.3816486Z",
}
Searching '2022-10-08T01:30:30.0000000Z' returns no records whereas when searching for '2022-10-08T01:30:30.3816486Z' records are being returned.
protected override IQueryable<Component> ApplyFiltersOnQueryInternal(IQueryable<Component> query, IFilter<Component> filter)
{
if (filter == null)
return query;
var componentFilter = (filter as ComponentFilter)!;
if (componentFilter.LastSuccessfulDeployment.HasValue)
query = query.Where(x => x.LastSuccessfulDeployment == componentFilter.LastSuccessfulDeployment);
return query
.Skip((componentFilter.CurrentPage - 1) * componentFilter.PageSize)
.Take(componentFilter.PageSize);
}
EntityQueryable DebugView:
-- #__componentFilter_LastSuccessfulDeployment_0='08/10/2022 01:30:30'
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Component") AND (c["LastSuccessfulDeployment"] = #__componentFilter_LastSuccessfulDeployment_0))
-- #__componentFilter_LastSuccessfulDeployment_0='12/10/2022 15:18:14'
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Component") AND (c["LastSuccessfulDeployment"] = #__componentFilter_LastSuccessfulDeployment_0))
```Code

Get value of optional types when retrieving value of record fields in F#

I have a type defined as follows:
type Employee = {
Id: Guid
Name: string
Phone: string
Email: Option<string>
}
and an instance of this type:
let emp = {
Id = Guid "bc07e94c-b376-45a2-928b-508b888802c9"
Name = "A"
Phone = "B"
Email = Some "E"
}
I want to extract the field names and values from this record type using reflection like the following:
let getFieldValueMappingOfARecordType (data: 'T) : seq<string * obj> =
let fieldValueMapping =
data.GetType()
|> FSharpType.GetRecordFields
|> Seq.map (
fun propertyInfo ->
(propertyInfo.Name, data |> propertyInfo.GetValue)
)
fieldValueMapping
Then invoking the above function with the instance of employee type
let mapping = getFieldValueMappingOfARecordType emp
|> Seq.toList
gives us:
val mapping : (string * obj) list =
[("Id", bc07e94c-b376-45a2-928b-508b888802c9); ("Name", "A"); ("Phone", "B");
("Email", Some "E")]
So far it's working well with non-optional type. But in case of optional types, it's returning the value of the field as either Some value or None. What I would like to do is to get the value when the field has Some value or make it null when it's None.
Essentially like the follwing:
val mapping : (string * obj) list =
[("Id", bc07e94c-b376-45a2-928b-508b888802c9); ("Name", "A"); ("Phone", "B");
("Email", "E")]
Or if the employee instance is like the following:
let emp = {
Id = Guid "bc07e94c-b376-45a2-928b-508b888802c9"
Name = "A"
Phone = "B"
Email = None
}
Then,
val mapping : (string * obj) list =
[("Id", bc07e94c-b376-45a2-928b-508b888802c9); ("Name", "A"); ("Phone", "B");
("Email", null)]
This is what I have so far (non-working code):
open System
open Microsoft.FSharp.Reflection
open System.Reflection
type Employee = {
Id: Guid
Name: string
Phone: string
Email: Option<string>
}
let emp = {
Id = Guid "bc07e94c-b376-45a2-928b-508b888802c9"
Name = "A"
Phone = "B"
Email = Some "E"
}
let getSomeOrNull (t: Type) (o: obj) =
let opt = typedefof<option<_>>.MakeGenericType [| t |]
match (o :?> opt) with
| Some s ->
s
| None ->
null
let getValues (data: 'T) =
let values =
data.GetType()
|> FSharpType.GetRecordFields
|> Array.map (
fun propertyInfo ->
let value =
data |> propertyInfo.GetValue
let isOption =
propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() = typedefof<Option<_>>
match isOption with
| true ->
(propertyInfo.Name, (getSomeOrNull propertyInfo.PropertyType value))
| false ->
(propertyInfo.Name, value)
)
values
getValues emp
|> printfn "%A"
I think the only way to do this is with reflection:
let getSomeOrNull (t: Type) (o: obj) =
if isNull o then null
else t.GetProperty("Value").GetValue(o)
I think this should do the trick:
let getSomeOrNull (o: obj) =
match o with
| :? Option<string> as o -> a |> Option.toObj > box
| _ -> null

Setting Picker ItemsSource results in "Object must implement IConvertible."

I can't figure out why I'm receiving the following error when setting the ItemsSource property on a picker control:
"Object must implement IConvertible."
XAML
My XAML is as follows:
** Error: "Object must implement IConvertible." **
<Picker ItemsSource="{Binding Roles}" />
What's interesting, is that I can set the ItemsSource property for a listview control with no issues in the same file.
Different control but same ItemsSource binding in same XAML file:
** This works! **
<ListView ItemsSource="{Binding Roles}" />
In conclusion, why do I only receive an exception when setting the ItemsSource for a Picker but not for a ListView control?
Appendix:
//--------------------------------
// Initializing viewmodel instance
//--------------------------------
var viewmodel = new Dashboard(user, _account.Query);
var page = new DashboardPage() { BindingContext = viewmodel };
await viewmodel.LoadAsync();
.
.
.
//---------------------
// Viewmodel definition
//---------------------
type Dashboard(user:AuthenticatedUser, query:Query) =
inherit ViewModelBase()
let mutable name = sprintf "%s %s" user.FirstName user.LastName
let mutable primaryRole = user.Role
let mutable roles = seq []
let mutable role = ""
let mutable positions = seq []
let mutable position = ""
let mutable incidents = seq []
let mutable incident = ""
let mutable accountabilities = seq []
let mutable accountability = ""
member x.Name with get() = name
and set(v) = name <- v
base.NotifyPropertyChanged(<# x.Name #>)
member x.PrimaryRole with get() = primaryRole
and set(v) = primaryRole <- v
base.NotifyPropertyChanged(<# x.PrimaryRole #>)
member x.Roles with get() = roles
and set(v) = roles <- v
base.NotifyPropertyChanged(<# x.Roles #>)
member x.Role with get() = role
and set(v) = role <- v
base.NotifyPropertyChanged(<# x.Role #>)
member x.Positions with get() = positions
and set(v) = positions <- v
base.NotifyPropertyChanged(<# x.Positions #>)
member x.Position with get() = position
and set(v) = position <- v
base.NotifyPropertyChanged(<# x.Position #>)
member x.Incidents with get() = incidents
and set(v) = incidents <- v
base.NotifyPropertyChanged(<# x.Incidents #>)
member x.Incident with get() = incident
and set(v) = incident <- v
base.NotifyPropertyChanged(<# x.Incident #>)
member x.Accountabilities with get() = accountabilities
and set(v) = accountabilities <- v
base.NotifyPropertyChanged(<# x.Accountabilities #>)
member x.Accountability with get() = accountability
and set(v) = accountability <- v
base.NotifyPropertyChanged(<# x.Accountability #>)
member x.LoadAsync() =
async {
do! async {
match! user |> query.Roles with
| Error _ -> failwith "Query for 'roles' failed"
| Ok result -> x.Roles <- result
}
do! async {
match! user |> query.Positions with
| Error _ -> failwith "Query for 'positions' failed"
| Ok result -> x.Positions <- result
}
do! async {
match! user |> query.Incidents with
| Error _ -> failwith "Query for 'incidents' failed"
| Ok result -> x.Incidents <- result
}
do! async {
match! user |> query.Accountabilities with
| Error _ -> failwith "Query for 'accountabilities' failed"
| Ok result -> x.Accountabilities <- result
}
} |> Async.StartAsTask
I had to use an ObservableCollection instead of IEnumerable (i.e. seq).
Note: I'm not sure why ListView worked without this.
let mutable roles = ObservableCollection<string>() // Updated
...
member x.LoadAsync() =
async {
do! async {
match! user |> query.Roles with
| Error _ -> failwith "Query for 'roles' failed"
| Ok result -> x.Roles <- ObservableCollection<string>(result) // Updated
}

FMDB Swift SQL Get Number of Rows in Table

FMDB seems to have lots of functions to deal with columns but none for rows. All I am trying to do is find the number of rows in a table.
func getTableRows() {
sharedInstance.database!.open()
let sqlStatement = "SELECT COUNT(*) FROM NAMESTABLE"
var resultSet: FMResultSet! = sharedInstance.database!.executeQuery(sqlStatement, withArgumentsInArray: nil)
if (resultSet != nil) {
let rowCount = XXXX
NSLog("Table Rows = %i",rowCount)
}
sharedInstance.database!.close()
}
I have tried various approaches with the XXXX but no joy. Am I going about this the wrong way? Many thanks.
try this:
func getTableRows() {
sharedInstance.database!.open()
let sqlStatement = "SELECT COUNT(field) FROM table_name"
var resultSet: FMResultSet! = sharedInstance.database!.executeQuery(sqlStatement, withArgumentsInArray: nil)
if (resultSet != nil) {
let rowCount = XXXX
NSLog("Table Rows = %i",rowCount)
}
sharedInstance.database!.close()
}

IO and parallel async in Fsharp

I have some computation intensive tasks, which are now only running on 1 core, so 1/8th of my machine capacity. At the end of each task, I write a log in a file.
What would be the most graceful way to handle this IO using parallel tasks ?
Having my write be itself async ?
Sending messages to an agent who'd process the write sequentially ?
[<Fact>]
let boom () =
let tasks = [1 .. 10]
|> Seq.map (fun components -> async { //do compute intensive stuff
use writer = new StreamWriter("samefile")
writer.WriteLine "toto" }
)
tasks |> Async.Parallel |> Async.RunSynchronously
Edit
I ended up doing this, and replacing the new Stream in my async to be code by synchronous call to the agent.
[<Fact>]
let pasBoom () =
let tasks = [2 .. 2 .. 17]
|> Seq.map (fun components -> async { //do compute intensive stuff
//use writer = new StreamWriter("samefile")
use writerhanlde = repoFileHandle.PostAndReply(fun replyChannel -> GetFile(#"samefile", replyChannel))
printfn "%A" (writerhanlde.getWriter().ToString())
writerhanlde.getWriter().WriteLine "toto" }
)
tasks |> Async.Parallel |> Async.RunSynchronously
and the agent (there might be bugs please be careful, I just need something quick myself)
type IDisposableWriter =
inherit IDisposable
abstract getWriter : unit -> StreamWriter
type StreamMessage = | GetFile of string * AsyncReplyChannel<IDisposableWriter>
let repoFileHandle =
let writerCount = new Dictionary<string, int>()
let writerRepo = new Dictionary<string, StreamWriter> ()
Agent.Start(fun inbox ->
async { while true do
let! msg = inbox.Receive()
match msg with
| GetFile(filename, reply) ->
if not (writerRepo.ContainsKey(filename)) then
writerRepo.[filename] <- new StreamWriter(filename,true)
writerCount.[filename] <- 0
writerCount.[filename] <- writerCount.[filename] + 1
let obj = {new IDisposableWriter with
member this.getWriter () = writerRepo.[filename]
member IDisposable.Dispose() =
writerCount.[filename] <- writerCount.[filename] - 1
if writerCount.[filename] = 0 then
writerRepo.[filename].Dispose()
writerRepo.Remove(filename) |> ignore
}
reply.Reply(obj) })
and to avoid concurrent write
type WriteToStreamMessage = | WriteToStream of string * string
let fileWriterAgent =
Agent.Start(fun inbox ->
async { while true do
let! msg = inbox.Receive()
match msg with
| WriteToStream(filename, content) ->
use writerhanlde = repoFileHandle.PostAndReply(fun replyChannel -> GetFile(filename, replyChannel))
writerhanlde.getWriter().WriteLine content
})
Can you change your computation to return the message to be logged instead of writing it to a file? Then you could use PSeq in PowerPack, which is a thin wrapper over TPL:
open Microsoft.FSharp.Collections
let work n = sprintf "running task %d" n
let msgs = PSeq.init 10 work |> PSeq.toList
use writer = System.IO.StreamWriter(#"C:\out.log")
msgs |> List.iter writer.WriteLine

Resources