Öğretici: Tür Sağlayıcısı Oluşturma

F# dilindeki tür sağlayıcısı mekanizması, bilgi zengin programlama desteğinin önemli bir parçasıdır. Bu öğreticide, temel kavramları göstermek için birkaç basit tür sağlayıcısının geliştirilmesinde size yol göstererek kendi tür sağlayıcılarınızı nasıl oluşturacağınız açıklanmaktadır. F# dilindeki tür sağlayıcısı mekanizması hakkında daha fazla bilgi için bkz . Tür Sağlayıcıları.

F# ekosistemi, yaygın olarak kullanılan İnternet ve kurumsal veri hizmetleri için bir dizi tür sağlayıcısı içerir. Örneğin:

  • FSharp.Data JSON, XML, CSV ve HTML belge biçimleri için tür sağlayıcıları içerir.

  • SwaggerProvider , OpenApi 3.0 ve Swagger 2.0 şemaları tarafından açıklanan API'ler için nesne modeli ve HTTP istemcileri oluşturan iki oluşturucu tür sağlayıcısı içerir.

  • FSharp.Data.SqlClient , T-SQL'in F# dilinde derlenmiş zaman denetimiyle katıştırılması için bir tür sağlayıcısı kümesine sahiptir.

Özel tür sağlayıcıları oluşturabilir veya başkalarının oluşturduğu tür sağlayıcılarına başvurabilirsiniz. Örneğin, kuruluşunuzun her biri kendi kararlı veri şemasına sahip çok sayıda ve artan sayıda adlandırılmış veri kümesi sağlayan bir veri hizmeti olabilir. Şemaları okuyan ve geçerli veri kümelerini programcıya kesin bir şekilde sunan bir tür sağlayıcısı oluşturabilirsiniz.

Başlamadan Önce

Tür sağlayıcısı mekanizması öncelikle F# programlama deneyimine kararlı veri ve hizmet bilgileri alanları eklemek için tasarlanmıştır.

Bu mekanizma, program yürütme sırasında şemaları program mantığına uygun şekilde değişen bilgi alanları eklemek için tasarlanmamıştır. Ayrıca bu etki alanı bazı geçerli kullanımlar içerse bile mekanizma dil içi meta programlama için tasarlanmamıştır. Bu mekanizmayı yalnızca gerekli yerlerde ve bir tür sağlayıcısının geliştirilmesinin çok yüksek değere sahip olduğu durumlarda kullanmanız gerekir.

Şemanın kullanılamadığı bir tür sağlayıcısı yazmaktan kaçınmalısınız. Benzer şekilde, sıradan bir .NET kitaplığının (hatta mevcut bir kitaplığın) yeterli olacağı bir tür sağlayıcısı yazmaktan kaçınmalısınız.

Başlamadan önce aşağıdaki soruları sorabilirsiniz:

  • Bilgi kaynağınız için bir şemanız var mı? Öyleyse, F# ve .NET tür sistemine eşleme nedir?

  • Uygulamanız için başlangıç noktası olarak mevcut (dinamik olarak yazılan) bir API kullanabilir misiniz?

  • Siz ve kuruluşunuz, yazmaya değer hale getirmek için tür sağlayıcısını yeterince kullanıyor musunuz? Normal bir .NET kitaplığı gereksinimlerinizi karşılar mı?

  • Şemanız ne kadar değişecek?

  • Kodlama sırasında değişecek mi?

  • Kodlama oturumları arasında değişecek mi?

  • Program yürütme sırasında değişecek mi?

Tür sağlayıcıları, şemanın çalışma zamanında ve derlenmiş kodun ömrü boyunca kararlı olduğu durumlar için en uygun olanıdır.

Basit Tür Sağlayıcısı

Bu örnek Samples.HelloWorldTypeProvider örneğidir ve F# Türü Sağlayıcı SDK'sının dizinindeki examples örneklere benzer. Sağlayıcı, F# imza söz dizimi kullanılarak ve dışındaki Type1tüm ayrıntıları atlayarak aşağıdaki kodda gösterildiği gibi 100 silinmiş tür içeren bir "tür alanı" sağlar. Silinen türler hakkında daha fazla bilgi için, bu konunun devamında yer alan Silinen Sağlanan Türler Hakkında Ayrıntılar'a bakın.

namespace Samples.HelloWorldTypeProvider

type Type1 =
    /// This is a static property.
    static member StaticProperty : string

    /// This constructor takes no arguments.
    new : unit -> Type1

    /// This constructor takes one argument.
    new : data:string -> Type1

    /// This is an instance property.
    member InstanceProperty : int

    /// This is an instance method.
    member InstanceMethod : x:int -> char

    nested type NestedType =
        /// This is StaticProperty1 on NestedType.
        static member StaticProperty1 : string
        …
        /// This is StaticProperty100 on NestedType.
        static member StaticProperty100 : string

type Type2 =
…
…

type Type100 =
…

Sağlanan tür ve üye kümesinin statik olarak bilindiğini unutmayın. Bu örnek, sağlayıcıların şemaya bağlı türler sağlama özelliğinden yararlanmaz. Tür sağlayıcısının uygulanması aşağıdaki kodda özetlenmiştir ve ayrıntılar bu konunun sonraki bölümlerinde ele alınmıştır.

Uyarı

Bu kod ile çevrimiçi örnekler arasında farklar olabilir.

namespace Samples.FSharp.HelloWorldTypeProvider

open System
open System.Reflection
open ProviderImplementation.ProvidedTypes
open FSharp.Core.CompilerServices
open FSharp.Quotations

// This type defines the type provider. When compiled to a DLL, it can be added
// as a reference to an F# command-line compilation, script, or project.
[<TypeProvider>]
type SampleTypeProvider(config: TypeProviderConfig) as this =

  // Inheriting from this type provides implementations of ITypeProvider
  // in terms of the provided types below.
  inherit TypeProviderForNamespaces(config)

  let namespaceName = "Samples.HelloWorldTypeProvider"
  let thisAssembly = Assembly.GetExecutingAssembly()

  // Make one provided type, called TypeN.
  let makeOneProvidedType (n:int) =
  …
  // Now generate 100 types
  let types = [ for i in 1 .. 100 -> makeOneProvidedType i ]

  // And add them to the namespace
  do this.AddNamespace(namespaceName, types)

[<assembly:TypeProviderAssembly>]
do()

Bu sağlayıcıyı kullanmak için ayrı bir Visual Studio örneği açın, bir F# betiği oluşturun ve ardından aşağıdaki kodda gösterildiği gibi #r kullanarak betiğinizden sağlayıcıya bir başvuru ekleyin:

#r @".\bin\Debug\Samples.HelloWorldTypeProvider.dll"

let obj1 = Samples.HelloWorldTypeProvider.Type1("some data")

let obj2 = Samples.HelloWorldTypeProvider.Type1("some other data")

obj1.InstanceProperty
obj2.InstanceProperty

[ for index in 0 .. obj1.InstanceProperty-1 -> obj1.InstanceMethod(index) ]
[ for index in 0 .. obj2.InstanceProperty-1 -> obj2.InstanceMethod(index) ]

let data1 = Samples.HelloWorldTypeProvider.Type1.NestedType.StaticProperty35

Ardından, tür sağlayıcısının Samples.HelloWorldTypeProvider oluşturduğu ad alanı altında türleri arayın.

Sağlayıcıyı yeniden derlemeden önce, sağlayıcı DLL'sini kullanan tüm Visual Studio ve F# Interactive örneklerini kapattığınızdan emin olun. Aksi takdirde, çıkış DLL'i kilitlendiğinden derleme hatası oluşur.

Yazdırma deyimlerini kullanarak bu sağlayıcıda hata ayıklamak için sağlayıcıyla ilgili bir sorun ortaya çıkaran bir betik yapın ve aşağıdaki kodu kullanın:

fsc.exe -r:bin\Debug\HelloWorldTypeProvider.dll script.fsx

Visual Studio kullanarak bu sağlayıcıda hata ayıklamak için, yönetici kimlik bilgileriyle Visual Studio için Geliştirici Komut İstemi'ni açın ve aşağıdaki komutu çalıştırın:

devenv.exe /debugexe fsc.exe -r:bin\Debug\HelloWorldTypeProvider.dll script.fsx

Alternatif olarak, Visual Studio'yu açın, Hata Ayıkla menüsünü açın, öğesini seçin Debug/Attach to process…ve betiğinizi düzenlediğiniz başka bir devenv işleme ekleyin. Bu yöntemi kullanarak, ikinci örneğe etkileşimli olarak ifadeler yazarak (tam IntelliSense ve diğer özelliklerle) tür sağlayıcısındaki belirli mantığı daha kolay hedefleyebilirsiniz.

Oluşturulan koddaki hataları daha iyi tanımlamak için Yalnızca Kodum hata ayıklamasını devre dışı bırakabilirsiniz. Bu özelliği etkinleştirme veya devre dışı bırakma hakkında bilgi için bkz . Hata Ayıklayıcı ile Kodda Gezinme. Ayrıca, menüyü açıp Debug ardından iletişim kutusunu açmak için Ctrl+Alt+E tuşlarını seçerek Exceptions de ilk şans özel durum yakalamayı Exceptions ayarlayabilirsiniz. Bu iletişim kutusunda, altında Common Language Runtime Exceptionsonay kutusunu seçin Thrown .

Tür Sağlayıcısının Uygulanması

Bu bölüm, tür sağlayıcısı uygulamasının asıl bölümlerinde size yol gösterir. İlk olarak, özel tür sağlayıcısının türünü tanımlarsınız:

[<TypeProvider>]
type SampleTypeProvider(config: TypeProviderConfig) as this =

Bu tür genel olmalıdır ve ayrı bir F# projesi türü içeren derlemeye başvurduğunda derleyicinin tür sağlayıcısını tanıması için bunu TypeProvider özniteliğiyle işaretlemeniz gerekir. Yapılandırma parametresi isteğe bağlıdır ve varsa F# derleyicisinin oluşturduğu tür sağlayıcısı örneği için bağlamsal yapılandırma bilgilerini içerir.

Ardından, ITypeProvider arabirimini uygulayacaksınız. Bu durumda, API'deki ProvidedTypes türünü temel tür olarak kullanırsınızTypeProviderForNamespaces. Bu yardımcı türü, her biri doğrudan sınırlı sayıda sabit, hevesle sağlanan türleri içeren, hevesle sağlanan ad alanlarının sınırlı bir koleksiyonunu sağlayabilir. Bu bağlamda sağlayıcı , gerekli olmasalar veya kullanılmasalar bile türleri hevesle oluşturur.

inherit TypeProviderForNamespaces(config)

Ardından, sağlanan türler için ad alanını belirten yerel özel değerleri tanımlayın ve tür sağlayıcısı derlemesinin kendisini bulun. Bu derleme daha sonra, sağlanan silinen türlerin mantıksal üst türü olarak kullanılır.

let namespaceName = "Samples.HelloWorldTypeProvider"
let thisAssembly = Assembly.GetExecutingAssembly()

Ardından, Tür1 türlerinin her birini sağlamak için bir işlev oluşturun... 100 yazın. Bu işlev, bu konunun ilerleyen bölümlerinde daha ayrıntılı olarak açıklanmıştır.

let makeOneProvidedType (n:int) = …

Ardından sağlanan 100 türü oluşturun:

let types = [ for i in 1 .. 100 -> makeOneProvidedType i ]

Ardından, türleri sağlanan ad alanı olarak ekleyin:

do this.AddNamespace(namespaceName, types)

Son olarak, tür sağlayıcısı DLL'sini oluşturduğunuzu belirten bir derleme özniteliği ekleyin:

[<assembly:TypeProviderAssembly>]
do()

Bir tür ve üyelerini sağlama

makeOneProvidedType işlevi, türlerden birini sağlamanın gerçek işini yapar.

let makeOneProvidedType (n:int) =
…

Bu adımda bu işlevin uygulanması açıklanmaktadır. İlk olarak, sağlanan türü oluşturun (örneğin, n = 1 olduğunda Tür1 veya n = 57 olduğunda Type57).

// This is the provided type. It is an erased provided type and, in compiled code,
// will appear as type 'obj'.
let t = ProvidedTypeDefinition(thisAssembly, namespaceName,
                               "Type" + string n,
                               baseType = Some typeof<obj>)

Aşağıdaki noktaları not edin:

  • Sağlanan bu tür silinir. Temel türün olduğunu objbelirttiğinizden, örnekler derlenmiş kodda obj türünde değerler olarak görünür.

  • İç içe olmayan bir tür belirttiğinizde, derlemeyi ve ad alanını belirtmeniz gerekir. Silinen türler için derleme, tür sağlayıcısı derlemesinin kendisi olmalıdır.

Ardından türüne XML belgeleri ekleyin. Bu belge gecikir, yani konak derleyicisi ihtiyaç duyarsa isteğe bağlı olarak hesaplanır.

t.AddXmlDocDelayed (fun () -> $"""This provided type {"Type" + string n}""")

Ardından türüne sağlanan bir statik özellik eklersiniz:

let staticProp = ProvidedProperty(propertyName = "StaticProperty",
                                  propertyType = typeof<string>,
                                  isStatic = true,
                                  getterCode = (fun args -> <@@ "Hello!" @@>))

Bu özelliği almak her zaman "Hello!" dizesini değerlendirir. GetterCode özelliği, konak derleyicisinin özelliği almak için oluşturduğu kodu temsil eden bir F# teklifi kullanır. Teklifler hakkında daha fazla bilgi için bkz . Kod Teklifleri (F#).

Özelliğine XML belgeleri ekleyin.

staticProp.AddXmlDocDelayed(fun () -> "This is a static property")

Şimdi sağlanan özelliği sağlanan türe ekleyin. Sağlanan bir üyeyi tek ve tek bir türe eklemeniz gerekir. Aksi takdirde üyeye hiçbir zaman erişilemez.

t.AddMember staticProp

Şimdi parametre içermeyen sağlanan bir oluşturucu oluşturun.

let ctor = ProvidedConstructor(parameters = [ ],
                               invokeCode = (fun args -> <@@ "The object data" :> obj @@>))

InvokeCode oluşturucu için, konak derleyicisinin oluşturucu çağrıldığında oluşturduğu kodu temsil eden bir F# teklifi döndürür. Örneğin, aşağıdaki oluşturucuyu kullanabilirsiniz:

new Type10()

Sağlanan türün bir örneği, temel alınan "Nesne verileri" verileriyle oluşturulur. Bu tür bu sağlanan türün silinmesi olduğundan (sağlanan türü bildirdiğinizde belirttiğiniz gibi) teklif edilen kod obj'ye dönüştürme içerir.

Oluşturucuya XML belgeleri ekleyin ve sağlanan oluşturucuyu sağlanan türe ekleyin:

ctor.AddXmlDocDelayed(fun () -> "This is a constructor")

t.AddMember ctor

Bir parametre alan ikinci bir sağlanan oluşturucu oluşturun:

let ctor2 =
ProvidedConstructor(parameters = [ ProvidedParameter("data",typeof<string>) ],
                    invokeCode = (fun args -> <@@ (%%(args[0]) : string) :> obj @@>))

InvokeCode oluşturucu için yeniden bir F# teklifi döndürür. Bu, konak derleyicisinin yöntemine yönelik bir çağrı için oluşturduğu kodu temsil eder. Örneğin, aşağıdaki oluşturucuyu kullanabilirsiniz:

new Type10("ten")

Sağlanan türün bir örneği, temel alınan "on" verisiyle oluşturulur. İşlevin InvokeCode bir tırnak döndürdüğünü fark etmiş olabilirsiniz. Bu işlevin girişi, oluşturucu parametresi başına bir ifade listesidir. Bu durumda, içinde tek parametre değerini temsil eden bir ifade kullanılabilir args[0]. Oluşturucuya yapılan bir çağrının kodu, döndürülen değeri silinen türüne objzorlamalı olarak belirler. Sağlanan ikinci oluşturucuyu türüne ekledikten sonra, sağlanan bir örnek özelliği oluşturursunuz:

let instanceProp =
    ProvidedProperty(propertyName = "InstanceProperty",
                     propertyType = typeof<int>,
                     getterCode= (fun args ->
                        <@@ ((%%(args[0]) : obj) :?> string).Length @@>))
instanceProp.AddXmlDocDelayed(fun () -> "This is an instance property")
t.AddMember instanceProp

Bu özelliği almak, temsil nesnesi olan dizenin uzunluğunu döndürür. özelliği, GetterCode konak derleyicisinin özelliği almak için oluşturduğu kodu belirten bir F# teklifi döndürür. GetterCode gibiInvokeCode, işlevi bir tırnak döndürür. Konak derleyicisi bu işlevi bir bağımsız değişken listesiyle çağırır. Bu durumda, bağımsız değişkenler yalnızca getter'ın çağrıldığı örneği temsil eden ve kullanarak args[0]erişebileceğiniz tek ifadeyi içerir. uygulaması GetterCode , silinen türündeki objsonuç teklifine eklenir ve derleyicinin nesnenin dize olduğunu denetleme mekanizmasını karşılamak için bir atama kullanılır. öğesinin makeOneProvidedType sonraki bölümünde tek parametreli bir örnek yöntemi sağlanır.

let instanceMeth =
    ProvidedMethod(methodName = "InstanceMethod",
                   parameters = [ProvidedParameter("x",typeof<int>)],
                   returnType = typeof<char>,
                   invokeCode = (fun args ->
                       <@@ ((%%(args[0]) : obj) :?> string).Chars(%%(args[1]) : int) @@>))

instanceMeth.AddXmlDocDelayed(fun () -> "This is an instance method")
// Add the instance method to the type.
t.AddMember instanceMeth

Son olarak, 100 iç içe özellik içeren iç içe bir tür oluşturun. Bu iç içe türün ve özelliklerinin oluşturulması gecikir, yani isteğe bağlı olarak hesaplanır.

t.AddMembersDelayed(fun () ->
  let nestedType = ProvidedTypeDefinition("NestedType", Some typeof<obj>)

  nestedType.AddMembersDelayed (fun () ->
    let staticPropsInNestedType =
      [
          for i in 1 .. 100 ->
              let valueOfTheProperty = "I am string "  + string i

              let p =
                ProvidedProperty(propertyName = "StaticProperty" + string i,
                  propertyType = typeof<string>,
                  isStatic = true,
                  getterCode= (fun args -> <@@ valueOfTheProperty @@>))

              p.AddXmlDocDelayed(fun () ->
                  $"This is StaticProperty{i} on NestedType")

              p
      ]

    staticPropsInNestedType)

  [nestedType])

Silinen Sağlanan Türler hakkındaki ayrıntılar

Bu bölümdeki örnek, yalnızca aşağıdaki durumlarda özellikle yararlı olan silinmiş sağlanan türleri sağlar:

  • Yalnızca veri ve yöntemleri içeren bir bilgi alanı için sağlayıcı yazarken.

  • Bilgi alanının pratik kullanımı için doğru çalışma zamanı türü semantiğin kritik olmadığı bir sağlayıcı yazarken.

  • Çok büyük ve birbirine bağlı bir bilgi alanı için bir sağlayıcı yazarken, bilgi alanı için gerçek .NET türleri oluşturmak teknik olarak uygun değildir.

Bu örnekte, sağlanan her tür yazmak objiçin silinir ve türün tüm kullanımları derlenmiş kodda tür obj olarak görünür. Aslında, bu örneklerde temel alınan nesneler dizelerdir, ancak tür .NET derlenmiş kodunda olduğu gibi System.Object görünür. Tür silme işleminin tüm kullanımlarında olduğu gibi, silinen türleri alt etmek için açık kutulama, kutulama kaldırma ve atama kullanabilirsiniz. Bu durumda, nesne kullanıldığında geçerli olmayan bir atama özel durumu oluşabilir. Bir sağlayıcı çalışma zamanı, yanlış gösterimlere karşı korumaya yardımcı olmak için kendi özel gösterim türünü tanımlayabilir. Silinen türleri F# içinde tanımlayamazsınız. Yalnızca sağlanan türler silinebilir. Tür sağlayıcınız için silinen türleri veya silinen türleri sağlayan bir sağlayıcıyı kullanmanın hem pratik hem de semantik nedenlerini anlamanız gerekir. Silinen bir türün gerçek .NET türü yoktur. Bu nedenle, tür üzerinde doğru yansıma yapamazsınız ve tam çalışma zamanı türü semantiği kullanan çalışma zamanı atamaları ve diğer teknikleri kullanırsanız silinen türleri alta çevirebilirsiniz. Silinen türlerin alt sürüme çevrilmesi genellikle çalışma zamanında tür atama özel durumlarına neden olur.

Silinen Sağlanan Türler için Gösterim seçme

Silinen sağlanan türlerin bazı kullanımları için gösterim gerekmez. Örneğin, silinen sağlanan tür yalnızca statik özellikler ve üyeler içerebilir ve oluşturucu içermeyebilir ve hiçbir yöntem veya özellik türün bir örneğini döndürmez. Silinen bir sağlanan türün örneklerine ulaşabiliyorsanız aşağıdaki soruları göz önünde bulundurmanız gerekir:

Sağlanan bir türün silinmesi nedir?

  • Sağlanan türün silinmesi, türün derlenmiş .NET kodunda nasıl göründüğüdür.

  • Sağlanan bir silinen sınıf türünün silinmesi her zaman türün devralma zincirindeki ilk silinmemiş temel türdür.

  • Sağlanan bir silinen arabirim türünün silinmesi her zaman System.Objectşeklindedir.

Sağlanan türün gösterimleri nelerdir?

  • Sağlanan bir tür için olası nesneler kümesi, temsilleri olarak adlandırılır. Bu belgedeki örnekte, silinen tüm türlerin Type1..Type100 gösterimleri her zaman dize nesneleridir.

Sağlanan türün tüm gösterimleri, sağlanan türün silinmesiyle uyumlu olmalıdır. (Aksi takdirde, F# derleyicisi tür sağlayıcısının kullanımı için bir hata verir veya geçerli olmayan doğrulanamaz .NET kodu oluşturulur. Tür sağlayıcısı geçerli olmayan bir gösterim veren kod döndürüyorsa geçerli değildir.)

Sağlanan nesneler için, her ikisi de çok yaygın olan aşağıdaki yaklaşımlardan birini kullanarak bir gösterim seçebilirsiniz:

  • Var olan bir .NET türü üzerinde kesin olarak yazılan bir sarmalayıcı sağlıyorsanız, genellikle türünüzün bu türe silmesi, bu tür örneklerini gösterim olarak kullanması veya her ikisini birden kullanması mantıklıdır. Bu yaklaşım, bu türdeki mevcut yöntemlerin çoğu kesin olarak belirlenmiş sürümü kullanırken hala anlamlı olduğunda uygundur.

  • Mevcut .NET API'lerinden önemli ölçüde farklı bir API oluşturmak istiyorsanız, sağlanan türlerin tür silme ve gösterimleri olacak çalışma zamanı türleri oluşturmak mantıklıdır.

Bu belgedeki örnek, sağlanan nesnelerin gösterimleri olarak dizeleri kullanır. Sıklıkla, gösterimler için diğer nesneleri kullanmak uygun olabilir. Örneğin, bir sözlüğü özellik paketi olarak kullanabilirsiniz:

ProvidedConstructor(parameters = [],
    invokeCode= (fun args -> <@@ (new Dictionary<string,obj>()) :> obj @@>))

Alternatif olarak, tür sağlayıcınızda bir veya daha fazla çalışma zamanı işlemiyle birlikte gösterimi oluşturmak için çalışma zamanında kullanılacak bir tür tanımlayabilirsiniz:

type DataObject() =
    let data = Dictionary<string,obj>()
    member x.RuntimeOperation() = data.Count

Sağlanan üyeler bu nesne türünün örneklerini oluşturabilir:

ProvidedConstructor(parameters = [],
    invokeCode= (fun args -> <@@ (new DataObject()) :> obj @@>))

Bu durumda, bu türü oluştururken ProvidedTypeDefinitionolarak belirterek (isteğe bağlı olarak) bu türü tür silme olarak baseType kullanabilirsiniz:

ProvidedTypeDefinition(…, baseType = Some typeof<DataObject> )
…
ProvidedConstructor(…, InvokeCode = (fun args -> <@@ new DataObject() @@>), …)

Önemli Dersler

Önceki bölümde, bir dizi tür, özellik ve yöntem sağlayan basit bir silme türü sağlayıcısının nasıl oluşturulacağı açıklanmıştır. Bu bölümde, bir tür sağlayıcısından silinen türleri sağlamanın bazı avantajları ve dezavantajları da dahil olmak üzere tür silme kavramı açıklanmış ve silinen türlerin gösterimleri ele alınmıştır.

Statik Parametreler Kullanan Bir Tür Sağlayıcısı

Tür sağlayıcılarını statik verilere göre parametreleştirebilme özelliği, sağlayıcının herhangi bir yerel veya uzak veriye erişmesi gerekmese bile birçok ilginç senaryoya olanak tanır. Bu bölümde, böyle bir sağlayıcıyı bir araya getirmek için bazı temel teknikleri öğreneceksiniz.

Tür İşaretli Regex Sağlayıcısı

.NET Regex kitaplıklarını aşağıdaki derleme zamanı garantilerini sağlayan bir arabirimde sarmalayan normal ifadeler için bir tür sağlayıcısı uygulamak istediğinizi düşünün:

  • Normal ifadenin geçerli olup olmadığını doğrulama.

  • Normal ifadedeki herhangi bir grup adını temel alan eşleşmelerde adlandırılmış özellikler sağlama.

Bu bölümde, normal ifade deseninin bu avantajları sağlamak için parametreleştirdiğini bir RegexTyped tür oluşturmak için tür sağlayıcılarının nasıl kullanılacağı gösterilmektedir. Sağlanan desen geçerli değilse derleyici bir hata bildirir ve tür sağlayıcısı grupları desenden ayıklayabilir, böylece eşleşmelerde adlandırılmış özellikleri kullanarak bunlara erişebilirsiniz. Bir tür sağlayıcısı tasarlarken, kullanıma sunulan API'nin son kullanıcılara nasıl görünmesi gerektiğini ve bu tasarımın .NET koduna nasıl çevrileceğini göz önünde bulundurmalısınız. Aşağıdaki örnekte, alan kodunun bileşenlerini almak için böyle bir API'nin nasıl kullanılacağı gösterilmektedir:

type T = RegexTyped< @"(?<AreaCode>^\d{3})-(?<PhoneNumber>\d{3}-\d{4}$)">
let reg = T()
let result = T.IsMatch("425-555-2345")
let r = reg.Match("425-555-2345").Group_AreaCode.Value //r equals "425"

Aşağıdaki örnekte, tür sağlayıcısının bu çağrıları nasıl çevirdiği gösterilmektedir:

let reg = new Regex(@"(?<AreaCode>^\d{3})-(?<PhoneNumber>\d{3}-\d{4}$)")
let result = reg.IsMatch("425-123-2345")
let r = reg.Match("425-123-2345").Groups["AreaCode"].Value //r equals "425"

Aaşağıdaki noktaları unutmayın:

  • Standart Regex türü parametreli RegexTyped türü temsil eder.

  • Oluşturucu RegexTyped , Regex oluşturucusunun bir çağrısına neden olur ve desen için statik tür bağımsız değişkenini geçirir.

  • yönteminin Match sonuçları standart Match türle temsil edilir.

  • Adlandırılmış her grup sağlanan bir özelliğe neden olur ve özelliğe erişim, eşleşmenin Groups koleksiyonunda bir dizin oluşturucunun kullanılmasına neden olur.

Aşağıdaki kod, böyle bir sağlayıcıyı uygulamaya yönelik mantığın temelidir ve bu örnek sağlanan türe tüm üyelerin eklenmesini atlar. Eklenen her üye hakkında bilgi için bu konunun devamında yer alan uygun bölüme bakın.

namespace Samples.FSharp.RegexTypeProvider

open System.Reflection
open Microsoft.FSharp.Core.CompilerServices
open Samples.FSharp.ProvidedTypes
open System.Text.RegularExpressions

[<TypeProvider>]
type public CheckedRegexProvider() as this =
    inherit TypeProviderForNamespaces()

    // Get the assembly and namespace used to house the provided types
    let thisAssembly = Assembly.GetExecutingAssembly()
    let rootNamespace = "Samples.FSharp.RegexTypeProvider"
    let baseTy = typeof<obj>
    let staticParams = [ProvidedStaticParameter("pattern", typeof<string>)]

    let regexTy = ProvidedTypeDefinition(thisAssembly, rootNamespace, "RegexTyped", Some baseTy)

    do regexTy.DefineStaticParameters(
        parameters=staticParams,
        instantiationFunction=(fun typeName parameterValues ->

          match parameterValues with
          | [| :? string as pattern|] ->

            // Create an instance of the regular expression.
            //
            // This will fail with System.ArgumentException if the regular expression is not valid.
            // The exception will escape the type provider and be reported in client code.
            let r = System.Text.RegularExpressions.Regex(pattern)

            // Declare the typed regex provided type.
            // The type erasure of this type is 'obj', even though the representation will always be a Regex
            // This, combined with hiding the object methods, makes the IntelliSense experience simpler.
            let ty =
              ProvidedTypeDefinition(
                thisAssembly,
                rootNamespace,
                typeName,
                baseType = Some baseTy)

            ...

            ty
          | _ -> failwith "unexpected parameter values"))

    do this.AddNamespace(rootNamespace, [regexTy])

[<TypeProviderAssembly>]
do ()

Aaşağıdaki noktaları unutmayın:

  • Tür sağlayıcısı iki statik parametre alır: patternzorunlu olan ve isteğe bağlı olan (varsayılan bir değer sağlandığından options).

  • Statik bağımsız değişkenler sağlandıktan sonra normal ifadenin bir örneğini oluşturursunuz. Regex hatalı biçimlendirilmişse bu örnek bir özel durum oluşturur ve bu hata kullanıcılara bildirilir.

  • Geri çağırma içinde DefineStaticParameters , bağımsız değişkenler sağlandıktan sonra döndürülecek türü tanımlarsınız.

  • Bu kod, IntelliSense deneyiminin sorunsuz kalması için true olarak ayarlanır HideObjectMethods . Bu öznitelik, sağlanan bir nesne için Equals, GetHashCode, Finalizeve GetType üyelerinin IntelliSense listelerinden gizlenmeye neden olur.

  • Yönteminin temel türü olarak kullanırsınız obj , ancak sonraki örnekte gösterildiği gibi bu türün çalışma zamanı gösterimi olarak bir Regex nesnesi kullanacaksınız.

  • Oluşturucuya yapılan Regex çağrı, normal bir ifade geçerli olmadığında bir ArgumentException oluşturur. Derleyici bu özel durumu yakalar ve derleme zamanında veya Visual Studio düzenleyicisinde kullanıcıya bir hata iletisi bildirir. Bu özel durum, normal ifadelerin bir uygulama çalıştırılmadan doğrulanmasına olanak tanır.

Yukarıda tanımlanan tür, anlamlı yöntemler veya özellikler içermediğinden henüz kullanışlı değildir. İlk olarak statik IsMatch bir yöntem ekleyin:

let isMatch =
    ProvidedMethod(
        methodName = "IsMatch",
        parameters = [ProvidedParameter("input", typeof<string>)],
        returnType = typeof<bool>,
        isStatic = true,
        invokeCode = fun args -> <@@ Regex.IsMatch(%%args[0], pattern) @@>)

isMatch.AddXmlDoc "Indicates whether the regular expression finds a match in the specified input string."
ty.AddMember isMatch

Önceki kod, bir dizeyi giriş olarak alan ve döndüren bir boolyöntemi IsMatchtanımlar. Tek karmaşık bölüm, bağımsız değişkenin args tanım içinde InvokeCode kullanılmasıdır. Bu örnekte, args bu yöntemin bağımsız değişkenlerini temsil eden tırnakların listesidir. Yöntem bir örnek yöntemiyse, ilk bağımsız değişken bağımsız değişkeni temsil eder this . Ancak, statik bir yöntem için bağımsız değişkenlerin tümü yalnızca yöntemin açık bağımsız değişkenleridir. Alıntılanan değerin türünün belirtilen dönüş türüyle (bu örnekte bool) eşleşmesi gerektiğini unutmayın. Ayrıca bu kodun AddXmlDoc , sağlanan yöntemin IntelliSense aracılığıyla sağlayabileceğiniz yararlı belgelere de sahip olduğundan emin olmak için yöntemini kullandığını unutmayın.

Ardından bir örnek Match yöntemi ekleyin. Ancak, gruplara kesin olarak belirlenmiş bir şekilde erişilmesi için bu yöntem sağlanan Match türde bir değer döndürmelidir. Bu nedenle, önce türünü bildirirsiniz Match . Bu tür statik bağımsız değişken olarak sağlanan desene bağlı olduğundan, bu tür parametreli tür tanımı içinde iç içe yerleştirilmelidir:

let matchTy =
    ProvidedTypeDefinition(
        "MatchType",
        baseType = Some baseTy,
        hideObjectMethods = true)

ty.AddMember matchTy

Ardından her grup için Eşleştirme türüne bir özellik eklersiniz. Çalışma zamanında bir eşleşme bir Match değer olarak temsil edilir, bu nedenle özelliği tanımlayan tırnak işareti ilgili grubu almak için dizine alınan özelliği kullanmalıdır Groups .

for group in r.GetGroupNames() do
    // Ignore the group named 0, which represents all input.
    if group <> "0" then
    let prop =
      ProvidedProperty(
        propertyName = group,
        propertyType = typeof<Group>,
        getterCode = fun args -> <@@ ((%%args[0]:obj) :?> Match).Groups[group] @@>)
        prop.AddXmlDoc($"""Gets the ""{group}"" group from this match""")
    matchTy.AddMember prop

Sağlanan özelliğe XML belgeleri eklediğinizi de unutmayın. Ayrıca, bir işlev sağlanırsa bir GetterCode özelliğin okunabileceğini ve bir işlev sağlandığında özelliğin yazılabilir SetterCode olduğunu, dolayısıyla sonuçta elde edilen özelliğin salt okunur olduğunu unutmayın.

Artık şu Match türde bir değer döndüren bir örnek yöntemi oluşturabilirsiniz:

let matchMethod =
    ProvidedMethod(
        methodName = "Match",
        parameters = [ProvidedParameter("input", typeof<string>)],
        returnType = matchTy,
        invokeCode = fun args -> <@@ ((%%args[0]:obj) :?> Regex).Match(%%args[1]) :> obj @@>)

matchMeth.AddXmlDoc "Searches the specified input string for the first occurrence of this regular expression"

ty.AddMember matchMeth

Bir örnek yöntemi oluşturduğunuzdan, args[0] yönteminin RegexTyped çağrıldığı örneği temsil eder ve args[1] giriş bağımsız değişkenidir.

Son olarak, sağlanan türün örneklerinin oluşturulabilmesi için bir oluşturucu sağlayın.

let ctor =
    ProvidedConstructor(
        parameters = [],
        invokeCode = fun args -> <@@ Regex(pattern, options) :> obj @@>)

ctor.AddXmlDoc("Initializes a regular expression instance.")

ty.AddMember ctor

Oluşturucu yalnızca standart bir .NET Regex örneğinin oluşturulmasını siler ve sağlanan türün silinmesi nedeniyle obj bir nesneye yeniden kutulanır. Bu değişiklikle, konu başlığında daha önce belirtilen örnek API kullanımı beklendiği gibi çalışır. Aşağıdaki kod tamamlandı ve son hali:

namespace Samples.FSharp.RegexTypeProvider

open System.Reflection
open Microsoft.FSharp.Core.CompilerServices
open Samples.FSharp.ProvidedTypes
open System.Text.RegularExpressions

[<TypeProvider>]
type public CheckedRegexProvider() as this =
    inherit TypeProviderForNamespaces()

    // Get the assembly and namespace used to house the provided types.
    let thisAssembly = Assembly.GetExecutingAssembly()
    let rootNamespace = "Samples.FSharp.RegexTypeProvider"
    let baseTy = typeof<obj>
    let staticParams = [ProvidedStaticParameter("pattern", typeof<string>)]

    let regexTy = ProvidedTypeDefinition(thisAssembly, rootNamespace, "RegexTyped", Some baseTy)

    do regexTy.DefineStaticParameters(
        parameters=staticParams,
        instantiationFunction=(fun typeName parameterValues ->

            match parameterValues with
            | [| :? string as pattern|] ->

                // Create an instance of the regular expression.

                let r = System.Text.RegularExpressions.Regex(pattern)

                // Declare the typed regex provided type.

                let ty =
                    ProvidedTypeDefinition(
                        thisAssembly,
                        rootNamespace,
                        typeName,
                        baseType = Some baseTy)

                ty.AddXmlDoc "A strongly typed interface to the regular expression '%s'"

                // Provide strongly typed version of Regex.IsMatch static method.
                let isMatch =
                    ProvidedMethod(
                        methodName = "IsMatch",
                        parameters = [ProvidedParameter("input", typeof<string>)],
                        returnType = typeof<bool>,
                        isStatic = true,
                        invokeCode = fun args -> <@@ Regex.IsMatch(%%args[0], pattern) @@>)

                isMatch.AddXmlDoc "Indicates whether the regular expression finds a match in the specified input string"

                ty.AddMember isMatch

                // Provided type for matches
                // Again, erase to obj even though the representation will always be a Match
                let matchTy =
                    ProvidedTypeDefinition(
                        "MatchType",
                        baseType = Some baseTy,
                        hideObjectMethods = true)

                // Nest the match type within parameterized Regex type.
                ty.AddMember matchTy

                // Add group properties to match type
                for group in r.GetGroupNames() do
                    // Ignore the group named 0, which represents all input.
                    if group <> "0" then
                        let prop =
                          ProvidedProperty(
                            propertyName = group,
                            propertyType = typeof<Group>,
                            getterCode = fun args -> <@@ ((%%args[0]:obj) :?> Match).Groups[group] @@>)
                        prop.AddXmlDoc(sprintf @"Gets the ""%s"" group from this match" group)
                        matchTy.AddMember(prop)

                // Provide strongly typed version of Regex.Match instance method.
                let matchMeth =
                  ProvidedMethod(
                    methodName = "Match",
                    parameters = [ProvidedParameter("input", typeof<string>)],
                    returnType = matchTy,
                    invokeCode = fun args -> <@@ ((%%args[0]:obj) :?> Regex).Match(%%args[1]) :> obj @@>)
                matchMeth.AddXmlDoc "Searches the specified input string for the first occurrence of this regular expression"

                ty.AddMember matchMeth

                // Declare a constructor.
                let ctor =
                  ProvidedConstructor(
                    parameters = [],
                    invokeCode = fun args -> <@@ Regex(pattern) :> obj @@>)

                // Add documentation to the constructor.
                ctor.AddXmlDoc "Initializes a regular expression instance"

                ty.AddMember ctor

                ty
            | _ -> failwith "unexpected parameter values"))

    do this.AddNamespace(rootNamespace, [regexTy])

[<TypeProviderAssembly>]
do ()

Önemli Dersler

Bu bölümde, statik parametreleri üzerinde çalışan bir tür sağlayıcısının nasıl oluşturulacağı açıklanmıştır. Sağlayıcı statik parametreyi denetler ve değerine göre işlemler sağlar.

Yerel veriler tarafından yedeklenen bir tür sağlayıcısı

Genellikle tür sağlayıcılarının api'leri yalnızca statik parametrelere değil, aynı zamanda yerel veya uzak sistemlerden gelen bilgilere göre sunmasını isteyebilirsiniz. Bu bölümde, yerel veri dosyaları gibi yerel verileri temel alan tür sağlayıcıları açıklanmıştır.

Basit CSV Dosya Sağlayıcısı

Basit bir örnek olarak, bilimsel verilere Virgülle Ayrılmış Değer (CSV) biçiminde erişmek için bir tür sağlayıcısı düşünün. Bu bölümde, aşağıdaki tabloda gösterildiği gibi CSV dosyalarının bir üst bilgi satırı ve ardından kayan nokta verileri içerdiği varsayılır:

Mesafe (ölçüm) Saat (saniye)
50.0 3.7
100.0 5.2
150.0 6.4

Bu bölümde, türünde bir özelliğe ve türünde bir Distance özelliğe sahip satırları almak için kullanabileceğiniz bir Time tür float<meter>float<second>sağlama gösterilmektedir. Kolaylık olması için aşağıdaki varsayımlar yapılır:

  • Üst bilgi adları birimsizdir veya "Ad (birim)" biçimindedir ve virgül içermez.

  • Birimler, FSharp.Data.UnitSystems.SI.UnitNames Modülü (F#) modülünün tanımladığı tüm System International (SI) birimleridir.

  • Birimler bileşik (örneğin, ölçüm/saniye) yerine basittir (örneğin, ölçüm).

  • Tüm sütunlar kayan nokta verileri içerir.

Daha eksiksiz bir sağlayıcı bu kısıtlamaları gevşeter.

İlk adım da API'nin nasıl görüneceğini göz önünde bulundurmaktır. Önceki tablonun içeriğine sahip bir info.csv dosya (virgülle ayrılmış biçimde) göz önüne alındığında, sağlayıcı kullanıcıları aşağıdaki örneğe benzer bir kod yazabilmelidir:

let info = new MiniCsv<"info.csv">()
for row in info.Data do
let time = row.Time
printfn $"{float time}"

Bu durumda, derleyicinin bu çağrıları aşağıdaki örneğe benzer bir şeye dönüştürmesi gerekir:

let info = new CsvFile("info.csv")
for row in info.Data do
let (time:float) = row[1]
printfn $"%f{float time}"

En uygun çeviri için tür sağlayıcısının tür sağlayıcısının derlemesinde gerçek CsvFile bir tür tanımlaması gerekir. Tür sağlayıcıları genellikle önemli mantığı sarmalamada birkaç yardımcı tür ve yöntem kullanır. Ölçüler çalışma zamanında silindiğinden, satırın silinen türü olarak bir kullanabilirsiniz float[] . Derleyici, farklı sütunları farklı ölçü türlerine sahip olarak ele alır. Örneğin, örneğimizdeki ilk sütun türüne float<meter>, ikinci sütunda ise float<second>vardır. Ancak, silinen gösterim oldukça basit kalabilir.

Aşağıdaki kod, uygulamanın çekirdeğini gösterir.

// Simple type wrapping CSV data
type CsvFile(filename) =
    // Cache the sequence of all data lines (all lines but the first)
    let data =
        seq {
            for line in File.ReadAllLines(filename) |> Seq.skip 1 ->
                line.Split(',') |> Array.map float
        }
        |> Seq.cache
    member _.Data = data

[<TypeProvider>]
type public MiniCsvProvider(cfg:TypeProviderConfig) as this =
    inherit TypeProviderForNamespaces(cfg)

    // Get the assembly and namespace used to house the provided types.
    let asm = System.Reflection.Assembly.GetExecutingAssembly()
    let ns = "Samples.FSharp.MiniCsvProvider"

    // Create the main provided type.
    let csvTy = ProvidedTypeDefinition(asm, ns, "MiniCsv", Some(typeof<obj>))

    // Parameterize the type by the file to use as a template.
    let filename = ProvidedStaticParameter("filename", typeof<string>)
    do csvTy.DefineStaticParameters([filename], fun tyName [| :? string as filename |] ->

        // Resolve the filename relative to the resolution folder.
        let resolvedFilename = Path.Combine(cfg.ResolutionFolder, filename)

        // Get the first line from the file.
        let headerLine = File.ReadLines(resolvedFilename) |> Seq.head

        // Define a provided type for each row, erasing to a float[].
        let rowTy = ProvidedTypeDefinition("Row", Some(typeof<float[]>))

        // Extract header names from the file, splitting on commas.
        // use Regex matching to get the position in the row at which the field occurs
        let headers = Regex.Matches(headerLine, "[^,]+")

        // Add one property per CSV field.
        for i in 0 .. headers.Count - 1 do
            let headerText = headers[i].Value

            // Try to decompose this header into a name and unit.
            let fieldName, fieldTy =
                let m = Regex.Match(headerText, @"(?<field>.+) \((?<unit>.+)\)")
                if m.Success then

                    let unitName = m.Groups["unit"].Value
                    let units = ProvidedMeasureBuilder.Default.SI unitName
                    m.Groups["field"].Value, ProvidedMeasureBuilder.Default.AnnotateType(typeof<float>,[units])

                else
                    // no units, just treat it as a normal float
                    headerText, typeof<float>

            let prop =
                ProvidedProperty(fieldName, fieldTy,
                    getterCode = fun [row] -> <@@ (%%row:float[])[i] @@>)

            // Add metadata that defines the property's location in the referenced file.
            prop.AddDefinitionLocation(1, headers[i].Index + 1, filename)
            rowTy.AddMember(prop)

        // Define the provided type, erasing to CsvFile.
        let ty = ProvidedTypeDefinition(asm, ns, tyName, Some(typeof<CsvFile>))

        // Add a parameterless constructor that loads the file that was used to define the schema.
        let ctor0 =
            ProvidedConstructor([],
                invokeCode = fun [] -> <@@ CsvFile(resolvedFilename) @@>)
        ty.AddMember ctor0

        // Add a constructor that takes the file name to load.
        let ctor1 = ProvidedConstructor([ProvidedParameter("filename", typeof<string>)],
            invokeCode = fun [filename] -> <@@ CsvFile(%%filename) @@>)
        ty.AddMember ctor1

        // Add a more strongly typed Data property, which uses the existing property at run time.
        let prop =
            ProvidedProperty("Data", typedefof<seq<_>>.MakeGenericType(rowTy),
                getterCode = fun [csvFile] -> <@@ (%%csvFile:CsvFile).Data @@>)
        ty.AddMember prop

        // Add the row type as a nested type.
        ty.AddMember rowTy
        ty)

    // Add the type to the namespace.
    do this.AddNamespace(ns, [csvTy])

Uygulama hakkında aşağıdaki noktaları not edin:

  • Aşırı yüklenmiş oluşturucular özgün dosyanın veya aynı şemaya sahip bir dosyanın okunmasına izin verir. Bu düzen, yerel veya uzak veri kaynakları için bir tür sağlayıcısı yazdığınızda yaygındır ve bu desen, uzak veriler için şablon olarak yerel bir dosyanın kullanılmasına izin verir.

  • Göreli dosya adlarını çözümlemek için tür sağlayıcısı oluşturucusunda geçirilen TypeProviderConfig değerini kullanabilirsiniz.

  • Sağlanan özelliklerin AddDefinitionLocation konumunu tanımlamak için yöntemini kullanabilirsiniz. Bu nedenle, sağlanan bir özellikte kullanırsanız Go To Definition , CSV dosyası Visual Studio'da açılır.

  • SI birimlerini ProvidedMeasureBuilder aramak ve ilgili float<_> türleri oluşturmak için türünü kullanabilirsiniz.

Önemli Dersler

Bu bölümde, veri kaynağının kendisinde bulunan basit bir şemayla yerel bir veri kaynağı için tür sağlayıcısının nasıl oluşturulacağı açıklanmıştır.

Daha Fazla

Aşağıdaki bölümlerde daha fazla çalışma önerileri yer almaktadır.

Silinen Türler için Derlenmiş Koda Bir Bakış

Tür sağlayıcısı kullanımının yayılan koda nasıl karşılık geldiğini öğrenmek için, bu konunun önceki bölümlerinde kullanılan öğesini kullanarak aşağıdaki işleve HelloWorldTypeProvider bakın.

let function1 () =
    let obj1 = Samples.HelloWorldTypeProvider.Type1("some data")
    obj1.InstanceProperty

burada, ildasm.exe kullanılarak oluşturulan kodun bir görüntüsü gösterilir:

.class public abstract auto ansi sealed Module1
extends [mscorlib]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAtt
ribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags)
= ( 01 00 07 00 00 00 00 00 )
.method public static int32  function1() cil managed
{
// Code size       24 (0x18)
.maxstack  3
.locals init ([0] object obj1)
IL_0000:  nop
IL_0001:  ldstr      "some data"
IL_0006:  unbox.any  [mscorlib]System.Object
IL_000b:  stloc.0
IL_000c:  ldloc.0
IL_000d:  call       !!0 [FSharp.Core_2]Microsoft.FSharp.Core.LanguagePrimit
ives/IntrinsicFunctions::UnboxGeneric<string>(object)
IL_0012:  callvirt   instance int32 [mscorlib_3]System.String::get_Length()
IL_0017:  ret
} // end of method Module1::function1

} // end of class Module1

Örnekte gösterildiği gibi, türün Type1 ve özelliğin InstanceProperty tüm bahsetmeleri silindi ve yalnızca çalışma zamanı türlerindeki işlemler dahil edildi.

Tür Sağlayıcıları için Tasarım ve Adlandırma Kuralları

Tür sağlayıcıları yazarken aşağıdaki kuralları gözlemleyin.

Bağlan Ivity Protocols sağlayıcıları Genel olarak, OData veya SQL bağlantıları gibi veri ve hizmet bağlantı protokolleri için çoğu sağlayıcı DLL'sinin adları veya TypeProvidersile TypeProvider bitmelidir. Örneğin, aşağıdaki dizeye benzeyen bir DLL adı kullanın:

Fabrikam.Management.BasicTypeProviders.dll

Sağlanan türlerinizin ilgili ad alanının üyesi olduğundan emin olun ve uyguladığınız bağlantı protokollerini belirtin:

  Fabrikam.Management.BasicTypeProviders.WmiConnection<…>
  Fabrikam.Management.BasicTypeProviders.DataProtocolConnection<…>

Genel Kodlama için Yardımcı Program Sağlayıcıları. Normal ifadeler için olduğu gibi bir yardımcı program türü sağlayıcısı için, aşağıdaki örnekte gösterildiği gibi tür sağlayıcısı bir temel kitaplığın parçası olabilir:

#r "Fabrikam.Core.Text.Utilities.dll"

Bu durumda, sağlanan tür normal .NET tasarım kurallarına göre uygun bir noktada görünür:

  open Fabrikam.Core.Text.RegexTyped

  let regex = new RegexTyped<"a+b+a+b+">()

Singleton Veri Kaynakları. Bazı tür sağlayıcıları tek bir ayrılmış veri kaynağına bağlanır ve yalnızca veri sağlar. Bu durumda, son eki bırakmanız TypeProvider ve .NET adlandırması için normal kuralları kullanmanız gerekir:

#r "Fabrikam.Data.Freebase.dll"

let data = Fabrikam.Data.Freebase.Astronomy.Asteroids

Daha fazla bilgi için bu konunun ilerleyen bölümlerinde açıklanan tasarım kuralına bakın GetConnection .

Tür Sağlayıcıları için Tasarım Desenleri

Aşağıdaki bölümlerde, tür sağlayıcıları yazarken kullanabileceğiniz tasarım desenleri açıklanmaktadır.

Get Bağlan ion Tasarım Deseni

Çoğu tür sağlayıcısı, aşağıdaki örnekte gösterildiği gibi FSharp.Data.TypeProviders.dll'daki tür sağlayıcıları tarafından kullanılan deseni kullanacak GetConnection şekilde yazılmalıdır:

#r "Fabrikam.Data.WebDataStore.dll"

type Service = Fabrikam.Data.WebDataStore<…static connection parameters…>

let connection = Service.GetConnection(…dynamic connection parameters…)

let data = connection.Astronomy.Asteroids

Uzak Veri ve Hizmetler Tarafından Yedeklenen Tür Sağlayıcıları

Uzak veriler ve hizmetler tarafından yedeklenen bir tür sağlayıcısı oluşturmadan önce, bağlı programlamanın doğasında bulunan bir dizi sorunu göz önünde bulundurmanız gerekir. Bu sorunlar aşağıdaki konuları içerir:

  • şema eşlemesi

  • şema değişikliği varlığında canlılık ve geçersizlik

  • şema önbelleğe alma

  • veri erişim işlemlerinin zaman uyumsuz uygulamaları

  • LINQ sorguları da dahil olmak üzere destekleyici sorgular

  • kimlik bilgileri ve kimlik doğrulaması

Bu konu başlığında bu sorunlar daha fazla keşfedilmez.

Ek Yazma Teknikleri

Kendi tür sağlayıcılarınızı yazarken aşağıdaki ek teknikleri kullanmak isteyebilirsiniz.

İsteğe Bağlı Türler ve Üyeler Oluşturma

ProvidedType API'sinde AddMember'ın gecikmeli sürümleri vardır.

  type ProvidedType =
      member AddMemberDelayed  : (unit -> MemberInfo)      -> unit
      member AddMembersDelayed : (unit -> MemberInfo list) -> unit

Bu sürümler, isteğe bağlı tür alanları oluşturmak için kullanılır.

Dizi türleri ve Genel Tür Örneklemeleri Sağlama

sağlanan üyeleri (imzaları dizi türlerini, byref türlerini ve genel türlerin örneklemelerini içerir) normal MakeArrayType, MakePointerTypeve MakeGenericType dahil olmak üzere ProvidedTypeDefinitionsherhangi bir örneğinde Typekullanarak yaparsınız.

Not

Bazı durumlarda yardımcısını içinde ProvidedTypeBuilder.MakeGenericTypekullanmanız gerekebilir. Daha fazla ayrıntı için Tür Sağlayıcısı SDK'sı belgelerine bakın.

Ölçü Birimi Ek Açıklamaları Sağlama

ProvidedTypes API'sinde ölçü ek açıklamaları sağlamak için yardımcılar sağlanır. Örneğin, türünü float<kg>sağlamak için aşağıdaki kodu kullanın:

  let measures = ProvidedMeasureBuilder.Default
  let kg = measures.SI "kilogram"
  let m = measures.SI "meter"
  let float_kg = measures.AnnotateType(typeof<float>,[kg])

türünü Nullable<decimal<kg/m^2>>sağlamak için aşağıdaki kodu kullanın:

  let kgpm2 = measures.Ratio(kg, measures.Square m)
  let dkgpm2 = measures.AnnotateType(typeof<decimal>,[kgpm2])
  let nullableDecimal_kgpm2 = typedefof<System.Nullable<_>>.MakeGenericType [|dkgpm2 |]

Yerel Proje veya Betik Yerel Kaynaklarına Erişme

Tür sağlayıcısının her örneğine, inşaat sırasında bir TypeProviderConfig değer verilebilir. Bu değer sağlayıcının "çözümleme klasörünü" (derlemenin proje klasörü veya betik içeren dizin), başvuruda bulunılan derlemelerin listesini ve diğer bilgileri içerir.

Örneğin

Sağlayıcılar, F# dil hizmetine şema varsayımlarının değişmiş olabileceğini bildirmek için geçersizleştirme sinyalleri oluşturabilir. Geçersizleştirme oluştuğunda, sağlayıcı Visual Studio'da barındırılıyorsa tür denetimi yeniden yapılır. Sağlayıcı F# Etkileşimli'de veya F# Derleyicisi (fsc.exe) tarafından barındırıldığında bu sinyal yoksayılır.

Şema Bilgilerini Önbelleğe Alma

Sağlayıcılar genellikle şema bilgilerine erişimi önbelleğe almalıdır. Önbelleğe alınan veriler, statik parametre olarak veya kullanıcı verileri olarak verilen bir dosya adı kullanılarak depolanmalıdır. Şema önbelleğe alma örneği, derlemedeki tür sağlayıcılarındaki FSharp.Data.TypeProviders parametredirLocalSchemaFile. Bu sağlayıcıların uygulanmasında, bu statik parametre tür sağlayıcısını ağ üzerinden veri kaynağına erişmek yerine belirtilen yerel dosyadaki şema bilgilerini kullanmaya yönlendirir. Önbelleğe alınmış şema bilgilerini kullanmak için statik parametresini ForceUpdatefalseolarak da ayarlamanız gerekir. Çevrimiçi ve çevrimdışı veri erişimini etkinleştirmek için benzer bir teknik kullanabilirsiniz.

Yedekleme Derlemesi

Bir .dll veya .exe dosyasını derlediğinizde, oluşturulan türler için yedekleme .dll dosyası statik olarak sonuçta elde edilen derlemeye bağlanır. Bu bağlantı, Ara Dil (IL) türü tanımları ve yönetilen kaynaklar yedekleme derlemesinden son derlemeye kopyalanarak oluşturulur. F# Etkileşimli'yi kullandığınızda, yedekleme .dll dosyası kopyalanır ve bunun yerine doğrudan F# Etkileşimli işlemine yüklenir.

Tür Sağlayıcılarından Özel Durumlar ve Tanılamalar

Sağlanan türlerdeki tüm üyelerin tüm kullanımları özel durumlar oluşturabilir. Her durumda, bir tür sağlayıcısı özel durum oluşturursa, konak derleyicisi hatayı belirli bir tür sağlayıcısına bağlar.

  • Tür sağlayıcısı özel durumları hiçbir zaman iç derleyici hatalarına neden olmamalıdır.

  • Tür sağlayıcıları uyarıları bildiremez.

  • Bir tür sağlayıcısı F# derleyicisinde, bir F# geliştirme ortamında veya F# Etkileşimli'de barındırıldığında, bu sağlayıcının tüm özel durumları yakalanmış olur. İleti özelliği her zaman hata metnidir ve yığın izlemesi görüntülenmez. Özel durum oluşturacaksanız aşağıdaki örnekleri oluşturabilirsiniz: System.NotSupportedException, , System.IO.IOExceptionSystem.Exception.

Oluşturulan Türleri Sağlama

Şimdiye kadar, bu belgede silinen türlerin nasıl sağlandığı açıklanmıştır. Kullanıcıların programına gerçek .NET tür tanımları olarak eklenen oluşturulan türleri sağlamak için F# dilinde tür sağlayıcısı mekanizmasını da kullanabilirsiniz. Tür tanımı kullanarak oluşturulan sağlanan türlere başvurmanız gerekir.

open Microsoft.FSharp.TypeProviders

type Service = ODataService<"http://services.odata.org/Northwind/Northwind.svc/">

F# 3.0 sürümünün parçası olan ProvidedTypes-0.2 yardımcı kodu, oluşturulan türleri sağlamaya yönelik yalnızca sınırlı desteğe sahiptir. Oluşturulan tür tanımı için aşağıdaki deyimlerin true olması gerekir:

  • isErased olarak ayarlanmalıdır false.

  • Oluşturulan tür, oluşturulan kod parçaları için bir kapsayıcıyı temsil eden yeni oluşturulan ProvidedAssembly()öğesine eklenmelidir.

  • Sağlayıcının, diskte eşleşen bir .dll dosyasıyla gerçek bir yedekleme .NET .dll dosyası olan bir derlemesi olmalıdır.

Kurallar ve Sınırlamalar

Tür sağlayıcıları yazarken aşağıdaki kuralları ve sınırlamaları göz önünde bulundurun.

Sağlanan türlere ulaşılabilir olmalıdır

Sağlanan tüm türler iç içe olmayan türlerden erişilebilir olmalıdır. İç içe olmayan türler oluşturucuya yapılan çağrıda veya çağrısında TypeProviderForNamespacesAddNamespaceverilir. Örneğin, sağlayıcı bir tür StaticClass.P : Tsağlıyorsa, T'nin iç içe olmayan bir tür olduğundan veya bir türün altında iç içe olduğundan emin olmanız gerekir.

Örneğin, bazı sağlayıcıların bu T1, T2, T3, ... türleri içeren gibi DataTypes statik bir sınıfı vardır. Aksi takdirde hata, A derlemesindeki T türüne başvurunun bulunduğunu, ancak türün bu derlemede bulunamadığını belirtir. Bu hata görüntülenirse, tüm alt türlerinize sağlayıcı türlerinden ulaşılabildiğini doğrulayın. Not: Bu T1, T2, T3... türler, anında türler olarak adlandırılır. Bunları erişilebilir bir ad alanına veya üst türe eklemeyi unutmayın.

Tür Sağlayıcısı Mekanizmasının Sınırlamaları

F# içindeki tür sağlayıcısı mekanizması aşağıdaki sınırlamalara sahiptir:

  • F# dilindeki tür sağlayıcıları için temel alınan altyapı, sağlanan genel türleri veya sağlanan genel yöntemleri desteklemez.

  • Mekanizma, statik parametrelerle iç içe türleri desteklemez.

Geliştirme İpuçları

Geliştirme sürecinde aşağıdaki ipuçlarını yararlı bulabilirsiniz:

Visual Studio'nun iki örneğini çalıştırma

IDE testi, tür sağlayıcısının yeniden oluşturulmasını engelleyen .dll dosyasına kilitlendiği için bir örnekte tür sağlayıcısı geliştirebilir ve sağlayıcıyı diğerinde test edebilirsiniz. Bu nedenle, sağlayıcı ilk örnekte oluşturulurken Visual Studio'nun ikinci örneğini kapatmanız ve sağlayıcı oluşturulduktan sonra ikinci örneği yeniden açmanız gerekir.

fsc.exe çağrılarını kullanarak tür sağlayıcılarında hata ayıklama

Aşağıdaki araçları kullanarak tür sağlayıcılarını çağırabilirsiniz:

  • fsc.exe (F# komut satırı derleyicisi)

  • fsi.exe (F# Etkileşimli derleyicisi)

  • devenv.exe (Visual Studio)

Test betiği dosyasında (örneğin, script.fsx) fsc.exe kullanarak tür sağlayıcılarının hatalarını en kolay şekilde ayıklayabilirsiniz. Bir komut isteminden hata ayıklayıcı başlatabilirsiniz.

devenv /debugexe fsc.exe script.fsx

Yazdır-stdout günlüğünü kullanabilirsiniz.

Ayrıca bkz.