I've been revisiting one of my old experiments. The code below is a file I added to Daniel Mohl's F# MVC5 project which I created in Visual Studio 2015. It compiled and worked in VS2015 (and still does) but when I try to compile it in VS2019 I get an error message on the |> this.View lines towards the end: "FS0405: A protected member is called or 'base' is being used. This is only allowed in the direct implementation of members since they could escape their object scope". Does anyone have any idea what I need to do to get rid of the error
namespace fsmvcproject.Models
open System
open System.ComponentModel.DataAnnotations
type Newarticle() =
[<Key>]member val Id = 0 with get, set
member val Headline = "" with get, set
member val Author = "" with get, set
member val Publication = "" with get, set
member val Intro = "" with get, set
member val Story = "" with get, set
namespace fsmvcproject.Repositories
open System.Data.Entity
open fsmvcproject.Models
open System.Collections.Generic
type SGdbEntities() =
inherit DbContext("Data Source=127.0.0.1\SQLEXPRESS;Persist Security Info=True;Initial Catalog=SG;User ID=xxxx;Password=xxxx")
[<DefaultValue()>] val mutable newarticles : IDbSet<Newarticle>
member x.Newarticles with get() = x.newarticles and set v = x.newarticles <- v
type NewarticlesRepository() =
member x.GetAll () =
use context = new SGdbEntities()
query { for a in context.Newarticles do
sortByDescending a.Id
select a }
|> Seq.toList
member x.GetDetail (id) =
use context = new SGdbEntities()
query { for a in context.Newarticles do
where (a.Id = id)
select a }
|> Seq.toList
namespace fsmvcproject.Controllers
open fsmvcproject.Repositories
open System.Web.Mvc
[<HandleError>]
type ArticlesController(repository :NewarticlesRepository) =
inherit Controller()
new() = new ArticlesController(NewarticlesRepository())
member this.Index () =
repository.GetAll()
|> this.View
member this.Detail (id) =
repository.GetDetail(id)
|> this.View
I think the problem here is that View is a protected method, which means that you can call it directly from your derived class, but you can't treat it like a first-class F# function.
Thus, to fix the compiler error, try changing model |> this.View to this.View(model).
Related
I wrote a wrapper around List. I expect the internal list to keep state but it doesn't. What am I doing wrong? The methods are definitely executed but the internal list is always empty.
open System
open System.Collections.Generic
open NUnit.Framework
type MyList() =
member this.List = List<char>()
member this.AddX =
printfn "AddX called"
this.List.Add('X')
member this.CountX: int =
printfn "CountX called"
this.List.Count
[<Test>]
let TestX () =
let mylist = MyList()
mylist.AddX
mylist.AddX
Assert.AreEqual(2, mylist.CountX)
Tried putting a mutable keyword in different places (no success)
The problem is that every time you call the List member of MyList, it creates a new list, so the class isn't keeping internal state the way you want. (You can verify this by adding a printfn statement to the List method.)
To fix this problem, change the List member to be a value, which is initialized only once per class instance:
type MyList() =
member val List = List<char>()
...
Alternatively, you can use a let-bound value instead:
type MyList() =
let list = List<char>()
member this.AddX = list.Add('X')
member this.CountX = list.Count
I am trying to use F# with .NET Core 2.1 to create a simple CRUD application but none of the controllers don't get registered. I see none of the controllers in Swagger and the controller themselves don't start.
I appreciate any help or hint.
Startup.fs
namespace SimpleCms
type Startup private () =
let mutable configuration : IConfigurationRoot = null
member this.Configuration
with get () = configuration
and private set (value) = configuration <- value
new(env : IHostingEnvironment) as this =
Startup()
then
let builder =
ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional = false, reloadOnChange = true)
.AddJsonFile(sprintf "appsettings.%s.json" env.EnvironmentName, optional = true)
.AddEnvironmentVariables()
this.Configuration <- builder.Build()
// This method gets called by the runtime. Use this method to add services to the container.
member this.ConfigureServices(services : IServiceCollection) =
services.AddLogging() |> ignore
services.AddSwaggerGen(fun x ->
x.SwaggerDoc("v1", new Info(Title = "SimpleCms", Version = "v1")) |> ignore)
|> ignore
services.AddMvcCore() |> ignore
services.AddMvc() |> ignore
let container =
new Container(fun opt ->
opt.Scan(fun x ->
x.AssemblyContainingType(typeof<Startup>)
x.Assembly("Dal")
x.Assembly("Logic")
x.WithDefaultConventions() |> ignore)
opt.For<LiteDatabase>().Use(new LiteDatabase("Filename=database.db")) |> ignore
opt.Populate(services) |> ignore)
container.GetInstance<IServiceProvider>()
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
member this.Configure(app : IApplicationBuilder, env : IHostingEnvironment) =
app.UseSwagger() |> ignore
app.UseSwaggerUI(fun x ->
x.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1") |> ignore) |> ignore
app.UseMvc(fun x ->
x.MapRoute("default", "{controller=Home}/{action=Index}") |> ignore) |> ignore
HomeController.fs
namespace SimpleCms.Controllers
open Microsoft.AspNetCore.Mvc
[<Route("")>]
type HomeController() =
inherit Controller()
[<Route("")>]
[<HttpGet>]
member this.Index() =
Ok("Hello World!")
The complete code
Repo URL
Your problem is the IPostController interface. Remove that and Swagger will pick up the PostController.
For example:
[<Route("api/[controller]")>]
[<ApiController>]
type PostController(logic : IPostLogic) =
inherit Controller()
member this.logic = logic
[<Route("")>]
[<HttpGet>]
member this.GetAll() =
this.logic.GetAll()
//etc
Which makes Swagger show this:
Side Note: For ASP.NET Core 2.1 and above, you shouldn't be specifying the version of the Microsoft.AspNetCore.All package, your fsproj file should contain this instead:
<PackageReference Include="Microsoft.AspNetCore.All" />
Which in turn means you should be using version 2.1.1 of Microsoft.Extensions.Logging.Debug.
Finally, you are using a really old version of Swashbuckle.AspNetCore. I suggest you upgrade that too.
For a project I am working on I need a global variable(technically I don't, I could build it and then pass it to every single function call, and let every single function call know about it, but that seems just as hacky, less readable and more work.)
The global variables are look up tables(endgame, opening book and transpositions/cache) for a game.
The fact that some of the code may lose some of it's indempotent behavior is actually the point(speedups) in short, yes I know global mutable state is bad, it's really worth it in this case(10x+ performance improvement)
So here's the question, "build a singleton or use a static value in a static class with combinators"
They are effectively identical but I am curious what people have done before on this sort of problem
Or alternatively, should I be passing the thing around to everyone(or at least a reference to it anyways),is that really the best answer?
Here is a solution similar to the one posted by #Yin Zhu's, but using abstract types to specify a usage interface for the mutable value, a local definition to encapsulate it and object literals to provide an implementation (this is taken from Expert F#--which is co-authored by Don Syme):
type IPeekPoke =
abstract member Peek: unit -> int
abstract member Poke: int -> unit
let makeCounter initialState =
let state = ref initialState
{ new IPeekPoke with
member x.Poke(n) = state := !state + n
member x.Peek() = !state }
You can also do it with static fields, like this:
type Common() =
static let mutable queue : CloudQueue = null
static let mutable storageAccount : CloudStorageAccount = null
static member Queue
with get() = queue
and set v = queue <- v
static member StorageAccount
with get() = storageAccount
and set v = storageAccount <- v
In another module, just:
open Common
Common.Queue <- xxxx
here is the convention used in F# PowerPack Matrix library (\src\FSharp.PowerPackmath\associations.fs):
// put global variable in a special module
module GlobalAssociations =
// global variable ht
let ht =
let ht = new System.Collections.Generic.Dictionary<Type,obj>()
let optab =
[ typeof<float>, (Some(FloatNumerics :> INumeric<float>) :> obj);
typeof<int32>, (Some(Int32Numerics :> INumeric<int32>) :> obj);
...
typeof<bignum>, (Some(BigRationalNumerics :> INumeric<bignum>) :> obj); ]
List.iter (fun (ty,ops) -> ht.Add(ty,ops)) optab;
ht
// method to update ht
let Put (ty: System.Type, d : obj) =
// lock it before changing
lock ht (fun () ->
if ht.ContainsKey(ty) then invalidArg "ty" ("the type "+ty.Name+" already has a registered numeric association");
ht.Add(ty, d))
After a hiatus from my Silverlight / F# application, i am starting back into it and am running into an issue I cannot seem to get my head around. I have a member variable of my usercontrol that is a list ref, on button clicks I want to add records to it - but it never updates. I am pretty sure it has to do with being a member but I havent figured it out.
Thanks in advance to those who take the time to view and reply.
the problem lines:
.
.
.
member this.brokers = ref List.empty
.
.
.
// this line doesn't seem to work
this.brokers := candidate :: (!this.brokers)
.
.
.
the class:
type Page() as this =
inherit UriUserControl("/xyz;component/page.xaml", "page")
do
this.firm.ItemsSource <- RpcXYZ.getFirms()
this.email.Background <- SolidColorBrush(Colors.Red)
this.addBtn.IsEnabled <- false
()
// instance data
member this.brokers = ref List.empty
// bound controls for add candidate
member this.FE : FrameworkElement = (this.Content :?> FrameworkElement)
member this.fname : TextBox = this.FE ? fname
member this.lname : TextBox = this.FE ? lname
member this.email : TextBox = this.FE ? email
member this.firm : RadComboBox = this.FE ? firms
member this.addBtn : RadButton = this.FE ? addBtn
member this.addCadidate_Click (sender : obj) (args : RoutedEventArgs) =
let inline findFirm (f : RpcXYZ.firm) =
f.id = Int32.Parse(this.firm.SelectedValue.ToString())
let candidate : SalesRep = {
id = -1 ;
fname = this.fname.Text ;
lname = this.lname.Text ;
email = this.email.Text ;
phone = "" ;
firm = List.find findFirm <| RpcXYZ.getFirms();
score = None ;
}
// this line is fine t is a list of 1 item after 1 click
let t = candidate :: (!this.brokers)
// this line doesn't seem to work
this.brokers := candidate :: (!this.brokers)
ChildWindow().Show() |> ignore ;
member this.email_Changed (o : obj) (arg : TextChangedEventArgs) =
let txtBox = (o :?> TextBox)
let emailRegex = Regex("(\w[-._\w]*\w#\w[-._\w]*\w\.\w{2,3})")
if emailRegex.IsMatch(txtBox.Text) = false then
txtBox.Background <- SolidColorBrush(Colors.Red)
this.addBtn.IsEnabled <- false
else
txtBox.Background <- new SolidColorBrush(Colors.White)
this.addBtn.IsEnabled <- true
This
member this.brokers = ref List.Empty
defines a property getter. Every time you touch .brokers, it re-runs the code on the right hand side. That's the issue.
The fix would be to define an instance variable, and return that:
let brokers = ref List.Empty
member this.Brokers = brokers
Then a single ref is allocated when an instance of the class is constructed, and you keep accessing that same ref object via the member property.
Brian already explained the problem. However, is there any reason why you're not using a mutable member (with getter and setter) and instead use a readonly member that returns a reference cell?
Using a get/set member would be more idiomatic solution:
let mutable brokers = List.Empty
member this.Brokers
with get() = brokers
and set(value) = brokers <- value
The declaration is a bit longer (unfortunately, there are no automatic properties in F#!), but the member will look like a standard property (from both F# and C#). You could then use it like this:
x.Brokers <- candidate :: x.Brokers
Although, you need the property only for public members that should be accessed from outside of the type. For private fields, you can just use the mutable value brokers directly...
What is the F# equivalent of the following C# code? Specifically, I need to check if an event is being handled.
protected virtual void OnClicked(ClickEventArgs e) {
if (this.Clicked != null) //how can I perform this check in F#
this.Clicked(this, e);
}
Okay, I think I figured this thing out. Taking a cue from Don Syme's blog, specifically the section "The Implementation of the IEvent Module."
Instead of the following:
let validationFailedEvent = new Event<DataValidationEventHandler, DataValidationEventArgs>()
I had to implement IEvent myself and create a variable to hold the invocation list:
let mutable listeners: Delegate = null
let validationFailedEvent = { new IEvent<DataValidationEventHandler, DataValidationEventArgs> with
member x.AddHandler(d) =
listeners <- Delegate.Combine(listeners, d)
member x.RemoveHandler(d) =
listeners <- Delegate.Remove(listeners, d)
member x.Subscribe(observer) =
let h = new Handler<_>(fun sender args -> observer.OnNext(args))
(x :?> IEvent<_,_>).AddHandler(h)
{ new System.IDisposable with
member x.Dispose() = (x :?> IEvent<_,_>).RemoveHandler(h) } }
Then, to check if there are listeners, and, if not, raise an exception:
member private x.fireValidationFailedEvent(e:DataValidationEventArgs) =
match listeners with
| null -> failwith "No listeners"
| d -> d.DynamicInvoke([| box x; box e |])
An alternative way to implement RequiresSubscriptionEvent is to build on top of the existing Event functionality (using composition) and just add a counter that counts the number of registered handlers and add a property HasListeners (or even publish the number of listeners if you wanted...)
This makes the code a bit easier to use and hopefuly also safer, because if you don't check whether it has any listneres, it will still work as the usual F# code. And if you want to perform the check, you can...
type RequiresSubscriptionEvent<_>() =
let evt = new Event<_>()
let mutable counter = 0
let published =
{ new IEvent<_> with
member x.AddHandler(h) =
evt.Publish.AddHandler(h)
counter <- counter + 1;
member x.RemoveHandler(h) =
evt.Publish.RemoveHandler(h)
counter <- counter - 1;
member x.Subscribe(s) =
let h = new Handler<_>(fun _ -> s.OnNext)
x.AddHandler(h)
{ new System.IDisposable with
member y.Dispose() = x.RemoveHandler(h) } }
member x.Trigger(v) = evt.Trigger(v)
member x.Publish = published
member x.HasListeners = counter > 0
Sample usage:
type Demo() =
let evt = new RequiresSubscriptionEvent<_>()
[<CLIEvent>]
member x.OnSomething = evt.Publish
member x.FooThatFiresSomething() =
if evt.HasListeners then
evt.Trigger("foo!")
else
printfn "No handlers!"
Even though this isn't a part of standard F# libraries, it shows the great advantage of F# first class events. If there is some missing functionality, you can simply implement it yourself!
Typically, you don't need to do that check in F# (the event infrastructure checks for you):
type T() =
let ev = new Event<_>()
[<CLIEvent>]
member x.Event = ev.Publish
member x.OnClicked() =
ev.Trigger()
I followed kvb's suggestion and put this logic in a class. I copied Event from the F# sources and added a Handled property, which checks if the Delegate is null. I tried adding to, then removing handlers from the event to make sure it gets set back to null, and indeed it does.
type EventEx<'Delegate,'Args when 'Delegate : delegate<'Args,unit> and 'Delegate :> System.Delegate >() =
let mutable multicast : System.Delegate = null
static let argTypes =
let instanceBindingFlags = BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
let mi = typeof<'Delegate>.GetMethod("Invoke",instanceBindingFlags)
mi.GetParameters() |> (fun arr -> arr.[1..]) |> Array.map (fun p -> p.ParameterType)
member x.Handled = (multicast <> null)
member x.Trigger(sender:obj,args:'Args) =
match multicast with
| null -> ()
| d ->
if argTypes.Length = 1 then
d.DynamicInvoke([| sender; box args |]) |> ignore
else
d.DynamicInvoke(Array.append [| sender |] (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(box args))) |> ignore
member x.Publish =
{ new IEvent<'Delegate,'Args> with
member x.AddHandler(d) =
multicast <- System.Delegate.Combine(multicast, d)
member x.RemoveHandler(d) =
multicast <- System.Delegate.Remove(multicast, d)
member e.Subscribe(observer) =
let h = new Handler<_>(fun sender args -> observer.OnNext(args))
(e :?> IEvent<_,_>).AddHandler(h)
{ new System.IDisposable with
member x.Dispose() = (e :?> IEvent<_,_>).RemoveHandler(h) } }
This article here http://geekswithblogs.net/Erik/archive/2008/05/22/122302.aspx says you do not need to check for null events in F#, though I don't know what his reference is.
This article http://blogs.msdn.com/dsyme/articles/FSharpCompositionalEvents.aspx by Don Symes goes into F# events in quite a bit of detail. It looks like events are not owned by the class in F#
From the above,
it is that events are now first-class
values in the F# langauge. Indeed,
events are not a separate notion at
all in the language design, rather,
events are just values of type
Microsoft.FSharp.Idioms.IEvent<_>, and
.NET events are effectively just
properties of this type.
And
One of the restrictions of C# is that
events can only exist as members
within classes. With the F# model,
new event values can be created just
as values as part of any expression.