SharePoint Reference Document

I updated the reference document for SharePoint that I first mentioned in a previous post.

Right now, I have the following lists:

  • Fields: all the built-in SharePoint field types;
  • Content types;
  • List templates;
  • Site templates;
  • SPDataSource returned fields;
  • List fields;
  • Document library fields;
  • Search content classes.

You can find it in Excel format here: https://mscblogs.blob.core.windows.net/media/ricardoperes/Documents/Reference.xlsx.

If you spot anything wrong, please let me know!

Silverlight DynamicResource Markup Extension

This will be my first post on Silverlight!

WPF offers two markup extensions, StaticResource and DynamicResource, that can be used to set property values from items in resource dictionaries.

The difference between the two can be summarized as:

  • StaticResource is evaluated at the start of the application, and will find resources defined before its actual usage;
  • DynamicResource is evaluated at a later point, and will find resources declared anywhere, before or after its usage.

Unfortunately, Silverlight does not include DynamicResource, but it is relatively easy to achieve something like that; that is the purpose of this post.

Check out the following code:

public class DynamicResource : MarkupExtension

{

    public DynamicResource()

    {

    }


    public String ResourceName { get; set; }


    public override Object ProvideValue(IServiceProvider serviceProvider)

    {

        var provideValueTarget = serviceProvider.GetService<IProvideValueTarget>();

        var target = provideValueTarget.TargetObject as FrameworkElement;

        var property = provideValueTarget.TargetProperty as PropertyInfo;


        if (target != null)

        {

            RoutedEventHandler handler = null;

            handler = (sender, e) =>

            {

                var elm = sender as FrameworkElement;


                if (elm != null)

                {

                    var resource = TryFindResource(elm, this.ResourceName);

                    var typeConverterAttribute = property.GetCustomAttributes(typeof(TypeConverterAttribute), true).OfType<TypeConverterAttribute>().SingleOrDefault() ?? property.PropertyType.GetCustomAttributes(typeof(TypeConverterAttribute), true).OfType<TypeConverterAttribute>().SingleOrDefault();


                    if (typeConverterAttribute != null)

                    {

                        var typeConverterType = Type.GetType(typeConverterAttribute.ConverterTypeName, false);


                        if (typeConverterType != null)

                        {

                            var typeConverter = Activator.CreateInstance(typeConverterType) as TypeConverter;


                            if (typeConverter != null)

                            {

                                resource = typeConverter.ConvertFrom(resource);

                            }

                        }

                    }


                    property.SetValue(sender, resource, null);

                }


                target.Loaded -= handler;

            };


            target.Loaded += handler;

        }


        return (property.PropertyType.IsClass == true) ? null : Activator.CreateInstance(property.PropertyType);

    }


    private static Object TryFindResource(FrameworkElement element, Object resourceKey)

    {

        var currentElement = element;


        while (currentElement != null)

        {

            var resource = currentElement.Resources[resourceKey];

            if (resource != null)

            {

                return resource;

            }


            currentElement = currentElement.Parent as FrameworkElement;

        }


        return Application.Current.Resources[resourceKey];

    }

}

I won’t go into details on markup extensions and all that, but basically, this one hooks to the Loaded event of the FrameworkElement, thus deferring the setting of the property. The property and the target are found through the IProvideValueTarget instance and the resource is looked up recursively from the target element up to the Application instance. If the property to be set, or its class, defines a type converter, it will try to convert the found resource to the proper type.  In the meantime, it just returns the default value of the property type, null or a default instance, in case of a value type.

MVP Showcase 2015 – Wrap Up

(This post is in portuguese and english)

Well, the MVP Showcase 2015 event is over! After all the excitement, it’s nice to calm down, but I am already missing all these extraordinarily gifted people and all the technical hype! Smile

I couldn’t attend all the sessions that I wanted, because there were always two in parallel, but I must say that I was very impressed with the overall quality of the presentations! Well done, guys!

For those who might be interested, here are the slides for my presentation, “Entity Framework 7: NoORM” (in english). As always, I’d love to hear your feedback!

Once again, thanks to all our sponsors, and to all attended the event!


Bem, o MVP Showcase 2015 acabou! Depois de toda a excitação, é bom ter um bocado de sossego, mas já estou a sentir saudades de toda aquela gente tão extraordinariamente dotada e de toda a envolvente técnica! Smile

Não pude assistir a todas as sessões que gostaria, porque havia sempre duas em simultâneo, mas devo dizer que fiquei muito impressionado com a qualidade geral das apresentações! Bom trabalho, pessoal!

Para aqueles que possam estar interessados, aqui estão os slides da minha apresentação, “Entity Framework 7: NoORM” (em inglês). Como sempre, adorava ouvir o vosso feedback!

Mais uma vez, o meu obrigado aos nossos patrocinadores, e a todos os que assistiram ao evento!

MVP Showcase 2015

UPDATE: you can find the slide deck here.

(This post is in Portuguese and in English)

As I mentioned earlier, the Portuguese MVPs are organizing the MVP Showcase 2015 event, this Wednesday, April 22nd, in Lisbon, at Microsoft’s premises.

I will be presenting on Entity Framework 7, and the slide deck will be available here after the event. Besides myself, the other presenters will be:

The keynote will come from Cristina Gonzalez Herrero (@crisgherrero), the head of the MVP program for Portugal, Spain and Italy.

Looking forward to seeing you all there!


Como disse antes, os MVPs portugueses estão a organizar o evento MVP Showcase 2015, na próxima quarta-feira, 22 de Abril, em Lisboa, nas instalações da Microsoft.

Eu vou fazer uma apresentação sobre o Entity Framework 7, e os slides irão estar disponíveis aqui após o evento. Além de mim, os outros apresentadores serão:

A apresentação será feita pela Cristina Gonzalez Herrero (@crisgherrero), a responsável do programa MVP para Portugal, Espanha e Itália.

Espero ver-vos todos por lá!

Lesser-Known NHibernate Features: Custom Loggers

Extensible as it is, it’s no wonder that NHibernate also supports injecting custom loggers. The support is twofold:

  • There’s the ILoggerFactory interface, that must be implemented by custom logger factories; this is the responsible for creating actual loggers;
  • Then there’s the IInternalLogger interface, the one that provides the common functions found in most loggers (warn, info, debug, error, fatal, etc).

By default, it uses Log4Net, but it is easy to add our own logger.

So, let’s start by implementing a logger factory – just the skeleton, I leave it to you as an exercise:

public class CustomLoggerFactory : ILoggerFactory

{

    public IInternalLogger LoggerFor(Type type)

    {

        return new CustomLogger(type.FullName);

    }

 

    public IInternalLogger LoggerFor(String keyName)

    {

        return new CustomLogger(keyName);

    }

}

And then the actual logger:

public class CustomLogger : IInternalLogger

{

    public String Key { get; private set; }

 

    public CustomLogger(String key)

    {

        this.Key = key;

    }

 

    public void Debug(Object message, Exception exception)

    {

    }

 

    public void Debug(Object message)

    {

    }

 

    public void DebugFormat(String format, params Object[] args)

    {

    }

 

    public void Error(Object message, Exception exception)

    {

    }

 

    public void Error(Object message)

    {

    }

 

    public void ErrorFormat(String format, params Object[] args)

    {

    }

 

    public void Fatal(Object message, Exception exception)

    {

    }

 

    public void Fatal(Object message)

    {

    }

 

    public void Info(Object message, Exception exception)

    {

    }

 

    public void Info(Object message)

    {

    }

 

    public void InfoFormat(String format, params Object[] args)

    {

    }

 

    public Boolean IsDebugEnabled

    {

        get { return true; }

    }

 

    public Boolean IsErrorEnabled

    {

        get { return true; }

    }

 

    public Boolean IsFatalEnabled

    {

        get { return true; }

    }

 

    public Boolean IsInfoEnabled

    {

        get { return true; }

    }

 

    public Boolean IsWarnEnabled

    {

        get { return true; }

    }

 

    public void Warn(Object message, Exception exception)

    {

    }

 

    public void Warn(Object message)

    {

    }

 

    public void WarnFormat(String format, params Object[] args)

    {

    }

}

Remember, this is just a skeleton, do implement these methods anyway you like.

Now, all that is left is the registration:

LoggerProvider.SetLoggersFactory(new CustomLoggerFactory());

And NHibernate will start logging using your logger!

PS – Issue NH-3776 is an attempt to make using logger factories even simpler and in a similar fashion to other pluggable features.

SharePoint XSLT Web Part

After my previous post on XSLT processing, what else could follow? Of course, an XSLT web part for SharePoint! Smile

Here I want to solve a couple of problems:

  • Allow the usage of XSLT 2.0;
  • Have a more flexible parameter passing mechanism than <ParameterBindings>;
  • Make the XSLT extension mechanism (parameters, functions) more usable.

Similar to XsltListViewWebPart and the others, this web part will query SharePoint and return the results processed by a XSLT style sheet. I am going to built on top of the classes introduced in the last post. Here is the SPCustomXsltWebPart (please, do give it a better name…):

public enum XsltVersion

{

    Xslt1 = 1,

    Xslt2 = 2

}

 

public class SPCustomXsltWebPart : WebPart, IWebPartTable

{

    private static readonly Regex parametersRegex = new Regex(@"@(\w+)\b", RegexOptions.IgnoreCase);

 

    [NonSerialized]

    private DataTable table;

    [NonSerialized]

    private IOrderedDictionary parameters;

 

    public SPCustomXsltWebPart()

    {

        this.AddDefaultExtensions = true;

        this.RowLimit = Int32.MaxValue;

        this.Parameters = new ParameterCollection();

        this.XsltVersion = XsltVersion.Xslt1;

    }

 

    [Category("XSLT")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("XSL Version")]

    [WebDescription("The XSLT version")]

    [DefaultValue(XsltVersion.Xslt1)]

    public XsltVersion XsltVersion { get; set; }

 

    [Category("XSLT")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("XSL Link")]

    [WebDescription("The URL of a file containing XSLT")]

    [DefaultValue("")]

    public String XslLink { get; set; }

 

    [Category("XSLT")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("XSL")]

    [WebDescription("The XSLT content")]

    [DefaultValue("")]

    [PersistenceMode(PersistenceMode.InnerProperty)]

    public String Xsl { get; set; }

 

    [Category("Query")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("Query")]

    [WebDescription("The CAML query")]

    [DefaultValue("")]

    [PersistenceMode(PersistenceMode.InnerProperty)]

    public String Query { get; set; }

 

    [Category("Query")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("Row Limit")]

    [WebDescription("The row limit")]

    [DefaultValue(Int32.MaxValue)]

    public UInt32 RowLimit { get; set; }

 

    [Category("Query")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("Lists")]

    [WebDescription("The target lists")]

    [DefaultValue("")]

    public String Lists { get; set; }

 

    [Category("Query")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("Webs")]

    [WebDescription("The target webs")]

    [DefaultValue("")]

    public String Webs { get; set; }

 

    [Category("Query")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("View Fields")]

    [WebDescription("The view fields")]

    [DefaultValue("")]

    public String ViewFields { get; set; }

 

    [Category("Query")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("Query Throttle Mode")]

    [WebDescription("The query throttle mode")]

    [DefaultValue(SPQueryThrottleOption.Default)]

    public SPQueryThrottleOption QueryThrottleMode { get; set; }

 

    [Category("General")]

    [Personalizable(PersonalizationScope.Shared)]

    [WebBrowsable(true)]

    [WebDisplayName("Add Default Extensions")]

    [WebDescription("Adds the default extensions")]

    [DefaultValue(true)]

    public Boolean AddDefaultExtensions { get; set; }

 

    [PersistenceModeAttribute(PersistenceMode.InnerProperty)]

    public ParameterCollection Parameters { get; private set; }

 

    public event EventHandler<XsltExtensionEventArgs> XsltExtension;

 

    protected XsltProvider XsltProvider

    {

        get

        {

            return this.XsltVersion == XsltVersion.Xslt1 ? DefaultXsltProvider.Instance : SaxonXsltProvider.Instance;

        }

    }

    protected virtual void OnXsltExtension(XsltExtensionEventArgs e)

    {

        var handler = this.XsltExtension;

 

        if (handler != null)

        {

            handler(this, e);

        }

    }

 

    protected override void CreateChildControls()

    {

        var xml = this.GetXml();

        var html = this.Render(xml);

        var literal = new LiteralControl(html);

 

        this.Controls.Add(literal);

 

        base.CreateChildControls();

    }

 

    private String GetXslt()

    {

        var xslt = String.Empty;

 

        if (String.IsNullOrWhiteSpace(this.Xsl) == false)

        {

            xslt = this.Xsl;

        }

        else if (String.IsNullOrWhiteSpace(this.XslLink) == false)

        {

            var doc = new XmlDocument();

            doc.Load(this.XslLink);

 

            xslt = doc.InnerXml;

        }

 

        return xslt;

    }

 

    private DataTable GetTable()

    {

        if (this.table == null)

        {

            var query = new SPSiteDataQuery();

            query.Query = this.ApplyParameters(this.Query);

            query.QueryThrottleMode = this.QueryThrottleMode;

            query.RowLimit = this.RowLimit;

            query.Lists = this.Lists;

            query.Webs = this.Webs;

            query.ViewFields = this.ViewFields;

 

            this.table = SPContext.Current.Site.RootWeb.GetSiteData(query);

            this.table.TableName = "Row";

 

            foreach (var column in this.table.Columns.OfType<DataColumn>())

            {

                column.ColumnMapping = MappingType.Attribute;

            }

        }

 

        return this.table;

    }

 

    private String ApplyParameters(String value)

    {

        var parameters = this.GetParameters();

 

        value = parametersRegex.Replace(value, x => this.GetFormattedValue(parameters[x.Value.Substring(1)]));

 

        return value;

    }

 

    private String GetFormattedValue(Object value)

    {

        if (value == null)

        {

            return String.Empty;

        }

 

        if (value is Enum)

        {

            return ((Int32)value).ToString();

        }

 

        if (value is DateTime)

        {

            return SPUtility.CreateISO8601DateTimeFromSystemDateTime((DateTime)value);

        }

 

        if (value is IFormattable)

        {

            return (value as IFormattable).ToString(String.Empty, CultureInfo.InvariantCulture);

        }

 

        return value.ToString();

    }

 

    private IOrderedDictionary GetParameters()

    {

        if (this.parameters == null)

        {

            this.parameters = this.Parameters.GetValues(this.Context, this);

        }

 

        return this.parameters;

    }

 

    private String GetXml()

    {

        var sb = new StringBuilder();

        var table = this.GetTable();

 

        using (var writer = new StringWriter(sb))

        {

            table.WriteXml(writer);

        }

 

        sb

            .Replace("<DocumentElement>", "<dsQueryResponse RowLimit='" + this.RowLimit + "'><Rows>")

            .Replace("</DocumentElement>", "</Rows></dsQueryResponse>");

 

        return sb.ToString();

    }

 

    private String ApplyXslt(String xml, String xslt, XsltExtensionEventArgs args)

    {

        return this.XsltProvider.Transform(xml, xslt, args);

    }

 

    private String Render(String xml)

    {

        if (String.IsNullOrWhiteSpace(xml) == true)

        {

            return String.Empty;

        }

 

        var xslt = this.GetXslt();

 

        if (String.IsNullOrWhiteSpace(xslt) == true)

        {

            return String.Empty;

        }

 

        var extensions = new XsltExtensionEventArgs();

 

        this.OnXsltExtension(extensions);

 

        if (this.AddDefaultExtensions == true)

        {

            var defaultExtensions = Activator.CreateInstance(typeof(Microsoft.SharePoint.WebPartPages.DataFormWebPart).Assembly.GetType("Microsoft.SharePoint.WebPartPages.DataFormDdwRuntime"));

            extensions.AddExtension("http://schemas.microsoft.com/WebParts/v2/DataView/runtime", defaultExtensions);

        }

 

        foreach (var ext in extensions.Extensions)

        {

            extensions.AddExtension(ext.Key, ext.Value);

        }

 

        var parameters = this.GetParameters();

 

        foreach (var key in parameters.Keys.OfType<String>())

        {

            extensions.AddParameter(key, String.Empty, parameters[key].ToString());

        }

 

        foreach (var param in extensions.Parameters)

        {

            extensions.AddParameter(param.Name, param.NamespaceUri, param.Parameter.ToString());

        }

 

        return this.ApplyXslt(xml, xslt, extensions);

    }

 

    void IWebPartTable.GetTableData(TableCallback callback)

    {

        callback(this.GetTable().DefaultView);

    }

 

    PropertyDescriptorCollection IWebPartTable.Schema

    {

        get { return TypeDescriptor.GetProperties(this.GetTable().DefaultView); }

    }

}

This class extends the basic WebPart class and adds a couple of properties:

  • XsltVersion: the XSLT version to use, which will result in either my DefaultXsltProvider or the SaxonXsltProvider being used;
  • XslLink: the URL of a file containing XSLT;
  • Xsl: in case you prefer to have the XSLT inline;
  • Query: a CAML query;
  • Webs: the webs to query;
  • Lists: the lists to query;
  • ViewFields: the fields to return;
  • RowLimit: maximum number of rows to return;
  • QueryThrottleMode: the query throttle mode;
  • AddDefaultExtensions: whether to add the default extension functions and parameters;
  • Parameters: a standard collection of ASP.NET parameter controls.

The web part uses SPSiteDataQuery to execute a CAML query. Before the query is executed, any parameters it may have, in the form @ParameterName,  are replaced by actual values evaluated from the Parameters collection. This gives some flexibility to the queries, because, not only ASP.NET includes parameters for all the common sources, it’s very easy to add new ones. The web part knows how to format strings, enumerations, DateTime objects and in general any object implementing IFormattable; if you wish, you can extend it to support other types, but I don’t think it will be necessary.

An example usage:

<web:SPCustomXsltWebPart runat="server" XslLink="~/Style.xslt">

    <Query>

        <Where><Eq><FieldRef Name='Id'/><Value Type='Number'>@Id</Value></Eq></Where>

    </Query>

    <Parameters>

        <asp:QueryStringParameter Name="Id" QueryStringField="Id" Type="Int32" />

    </Parameters>

</web:SPCustomXsltWebPart>

Notice that one of the Xsl or the XslLink properties must be set, and the same goes for the Query.

Hope you find this useful, and let me know how it works!

     

    XSLT Processing in .NET

    God only knows why, but the .NET framework only includes support for XSLT 1.0. This makes it difficult, but not impossible, to use the more recent 2.0 version: a number of external libraries exist that can help us achieve that.

    I wanted to make this easier for us developers, namely:

    1. To have a common interface for abstracting typical functionality – transform some XML with some XSLT;
    2. Be able to inject parameters;
    3. Be able to inject custom extension functions.

    I first set out to define my base API:

    [Serializable]

    public abstract class XsltProvider

    {

        public abstract String Transform(String xml, String xslt, XsltExtensionEventArgs args);

        public abstract Single Version { get; }

    }

     

    [Serializable]

    public sealed class XsltExtensionEventArgs

    {

        public XsltExtensionEventArgs()

        {

            this.Extensions = new Dictionary<String, Object>();

            this.Parameters = new HashSet<XsltParameter>();

        }

     

        public IDictionary<String, Object> Extensions

        {

            get;

            private set;

        }

     

        public ISet<XsltParameter> Parameters

        {

            get;

            private set;

        }

     

        public XsltExtensionEventArgs AddExtension(String @namespace, Object extension)

        {

            this.Extensions[@namespace] = extension;

            return this;

        }

     

        public XsltExtensionEventArgs AddParameter(String name, String namespaceUri, String parameter)

        {

            this.Parameters.Add(new XsltParameter(name, namespaceUri, parameter));

            return this;

        }

    }

    The XsltProvider class only defines a version and a transformation method. This transformation method receives an XML and an XSLT parameters and also an optional collection of extension methods and parameters. There’s a singleton instance to make it easier to use, since this class really has no state.

    An implementation using .NET’s built-in classes, for XSLT 1.0, is trivial:

    [Serializable]

    public sealed class DefaultXsltProvider : XsltProvider

    {

        public static readonly XsltProvider Instance = new DefaultXsltProvider();

     

        public override Single Version

        {

            get { return 1F; }

        }

     

        public override String Transform(String xml, String xslt, XsltExtensionEventArgs args)

        {

            using (var stylesheet = new XmlTextReader(xslt, XmlNodeType.Document, null))

            {

                var arg = new XsltArgumentList();

     

                foreach (var key in args.Extensions.Keys)

                {

                    arg.AddExtensionObject(key, args.Extensions[key]);

                }

     

                foreach (var param in args.Parameters)

                {

                    arg.AddParam(param.Name, param.NamespaceUri, param.Parameter);

                }

     

                var doc = new XmlDocument();

                doc.LoadXml(xml);

     

                var transform = new XslCompiledTransform();

                transform.Load(stylesheet);

     

                var sb = new StringBuilder();

     

                using (var writer = new StringWriter(sb))

                {

                    var results = new XmlTextWriter(writer);

     

                    transform.Transform(doc, arg, results);

     

                    return sb.ToString();

                }

            }

        }

    }

    For XSLT 2.0, we have a number of options. I ended up using Saxon-HE, an open-source and very popular library for .NET and Java. I installed it through NuGet:

    image

    Here is a possible implementation on top of my base class:

    [Serializable]

    public sealed class SaxonXsltProvider : XsltProvider

    {

        public static readonly XsltProvider Instance = new SaxonXsltProvider();

     

        public override Single Version

        {

            get { return 2F; }

        }

     

        public override String Transform(String xml, String xslt, XsltExtensionEventArgs args)

        {

            var processor = new Processor();

     

            foreach (var key in args.Extensions.Keys)

            {

                foreach (var function in this.CreateExtensionFunctions(args.Extensions[key], key))

                {

                    processor.RegisterExtensionFunction(function);

                }

            }

     

            var document = new XmlDocument();

            document.LoadXml(xslt);

     

            var input = processor.NewDocumentBuilder().Build(document);

     

            var xsltCompiler = processor.NewXsltCompiler();

     

            var xsltExecutable = xsltCompiler.Compile(input);

     

            var xsltTransformer = xsltExecutable.Load();

     

            foreach (var parameter in args.Parameters)

            {

                xsltTransformer.SetParameter(new QName(String.Empty, parameter.NamespaceUri, parameter.Name), CustomExtensionFunctionDefinition.GetValue(parameter.Parameter));

            }

     

            using (var transformedXmlStream = new MemoryStream())

            {

                var dataSerializer = processor.NewSerializer(transformedXmlStream);

     

                xsltTransformer.InputXmlResolver = null;

                xsltTransformer.InitialContextNode = processor.NewDocumentBuilder().Build(input);

                xsltTransformer.Run(dataSerializer);

     

                var result = Encoding.Default.GetString(transformedXmlStream.ToArray());

     

                return result;

            }

        }

     

        private IEnumerable<ExtensionFunctionDefinition> CreateExtensionFunctions(Object extension, String namespaceURI)

        {

            foreach (var method in extension.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static).Where(x => x.IsAbstract == false))

            {

                yield return new CustomExtensionFunctionDefinition(method, namespaceURI, extension);

            }

        }

     

        class CustomExtensionFunctionDefinition : ExtensionFunctionDefinition

        {

            private readonly String namespaceURI;

            private readonly MethodInfo method;

            private readonly Object target;

     

            public CustomExtensionFunctionDefinition(MethodInfo method, String namespaceURI, Object target)

            {

                this.method = method;

                this.namespaceURI = namespaceURI;

                this.target = target;

            }

     

            public override XdmSequenceType[] ArgumentTypes

            {

                get

                {

                    return this.method.GetParameters().Select(x => this.GetArgumentType(x)).ToArray();

                }

            }

     

            private XdmSequenceType GetArgumentType(ParameterInfo parameter)

            {

                return new XdmSequenceType(GetValueType(parameter.ParameterType), XdmSequenceType.ONE);

            }

     

            internal static XdmAtomicValue GetValue(Object value)

            {

                if (value is String)

                {

                    return new XdmAtomicValue(value.ToString());

                }

     

                if ((value is Int32) || (value is Int32))

                {

                    return new XdmAtomicValue(Convert.ToInt64(value));

                }

     

                if (value is Boolean)

                {

                    return new XdmAtomicValue((Boolean)value);

                }

     

                if (value is Single)

                {

                    return new XdmAtomicValue((Single)value);

                }

     

                if (value is Double)

                {

                    return new XdmAtomicValue((Double)value);

                }

     

                if (value is Decimal)

                {

                    return new XdmAtomicValue((Decimal)value);

                }

     

                if (value is Uri)

                {

                    return new XdmAtomicValue((Uri)value);

                }

     

                throw new ArgumentException("Invalid value type.", "value");

            }

     

            internal static XdmAtomicType GetValueType(Type type)

            {

                if (type == typeof(Int32) || type == typeof(Int64) || type == typeof(Int16))

                {

                    return XdmAtomicType.BuiltInAtomicType(QName.XS_INTEGER);

                }

     

                if (type == typeof(Boolean))

                {

                    return XdmAtomicType.BuiltInAtomicType(QName.XS_BOOLEAN);

                }

     

                if (type == typeof(String))

                {

                    return XdmAtomicType.BuiltInAtomicType(QName.XS_STRING);

                }

     

                if (type == typeof(Single))

                {

                    return XdmAtomicType.BuiltInAtomicType(QName.XS_FLOAT);

                }

     

                if (type == typeof(Double))

                {

                    return XdmAtomicType.BuiltInAtomicType(QName.XS_DOUBLE);

                }

     

                if (type == typeof(Decimal))

                {

                    return XdmAtomicType.BuiltInAtomicType(QName.XS_DECIMAL);

                }

     

                if (type == typeof(Uri))

                {

                    return XdmAtomicType.BuiltInAtomicType(QName.XS_ANYURI);

                }

     

                throw new ArgumentException("Invalid value type.", "value");

            }

     

            public override QName FunctionName

            {

                get { return new QName(String.Empty, this.namespaceURI, this.method.Name); }

            }

     

            public override ExtensionFunctionCall MakeFunctionCall()

            {

                return new CustomExtensionFunctionCall(this.method, this.target);

            }

     

            public override Int32 MaximumNumberOfArguments

            {

                get { return this.method.GetParameters().Length; }

            }

     

            public override Int32 MinimumNumberOfArguments

            {

                get { return this.method.GetParameters().Count(p => p.HasDefaultValue == false); }

            }

     

            public override XdmSequenceType ResultType(XdmSequenceType[] argumentTypes)

            {

                return new XdmSequenceType(GetValueType(this.method.ReturnType), XdmSequenceType.ONE);

            }

        }

     

        class CustomExtensionFunctionCall : ExtensionFunctionCall

        {

            private readonly MethodInfo method;

            private readonly Object target;

     

            public CustomExtensionFunctionCall(MethodInfo method, Object target)

            {

                this.method = method;

                this.target = target;

            }

     

            public override IXdmEnumerator Call(IXdmEnumerator[] arguments, DynamicContext context)

            {

                var args = new List<Object>();

     

                foreach (var arg in arguments)

                {

                    var next = arg.MoveNext();

                    var current = arg.Current as XdmAtomicValue;

                    args.Add(current.Value);

                }

     

                var result = this.method.Invoke(this.target, args.ToArray());

     

                var value = CustomExtensionFunctionDefinition.GetValue(result);

                return value.GetEnumerator() as IXdmEnumerator;

            }

        }

    }

    You can see that this required considerable more effort. I use reflection to find all arguments and the return type of the passed extension objects and I convert them to the proper types that Saxon expects.

    A simple usage might be:

    //sample class containing utility functions

    class Utils

    {

        public int Length(string s)

        {

            //just a basic example

            return s.Length;

        }

    }

     

    var provider = SaxonXsltProvider.Instance;

     

    var arg = new XsltExtensionEventArgs()

        .AddExtension("urn:utils", new Utils())

        .AddParameter("MyParam", String.Empty, "My Value");

     

    var xslt = "<?xml version='1.0'?><xsl:transform version='2.0' xmlns:utils='urn:utils' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'><xsl:template match='/'>Length: <xsl:value-of select='utils:Length(\"Some Text\")'/> My Parameter: <xsl:value-of select='@MyParam'/></xsl:template></xsl:transform>";

    var xml = "<?xml version='1.0'?><My><SampleContents/></My>";

     

    var result = provider.Transform(xml, xslt, arg);

    Here I register an extension object with a public function taking one parameter and also a global parameter. In the XSLT I output this parameter and also the results of the invocation of a method in the extension object. The namespace of the custom function (Length) must match the one registered in the XsltExtensionEventArgs instance.

    As always, hope you find this useful! Winking smile

    MVP Showcase Portugal 2015

    (Portuguese only, sorry!)

    É com muito gosto que anuncio o evento MVP Showcase Portugal 2015!

    Organizado pela comunidade de MVPs portugueses, irá ocorrer dia 22 de Abril, nas instalações da Microsoft Portugal, e tem como objectivos:

    • Apresentar o programa MVP da Microsoft e explicar como os seus membros podem ajudar as empresas e mesmo developers individuais;
    • Promover as várias comunidades de developers portuguesas;
    • Realizar apresentações sobre várias tecnologias Microsoft, por especialistas nessas mesmas tecnologias;
    • Networking, networking, networking! Winking smile

    Cada sessão terá entre 30 e 45 minutos, e entre as quais haverá espaço para confraternizar com os MVPs e colocar questões: Ask The Experts.

    A participação é gratuita mas sujeita a inscrição em http://mvpshowcase2015.eventbrite.pt.

    Podem encontrar mais detalhes sobre o evento em http://msmvppt.org, incluindo a agenda detalhada.

    Estamos no Facebook em https://www.facebook.com/MVPpt, no Twitter em @mvpportugal e respondemos a quaisquer questões que tenham no email msmvp@outlook.pt.

    Os nossos patrocinadores, que tornaram este evento possível, são:

    patrocinadores

    Esperamos por vocês lá! Winking smile

    Lesser-Known NHibernate Features: Mapping By Attributes

    Some O/RMs do their mapping based on attributes. LINQ to SQL and Entity Framework are good examples, although Entity Framework also supports mapping by code. I’m not saying this is good or bad – some people think it “pollutes” POCOs, other think it makes them easier to understand -, but, likewise, NHibernate also allows to map entities by attributes, let’s see how.

    First, add a reference to the NHibernate.Mapping.Attributes NuGet package:

    image

    Next, go to your entity and start adding some attributes:

    [NHibernate.Mapping.Attributes.Class(Table = "blog", Lazy = true)]

    public class Blog

    {

        public Blog()

        {

            this.Posts = new List<Post>();

        }

    
    

        [NHibernate.Mapping.Attributes.Id(0, Column = "blog_id", Name = "BlogId")]

        [NHibernate.Mapping.Attributes.Generator(1, Class = "hilo")]

        public virtual Int32 BlogId { get; set; }

    
    

        [NHibernate.Mapping.Attributes.Property(Name = "Picture", Column = "picture", NotNull = false, TypeType = typeof(ImageUserType), Lazy = true)]

        public virtual Image Picture { get; set; }

    
    

        [NHibernate.Mapping.Attributes.Property(Name = "PostCount", Formula = "(SELECT COUNT(1) FROM post WHERE post.blog_id = blog_id)")]

        public virtual Int64 PostCount { get; protected set; }

    
    

        [NHibernate.Mapping.Attributes.ManyToOne(0, Column = "user_id", NotNull = true, Lazy = NHibernate.Mapping.Attributes.Laziness.NoProxy, Name = "Owner", Cascade = "save-update")]

        [NHibernate.Mapping.Attributes.Key(1)]

        public virtual User Owner { get; set; }

    
    

        [NHibernate.Mapping.Attributes.Property(Name = "Name", Column = "name", NotNull = true, Length = 50)]

        public virtual String Name { get; set; }

    
    

        [NHibernate.Mapping.Attributes.Property(Name = "Creation", Column = "creation", NotNull = true)]

        public virtual DateTime Creation { get; set; }

    
    

        [NHibernate.Mapping.Attributes.List(0, Name = "Posts", Cascade = "all-delete-orphan", Lazy = NHibernate.Mapping.Attributes.CollectionLazy.True, Inverse = true, Generic = true)]

        [NHibernate.Mapping.Attributes.Key(1, Column = "blog_id", NotNull = true)]

        [NHibernate.Mapping.Attributes.Index(2, Column = "number")]

        [NHibernate.Mapping.Attributes.OneToMany(3, ClassType = typeof(Post))]

        public virtual IList<Post> Posts { get; protected set; }

    }

    Basically, you will use:

    • ClassAttribute for marking a class as an entity;
    • IdAttribute: for declaring the id property;
    • GeneratorAttribute: for the id generator strategy;
    • PropertyAttribute: a mapped property;
    • BagAttribute/ListAttribute/SetAttribute/ArrayAttribute/etc: kinds of collections;
    • KeyAttribute: the key in a relation;
    • ElementAttribute: an element of an indexed collection;
    • IndexAttribute: the index in an indexed collection;
    • OneToManyAttribute/ManyToOneAttribute/OneToOneAttribute/ManyToManyAttribute: an endpoint property.

    All these attributes live in the NHibernate.Mapping.Attributes namespace and are named very closely to the equivalent HBM.XML elements, which means they must be ordered (notice the first number on some attributes). Now, in order to actually make sense of these attributes, you need to:

    var cfg = new Configuration();

    var serializer = new HbmSerializer() { Validate = true };

    
    

    using (var stream = serializer.Serialize(assemblyContainingEntitiesWithAttributes))

    {

        cfg.AddInputStream(stream);

    }

    And that’s all it takes! Have fun! Winking smile

    Visual Studio Tips 2

    Update: see the third post here.

    OK, continuing with my previous post, here are some more Visual Studio tips, this time, with a lot more pictures! Winking smile

    1. Start several projects simultaneously: when it comes to starting a project in a solution, there are three options:
      1. Start a single, fixed, project;
      2. Start the project where the focus is currently on;
      3. Start multiple projects simultaneously.

      Right click on the solution – PropertiesCommon Projects Startup Project and select Multiple startup projects:

      image

    2. Break on exceptions: Visual Studio can break automatically on first-chance exceptions (ones that may be caught afterwards). This is useful, because if you have catch blocks, you might not even notice them. First-chance exception breaking can be defined on a class per class basis. First, click on DebugExceptions:image

      Then, check the ones that you are interested in:

      image

    3. Conditional breakpoints: you can enable breakpoints based on some condition on the code. First, create a breakpoint, right click in it, then click on Condition:image

      Now you can add any condition you want, either based on local or global variables and methods – watch out for any possible side effects!

      image

      Another option is to only break when the hit count – the number of times the program has processed the instruction – has reached some value. Click on Hit Count and select the right condition, break always is the default:

      image

      Yet another option is to add a filter. This is a limited condition, which can only take a couple of variables. Activate it by clicking on Filter:

      image

    4. Tracepoints: in the Ultimate edition of Visual Studio (sorry, folks that to not have it!) you can send output to the trace window without actually writing any code. How many times did you add a Trace.WriteLine call, or something similar, for this purpose? Create a breakpoint, then click When Hit:image

      Now, add an output string, which can take variables, parameters or properties, plus some special tokens:

      image

      image

    5. Showing or hiding items from Output window: while debugging your application, the Output windows tends to be cluttered with all sorts of messages. Well, you can hide some of them. Just right click on the Output window and check out those that you want to hide:image
    6. Add links: in a project, you can add physical files, which are copied to the project’s folder, or links, which are kept in their original locations. Select AddExisting Item, navigate to the folder containing the file you want to add, select it, but instead of clicking Add, select Add As Link from the drop down:image
    7. Save text files with different encoding: due to several reasons, you might need to save a text file with a particular encoding. When about to save the file, click the drop down in the Save button and select Save with Encoding:image

      Then select the encoding and line ending you want:

      image

    8. Preview Web.config transformations: ASP.NET Web.config Transformations were introduced in Visual Studio 2010 and allow us to perform transformations on the Web.config file based on the current configuration – Debug, Release, etc, upon deployment. The problem is, most of you don’t know it’s possible to view the result file without actually deploying the application, but it is! Right click on a transformation file, like Web.Release.config, and select Preview Transform:image
    9. Generate ASP.NET designer files: when your ASP.NET web forms designer files – like Default.Designer.aspx.cs – become corrupted, for whatever reason, it’s better to remove them and have them generated by Visual Studio. Right click on the markup file – .aspx, .master or .ascx – and click on Project – Convert to Web Application (weird choice of name, I know):image
    10. Debug MSBuild scripts: I left the best for last! It’s possible to debug .csproj build tasks, which are basically MSBuild tasks. I won’t cover it all here, but instead make sure you read this post by Dan Moseleyhttp://blogs.msdn.com/b/visualstudio/archive/2010/07/06/debugging-msbuild-script-with-visual-studio.aspx.

    I will be back! Winking smile