Build a DataTable from scratch using F# - f#

Is it possible to build a DataTable object from scratch using F#
I have written this code
module DataHelper
open System
open System.Data
open System.Data.SqlClient
let addDataRow (dt : DataTable) kerberos =
let dr = dt.NewRow()
dr["Kerberos"] = kerberos
dt.Rows.Add(dr)
let Func userList : string seq =
let dt : DataTable = new DataTable("UserNameListType")
let dc : DataColumn = new DataColumn("Kerberos")
dt.Columns.Add(dc)
Seq.iter (fun user -> addDataRow dt user) userList
dt
But this has too many errors
VS.NET does not seem to understand DataTable, DataRow classes and only shows me a "Note" as intellisense.
Its hard to use the collection objects Rows, Columns in F# because none of the methods really work (which work easily in C#).

If you're doing this in a new project, you need to add references to System.Data.dll and System.Xml.dll. After that, Visual Studio should recognize the types. You can do that by right clicking on "References" in your project and choosing "Add Reference".
Aside from that, there are two minor mistakes in your code. The assignment should be written as (note that there is a dot before [ and the operator is <- instead of =):
dr.["Kerberos"] <- kerberos
And your Func function should return DataTable:
let Func userList : DataTable =

Related

How do I get an F# fsx script to re-execute and re-pull SQL data each time it's called from C#?

I have written a simple C# web app that allows the user to input some data and then upon button click:
the data is saved to a local SQL db
an F# script is called to retrieve that data using SqlCommandProvider
that data is passed back to C#
the passed back data is used in some calculations
the calc results are displayed onto the screen of the web page
Everything works fine except that when the button is clicked more than once, the same data is sent back from F# from the very first execution.
It appears that the script is not re-executing as would be expected, but if it is, it seems that the SqlCommandProvider might be locked to the first set of results that it initially returned.
It is obviously unnecessary to send the data to the db and back in order to perform these calcs. This app is being built for demonstration purposes of F#/C# usage together in a solution, not actual efficient production usage of the app.
#I "../packages/FSharp.Data.3.0.0/lib/net45"
#r "FSharp.Data.dll"
open FSharp.Data
#I "../packages/FSharp.Data.SqlClient.2.0.1/lib/net40"
#r "FSharp.Data.SqlClient.dll"
open FSharp.Data.SqlClient
[<Literal>]
let ConnectionString =
#"server=(local); database=CostPriceCalc; user id=MyId; password=MyPassword;"
[<Literal>]
let SqlQuery = "SELECT SharesSold, PricePerShare, SellDate, CostMethod FROM [CalcInputs] WHERE Id = (SELECT MAX(Id) FROM [CalcInputs])"
let cmd = new SqlCommandProvider<SqlQuery, ConnectionString>(ConnectionString)
let result = cmd.Execute() |> Seq.toArray
Additionally, I am not yet handling the data properly coming back from F#, instead I'm very inelegantly just converting to string and parsing through it to get what I need. That however, seems irrelevant (other than it's massively incorrect) because the C# variables are clear each time before pulling from F# and the same data just keeps coming back each time from F#.
public static List<NewData.Values> ParseFsharpData()
{
var parsedList = new List<NewData.Values>();
foreach (var item in CalculateCostPrice.result)
{
var parsedData = item.ToString().Replace(";", ",").
Replace("{ ", "").Replace(" }", "").Replace("Some ", "").
Replace("M,", ",").Replace("\"", "").
Split(',').ToList();
parsedList = (from value in parsedData
select new NewData.Values
{
Value1 = value.Split('=')[0].Trim(),
Value2 = value.Split('=')[1].Trim()
}).ToList();
}
return parsedList;
}
Lastly, I have confirmed that the new data IS being written correctly to the db. The issue seems confined to either the F# fsx script itself (named CalculateCostPrice) not re-executing as expected, OR the SqlCommandProvider caching the data.
I haven't tried to compile .fsx scripts yet, but my experience with using modules in F# projects makes me think that:
let result = cmd.Execute() |> Seq.toArray
compiles to a static variable on the CalculateCostPrice class. This would mean it'll only get executed once (when it's first used, if not earlier), and the result would be stored in the "result" variable.
Adding a parameter of type "unit" should change it to a method (again, not tested yet):
let result() = cmd.Execute() |> Seq.toArray
And you would call it from C# as:
foreach (var item in CalculateCostPrice.result())
In this case, the execution will happen when you call the method, not when the class gets initialized. I might rename it from "result" to "executeQuery" or something along those lines.

FS2024 Static linking error when PCL project use by TypeProvider

It's trying to make a TypeProvider for Xamarin.Forms, but has been plagued by FS2024 error.
Parse own library from the XAML of Xamarin.Forms
Assign x:Name to Propertis
`F#
type MainPage = Moonmile.XamarinFormsTypeProvider.XAML<"MainPage.xaml">
// made btn1 and text1 propertis
type MainPageEx(target:MainPage) =
let mutable count = 0
do
// When set event to btn.Clicked, happen FS2024 error.
// If this event is comment out, it success build.
target.btn1.Clicked.Add( fun e ->
count <- count + 1
target.btn1.Text <- "Clicked " + count.ToString())
// Property is success
member this.CurrentPage
with get() = target.CurrentPage
When you are referring to a property, build & operation you can normally.
But the internal class of Xamarin.Forms like Button.Clicked, If you try to access to, it is the build error.
Sample code for error
https://github.com/moonmile/SimpleEventTypeProvider
Making code for XamarinFormsTypeProvider
github.com/moonmile/XamarinFormsTypeProvider
Maybe, I suspect inconsistencies and is happening in the part of the generation of a Native TypeProvider and Xamrin.Forms.Core a PCL.
F# Compiler for F# 3.1 (Open Source Edition)
Freely distributed under the Apache 2.0 Open Source License my error!!!
isMscorlib: true
name: "System.Runtime"
PrimaryAssembly.DotNetCore.Name: "System.Runtime"
PrimaryAssembly.Mscorlib.Name: "mscorlib"
parameter error FS2024: Static linking may not use assembly that targets different profile.
It's to operate the property they work properly, and to MVVM perhaps.
Butt I am trying to implement a way to be assigned to Button.Clicked events
as shown in the codebehide-like buildings if possible.
Would there workaround or what?
In the case of XAML in WPF, How can such seems to work well.
github.com/fsprojects/FsXaml
This answer isn't guaranteed to be correct, but it should help at least point you in the right direction.
The first thing to do is to make sure that you have installed the latest Visual F# Tools Build, as this adds the FSharp.Core that is compatible with the PCL profiles (You can find it here: (https://visualfsharp.codeplex.com/). Once that is installed, you will want to reference either the Profile78, or Profile259 FSharp.Core.dll (On my machine, these are found at: "C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp.NETPortable\2.3.5.0", and "C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp.NETPortable\2.3.5.1" respectively).
Once you have that installed, the next thing to do is make sure that your PCL projects have the following in their project files (This tells MSBuild / xBuild that the projects are PCL libraries, and that they are F# Projects):
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{F2A71F9B-5D33-465A-A702-920D77279786}</ProjectTypeGuids>
Once that is done, you will need to select either Profile78, or Profile259 (I would recommend 78, as the current Xamarin.Forms nuget package doesn't support 259).
Once that is done, then you should be able to build and run and it should get rid of the error.
Thank you for my question.
Meybe,
When it build TypeProvider, F# compiler use classes in mscorlib.
When it resolve the type of btn1.Clicked event, the F# comiler use type in System.Runtime.
I think for that, and it can not be resolved at build time
Try, if you attach the Clicked Event using reflection, it has moved successfully on Android build through.
I seem, if it I use only shard classes in mscorlib and System.Rutime, I can build no FS2024 error.
type MainPage = Moonmile.XamarinFormsTypeProvider.XAML<"MainPage.xaml">
type MainPageEx() as this =
inherit BindObject<MainPage>(new MainPage())
// Add handlder by reflection
let AddHandler(target:obj, eventName:string, eventMethod: obj*obj -> unit ) =
let hdr = Action<obj,obj>( fun s e -> eventMethod(s,e))
let ei = target.GetType().GetRuntimeEvent(eventName)
let dt = ei.AddMethod.GetParameters().[0].ParameterType
let handler = new Action<obj,obj>(fun s e -> hdr.Invoke( s, new EventArgs() ))
let handlerInvoke = handler.GetType().GetRuntimeMethod("Invoke", [|typeof<obj>; typeof<Type[]>|])
let dele = handlerInvoke.CreateDelegate( dt, handler )
let add = new Func<Delegate, EventRegistrationToken> ( fun t ->
let para = ei.AddMethod.GetParameters()
let ret = ei.AddMethod.Invoke( target, [|t|])
if ret <> null then
ret :?> EventRegistrationToken
else
new EventRegistrationToken()
)
let remove = new Action<EventRegistrationToken>( fun t -> ei.RemoveMethod.Invoke(target, [|t|]) |> ignore )
// WindowsRuntimeMarshal.AddEventHandler<Delegate>(add, remove, dele)
add.Invoke( dele ) |> ignore
()
let mutable count = 0
do
(* // build error
target.btn1.Clicked.Add( fun e ->
count <- count + 1
target.btn1.Text <- "Clicked " + count.ToString())
*)
// add handler by reflection
AddHandler( base.Target.btn1, "Clicked", this.ButtonClick )
()
member this.CurrentPage
with get() = this.Target.CurrentPage
member this.ButtonClick(s,e) =
count <- count + 1
base.Target.text1.Text <- "clicked " + count.ToString()

F# PCL and System.IO.FileStream

I am writing a PCL using F# and I am trying to write something to disk. All of the PCL examples are in VB.NET and C# and those examples don't work in F#. Specifically, I have this code:
type FileSystemStockProvider(filePath:string) =
member this.PutData(stockData) =
let serializedData = stockData
|> Seq.map(fun row -> JsonConvert.SerializeObject(row))
let outFile = new System.IO.StreamWriter(filePath)
outFile.Write(serializedData)
The problem is that the System.IO.StreamWriter in F# does not have an overload to disk so it does not compile. Does anyone have a suggestion on how to write to disk using a PCL?
Thanks in advance
Confirmed, the overload you were looking for is missing. However, I don't think this is a mistake, as it is missing from the .net framework for C# and F#. It's not F# specific. The reason I expect is because of the sand boxing requirement on different mobile and cross platform devices. So, the Windows / Linux path name which we are used to doesn't apply universally any more.
See https://pclstorage.codeplex.com/. Use Nuget to retrieve
To reduce the number of steps in getFileStream, probably there is some room to do a little string parsing in a c:\ or a \\networkPath, or even a url based aRootWithAFewReservedWords://the/path/to/the/file.json kind of way.
btw: there was a problem with disposing of the file stream in your code. You need to be explicit and use one of the use / using / dispose techniques. Are you definitely not seeing the overload?
namespace PortableLibrary1
open Newtonsoft.Json
[<JsonObject>]
type TestType() =
[<JsonProperty>]
member val Property2 = "testData" with get, set
type FileSystemStockProvider(filePath:string) =
//let getFileStream folder file =
// // for windows / linux?
// new System.IO.StreamWriter("c:\\" + folder + "\\" + filePath)
let getFileStream folder file =
let rootFolder = PCLStorage.FileSystem.Current.LocalStorage
let folder = rootFolder.CreateFolderAsync(folder,PCLStorage.CreationCollisionOption.OpenIfExists).Result
let file = folder.CreateFileAsync(file, PCLStorage.CreationCollisionOption.ReplaceExisting).Result
file.OpenAsync(PCLStorage.FileAccess.ReadAndWrite).Result
member __.PutData(stockData) =
let fs = getFileStream "theFolder" "theFile.json"
use outFile = new System.IO.StreamWriter(fs)
stockData |> Seq.iter (JsonConvert.SerializeObject >> outFile.Write)

Build a Linq expression from an F# function, to pass to C#

I am trying use the Lex.Db database in an F# project in a WinRT 8.1 app.
I am following this tutorial for C#. I've successfuly added a reference to Lex.Db to an F# project and the simple calls in the tutorial translate to f# and compile (eg. let db = new DbInstance("demo")).
The problem is this C# code:
db.Map<Contact>().Key(i => i.Id)
-- Edit --
To save others from reading further, in F# 3.0 this is almost a non-problem. See kvb's comment below. The solution is:
db.Map<Contact>().Key(fun i => i.Id)
-- /Edit ---
The Key function expects an Expression<Func<Contact,'a>> and I am unable to build this in F#.
A very similar question appears in How to pass LinQ Expressions from F# to C# code. The recommended solution is to use LeafExpressionConverter.QuotationToLambdaExpression.
I have tried this as follows:
type Contact() =
member val Id = 0 with get, set
member val FirstName = "" with get, set
let db = new DbInstance("Demo")
let idExpr = LeafExpressionConverter.QuotationToLambdaExpression
<# fun (c : Contact) -> c.Id #>
db.Map<Contact>().Key(idExpr) |> ignore // <- Error
This produces a compiler error on idExpr:
Type mismatch. Expecting a Expression<Func<Contact,'a>> but given a Expression<(Contact -> int)>. The type 'Func<Contact,'a>' does not match the type 'Contact -> int'.
This question: Expression<Func<T, bool>> from a F# func seems to address the problem directly, but the solution uses Microsoft.FSharp.Linq.QuotationEvaluation, which I can't find in F# 3.0 for WinRT.
How do I turn <# fun (c : Contact) -> c.Id #> into an Expression<Func<Contact,'a>>?
Microsoft.FSharp.Linq.QuotationEvaluation is in PowerPack, but as of v3.0 the functionality it provides is available in Core via LeafExpressionConverter. You can use the code in the question you linked, but change it to use LeafExpressionConverter for the translation part.
open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
let toLinq (expr : Expr<'a -> 'b>) =
let linq = LeafExpressionConverter.QuotationToExpression expr
let call = linq :?> MethodCallExpression
let lambda = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters)

The field, constructor or member 'AsyncReadToEnd' is not defined error

I am learning RX (Reactive Extensions), and have found someone posted some code nearly one year ago using F# and RX to make a simple webCrawler. I tried to see if I can re-use the code. I download RX, and create a F# windows application, add reference to System.Reactive. My IDE is VS 2010 Ultimate, RX version is: 1.1.11111. The following is the code:
#light
open System
open System.Linq
open System.Collections.Generic
open System.Net
open System.IO
open System.Threading
open System.Text.RegularExpressions
open System.Reactive
open System.Reactive.Linq
let create f =
Observable.Create<_>(fun x ->
f x
new System.Action((fun () -> ())))
let ofAsync async =
create (fun obs -> Async.StartWithContinuations(async, obs.OnNext,obs.OnError,obs.OnError))
let fromEvent (event:IEvent<_,_>) = create (fun x -> event.Add x.OnNext)
let tickEvent = new Event<unit> ()
let tickEventObs = tickEvent.Publish |> fromEvent
let fetch(url:string) =
async { let req = WebRequest.Create(url)
let! resp = req.AsyncGetResponse()
let stream = resp.GetResponseStream()
let reader = new StreamReader(stream)
let! html = reader.AsyncReadToEnd()
return html
} |> ofAsync
But the code can not get compiled, I got the error message:
Error 1 The field, constructor or member 'AsyncReadToEnd' is not defined
So the error was on this line:
let! html = reader.AsyncReadToEnd()
I guess there could be some changes for the past one year in RX or F#.
For my current environment, what is the correct way to re-write the above code?
Thanks and happy new year to you all!
John
AsyncReadToEnd() extension method of StreamReader is part of FSharpPowerPack now. Install FSharpPowerPack from this link, if not yet, then add reference to FSharp.PowerPack to your project. This should make AsyncReadToEnd()method accessible from the rest of your code.
Use the dedicated AsyncStreamReader type
AsyncReadToEnd() extension method does not exists anymore in the FSharp.PowerPack.
It has been replaced with the AsyncStreamReader dedicated type that contains proper asynchronous implementation of stream reading (like ReadToEnd, ReadLine, etc.)
It can be used like that:
async {
use asyncReader = new AsyncStreamReader(stream)
return! asyncReader.ReadToEnd() }
Note: Once you have installed FSharp.PowerPack, the AsyncStreamReader type will be 'injected' in the Microsoft.FSharp.Control namespace
Other related answer: https://stackoverflow.com/a/7925440/1480391

Resources