Sunday, 18 September 2011

Creating a no-thrills custom type provider in F# 3.0 – some progress

I thought I would come at this from a different angle today. Following on from comments on the previous post, I decided it would make sense to try and find out what F#/Visual Studio does when I add a reference to my type provider.

So I have added a separate solution TypeProviderConsumer and opened that in another instance of VS 11. I create an empty F# console app project, and then go back to my type provider solution. Having corrected the omission that Ramon mentioned in a comment on my previous post, my type provider now looks like this:

using System;
using System.Reflection;
using Microsoft.FSharp.Core;
using Microsoft.FSharp.Core.CompilerServices;
    
[Serializable, TypeProvider, CompilationMapping(SourceConstructFlags.ObjectType)]
public class BasicTypeProvider : ITypeProvider, IProvidedNamespace, IDisposable
{
    Type baseType;

    public BasicTypeProvider(TypeProviderConfig config)
    {
        baseType = typeof(StaticType);
    }

    public Type ApplyStaticArguments(Type typeWithoutArguments, string typeNameWithArguments, object[] staticArguments)
    {
        return baseType;
    }

    public System.Linq.Expressions.Expression GetInvokerExpression(System.Reflection.MethodBase syntheticMethodBase, System.Linq.Expressions.ParameterExpression[] parameters)
    {
        return null;
    }

    public IProvidedNamespace[] GetNamespaces()
    {
        return new IProvidedNamespace[] { (IProvidedNamespace) this };
    }

    public System.Reflection.ParameterInfo[] GetStaticParameters(Type typeWithoutArguments)
    {
        return new ParameterInfo[] { };            
    }

    public event EventHandler Invalidate;

    public void Dispose()
    {
    }

    public IProvidedNamespace[] GetNestedNamespaces()
    {
        return new IProvidedNamespace[] { };
    }

    public Type[] GetTypes()
    {
        return new Type[] { baseType };
    }

    public string NamespaceName
    {
        get { return "TestTypeProvider"; }
    }

    public Type ResolveTypeName(string typeName)
    {
        if (string.Equals(typeName, "StaticType"))
        {
            return this.baseType;
        }
        return null;
    }
}

I set breakpoints in every method and property above, then choose Debug, Attach to Process and attach to the other instance of Visual Studio:

image

Switching back to the other instance, I add a reference to my custom type provider, and bam, we hit the first breakpoint. Awesome. Progress.

image

Checking out what config gets passed into our constructor:

image

If I keep hitting F5, it hits the breakpoints in this order:

  1. GetNamespaces()
  2. NamespaceName { get; }
  3. GetTypes()
  4. GetNestedNamespaces()

Then it repeats once more, starting at the constructor.

This is all good news – it means that I’m not missing some strange attribute or other construct. My type provider is being initialised and queried, so I guess at the moment I get the “This is not a provided type” message because I’m not implementing some or all of ITypeProvider or IProvidedNamespace correctly. This is something I can work on by getting my head back into FSharp.Data.TypeProviders. Watch this space.

As an aside – notice the TemporaryFolder property in the config above. If I take a look into the location to which it points, I find a number of generated dlls for the past few days. It turns out these are generated by the Linq to SQL type provider (which I have been testing alongside this investigation) in pairs – one for the type that I generate in my F# code (dbSchema) and another which contains the actual DataContext class and classes for each table in my database. This is good – I understand some more about what is generated under the hood for type providers, and should help me with the next stage of the investigation.

5 comments:

Chris Ballard said...

Looks like Robert Pickering has got a little further than me - see his latest post

Chris Ballard said...

Also this question on StackOverflow

Santiago said...

Hi Chris, I have some additional progress in a C# implementation.

Following Robert Pickering example, I have coded a Type derived custom class. Now, the F# intellisense is aware of my provided type and its properties.

But the compiler marks the properties references with this error:
"The type provider 'SER.TRIAL.TrialTypeProvider' reported an error: Value cannot be null."

I have marked all posible breakpoints in my type provider, provided namespace and custom type,

The debugger stops in some breakpoints until the line in error is marked. After that, there's no stop point in muy code.

Any idea?

Thanks

Santiago said...

Sorry, the complete error message is:


"The type provider 'SER.TRIAL.TrialTypeProvider' reported an error: Value cannot be null. Parameter name: types"

Santiago said...

Now I have a working c# type provider example.

The provided type is instantiated and I can set and get a property values (the error was due a namespace mismatch between the base type and the provided type).

I believe this post ( http://www.mindscapehq.com/blog/index.php/2011/09/19/f-type-providers-as-if-by-magic/ ) is a useful guide to continue.

Thanks