Azure Blob- ja Table Storage
Käytetään edellä tehtyä WorkerRole-projektia. Käytämme myös Windows Azure Storage -kirjastoa. (Aiemmassa versiossa tätä ohjetta käytettiin Fog-kirjastoa, mutta se ei toiminut hyvin uusimpien Azure SDK:iden kanssa.)
Ensimmäisenä on lisättävä connectionstringit Azuren storageille. Tämä tapahtuu näin:
Avaa deployment-projektin ServiceDefinition.csdef ja lisää sinne settingit: TableStorageConnectionString ja BlobStorageConnectionString. Nämä voivat emulator-ympäristössä olla tyhjät.
Tiedostojen sisällössä attribuutti name="FSharpAzure"
viittaa solutionin nimeen ja schemaVersion="2013-10.2.2"
käytettyyn AzureSDK:iin, tässä 2.2, mutta 2.3 olisi schemaVersion="2014-01.2.3"
ja 2.4 olisi schemaVersion="2014-06.2.4"
.
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="FSharpAzure"
xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"
schemaVersion="2013-10.2.2">
<WorkerRole name="WorkerRole1" vmsize="ExtraSmall">
<Endpoints>
<InputEndpoint name="Endpoint1" protocol="http" port="80" localPort="80" />
</Endpoints>
<Imports>
<Import moduleName="Diagnostics" />
</Imports>
<ConfigurationSettings>
<Setting name="TableStorageConnectionString" />
<Setting name="BlobStorageConnectionString" />
</ConfigurationSettings>
</WorkerRole>
</ServiceDefinition>
Muokkaa myös tiedostoja ServiceConfiguration.Cloud.cscfg ja ServiceConfiguration.Local.cscfg sisältämään vastaavat settingit:
<?xml version="1.0" encoding="utf-8"?>
<ServiceConfiguration serviceName="FSharpAzure"
xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration"
osFamily="4" osVersion="*" schemaVersion="2013-10.2.2">
<Role name="WorkerRole1">
<Instances count="1" />
<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"
value="UseDevelopmentStorage=true" />
<Setting name="TableStorageConnectionString" value="UseDevelopmentStorage=true" />
<Setting name="BlobStorageConnectionString" value="UseDevelopmentStorage=true" />
</ConfigurationSettings>
</Role>
</ServiceConfiguration>
Emulaattorille kelpaa arvo value="UseDevelopmentStorage=true", mutta tuotannossa tämä connection-string on jotain muuta. Ohjeet siihen löytyy netistä.
F-Sharp skripti-tiedostojen käyttö
Näiden koodien ajaminen toimii interactive-ympäristöstä tiettyyn pisteeseen asti, mutta itse Azuren kutsukoodien suoritus ei. Voit siis lisätä koodien alkuun:
1: 2: 3: 4: 5: |
#if INTERACTIVE #r "../packages/Fog.0.1.4.1/Lib/Net40/Fog.dll" #r "System.Data.Services.Client.dll" #r "Microsoft.WindowsAzure.StorageClient.dll" #endif |
Näissä on huomattava se, että NuGet on voinut hakea sinulle eri version komponentista kuin mitä tässä esimerkissä. Joten jos Visual Studio alleviivaa rivin punaisella ja valittaa, että tiedostoa ei löydy, niin tämä voi johtua siitä, että sinulla on eri polussa uudempi versio siitä. Toinen F#:ssa tyypillinen tapa on lisätä projektiin yksi tiedosto tyyppiä Script File (*.fsx), joka suoritetaan vain interactive-tyylisessä scriptaus-ajoissa, mutta jää itsestään käännöksen ulkopuolelle. Nämä #r:t toimivat myös siellä, ja projektin .fs-tiedoston voi ladata käskyillä:
1: 2: |
#load "MyLogics.fs" open MyLogics |
Välimuisti ja asetusten haku
Tässä koodi, joka toimii sekä Blob:in että Table:n yhteydessä. Avaa logiikka"luokka" (=tiedosto) ja lisää siihen seuraava koodi:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: |
module MyLogics open System open Microsoft.WindowsAzure.ServiceRuntime open Microsoft.WindowsAzure.Storage open System.Collections.Concurrent let dict = ConcurrentDictionary() //for caching let fetchSetting = RoleEnvironment.GetConfigurationSettingValue >> CloudStorageAccount.Parse |
Käytetään memoize:a välimuistitukseen. Memoize/memoization poikkeaa normaalista cache-välimuistista siten, että välimuistitetaankin funktio, eikä vain sen parametreja.
Azure Blob Storage
Blob-storage on simppeli tietovarasto esim. tiedostoja varten. Lisää edellisen perään seuraava toiminnallisuus:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: |
let blobConnection (container:string) = let account = fetchSetting "BlobStorageConnectionString" let client = account.CreateCloudBlobClient() client.GetContainerReference(container.ToLower()) let uploadBlobToContainer containerName blobName (item:string) = let memoize f = fun x -> dict.GetOrAdd(Some x, lazy (f x)).Force() let container = memoize(fun cont -> blobConnection cont) containerName async { let! ok = container.CreateIfNotExistsAsync() |> Async.AwaitTask let blob = container.GetBlockBlobReference(blobName) let enc = System.Text.Encoding.ASCII.GetBytes(item) use ms = new System.IO.MemoryStream(enc, 0, enc.Length) do! blob.UploadFromStreamAsync ms |> Async.AwaitIAsyncResult |> Async.Ignore } let addToBlob = uploadBlobToContainer "testContainer" "testBlob" open System open FSharp.Data let demoData = let ``scientist of the day `` = FreebaseData.GetDataContext().Commons.Computers.``Computer Scientists`` |> Seq.skip DateTime.Now.DayOfYear // |> Seq.head "Scientist of the day: " + ``scientist of the day ``.Name |
Nyt voit lisätä kutsun tähän WorkerRole.fs:n wr.OnStart() -metodissa (ennen kutsua base.OnStart()):
1:
|
MyLogics.demoData |> MyLogics.addToBlob |> Async.RunSynchronously |
Kun ajat softan (F5), niin Server Explorer:iin (ei Solution Explorer) on (refresh:in jälkeen) ilmestynyt seuraava blobi, jonka voit tupla-klikata auki, jonka blob-listasta voit taas tupla-klikata itse tiedot auki:
Azure Table Storage
Azure Table Storage on NoSQL-henkinen tietovarasto.
Ohessa koodiesimerkki sen käyttöön:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: |
let ``Azure dvd table`` = "Dvd" [<Measure>] type stars open Microsoft.WindowsAzure.Storage.Table type MyDvdEntity(partitionKey, rowKey, name, rating) = inherit TableEntity(partitionKey, rowKey) new(name, rating) = MyDvdEntity("defaultPartition", System.Guid.NewGuid().ToString(), name, rating) new() = MyDvdEntity("", 0<stars>) member val Name = name with get, set member val Rating = rating with get, set let tableConnection tableName = let account = fetchSetting "TableStorageConnectionString" let client = account.CreateCloudTableClient() client.GetTableReference(tableName) let doAction tableName operation = let memoize f = fun x -> dict.GetOrAdd(Some x, lazy (f x)).Force() let table = memoize(fun tb -> tableConnection tb) tableName async { let! created = table.CreateIfNotExistsAsync() |> Async.AwaitTask return! table.ExecuteAsync(operation) |> Async.AwaitTask } let addDvd name rating = let dvd = MyDvdEntity( PartitionKey = "myPartition", RowKey = System.Guid.NewGuid().ToString(), Name = name, Rating = rating ) dvd, dvd |> TableOperation.Insert //Insert, Delete, Replace, etc. |> doAction ``Azure dvd table`` |> Async.RunSynchronously let updateDvd dvd = dvd |> TableOperation.Replace |> doAction ``Azure dvd table`` |> Async.RunSynchronously let deleteDvd dvd = dvd |> TableOperation.Delete |> doAction ``Azure dvd table`` |> Async.RunSynchronously |
...ja vastaavasti tätä kutsutaan WorkerRole.fs-tiedostosta, kutsu OnStart-metodiin, ennen base.OnStart:ia, esim:
1: 2: 3: |
let dvd, result = MyLogics.addDvd "Godfather" 4<stars> dvd.Rating <- 5<MyLogics.stars> let result2 = MyLogics.updateDvd dvd |
Varsinainen Table Storage:n kyselykieli on varsin suppea, eikä uusi Table Service Layer tue LINQ:a, joten kyselyt pitää tehdä joko TableOperation.Retrieve-metodin kautta tai rakentamalla erikseen TableQuery ja suorittamalla se ExecuteQuery-metodilla.
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.dict
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken
Full name: Microsoft.FSharp.Control.Async
--------------------
type Async<'T>
Full name: Microsoft.FSharp.Control.Async<_>
member BodyName : string
member Clone : unit -> obj
member CodePage : int
member DecoderFallback : DecoderFallback with get, set
member EncoderFallback : EncoderFallback with get, set
member EncodingName : string
member Equals : value:obj -> bool
member GetByteCount : chars:char[] -> int + 3 overloads
member GetBytes : chars:char[] -> byte[] + 5 overloads
member GetCharCount : bytes:byte[] -> int + 2 overloads
...
Full name: System.Text.Encoding
System.Text.Encoding.GetBytes(chars: char []) : byte []
System.Text.Encoding.GetBytes(chars: char [], index: int, count: int) : byte []
System.Text.Encoding.GetBytes(chars: nativeptr<char>, charCount: int, bytes: nativeptr<byte>, byteCount: int) : int
System.Text.Encoding.GetBytes(s: string, charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
System.Text.Encoding.GetBytes(chars: char [], charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
type MemoryStream =
inherit Stream
new : unit -> MemoryStream + 6 overloads
member CanRead : bool
member CanSeek : bool
member CanWrite : bool
member Capacity : int with get, set
member Flush : unit -> unit
member GetBuffer : unit -> byte[]
member Length : int64
member Position : int64 with get, set
member Read : buffer:byte[] * offset:int * count:int -> int
...
Full name: System.IO.MemoryStream
--------------------
System.IO.MemoryStream() : unit
System.IO.MemoryStream(capacity: int) : unit
System.IO.MemoryStream(buffer: byte []) : unit
System.IO.MemoryStream(buffer: byte [], writable: bool) : unit
System.IO.MemoryStream(buffer: byte [], index: int, count: int) : unit
System.IO.MemoryStream(buffer: byte [], index: int, count: int, writable: bool) : unit
System.IO.MemoryStream(buffer: byte [], index: int, count: int, writable: bool, publiclyVisible: bool) : unit
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.skip
Full name: Microsoft.FSharp.Collections.Seq.head
type MeasureAttribute =
inherit Attribute
new : unit -> MeasureAttribute
Full name: Microsoft.FSharp.Core.MeasureAttribute
--------------------
new : unit -> MeasureAttribute
type Guid =
struct
new : b:byte[] -> Guid + 4 overloads
member CompareTo : value:obj -> int + 1 overload
member Equals : o:obj -> bool + 1 overload
member GetHashCode : unit -> int
member ToByteArray : unit -> byte[]
member ToString : unit -> string + 2 overloads
static val Empty : Guid
static member NewGuid : unit -> Guid
static member Parse : input:string -> Guid
static member ParseExact : input:string * format:string -> Guid
...
end
Full name: System.Guid
--------------------
System.Guid()
System.Guid(b: byte []) : unit
System.Guid(g: string) : unit
System.Guid(a: int, b: int16, c: int16, d: byte []) : unit
System.Guid(a: uint32, b: uint16, c: uint16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : unit
System.Guid(a: int, b: int16, c: int16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : unit
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set