Quantcast
Channel: Development With A Dot
Viewing all 404 articles
Browse latest View live

SharePoint Reference Document


SharePoint Filter Web Part

$
0
0

SharePoint includes a couple of useful web parts that can be used to filter other web parts. The problem is, they are only available in the Enterprise edition. It is easy, however, to build our own filters.

The key interface here is ITransformableFilterValues; it defines the contract for passing one or more values into another web part for the purpose of filtering it. Another useful interface is IDefaultFilterValue, which, guess what, defines a default value to be returned in case no one exists to be returned. Let's see an example that retrieves values from the query string:

publicclass QueryStringFilter : WebPart, ITransformableFilterValues, IDefaultFilterValue
{
public QueryStringFilter()
    {
this.ChromeType = PartChromeType.None;
this.Hidden = true;
this.MultiValueHandling = MultiValueHandling.First;
this.MultiValueSeparator = String.Empty;
    }
 
    [WebDisplayName("Default Value")]
    [WebPartStorage(Storage.Shared)]
    [WebBrowsable(true)]
    [FriendlyName("Default Value")]
    [DefaultValue("")]
    [Description("")]
    [Personalizable(PersonalizationScope.Shared)]
public String DefaultValue { get; set; }
 
    [WebDisplayName("Allow All Value")]
    [WebPartStorage(Storage.Shared)]
    [WebBrowsable(true)]
    [FriendlyName("Allow All Value")]
    [Personalizable(PersonalizationScope.Shared)]
    [DefaultValue(false)]
    [Description("")]
public Boolean AllowAllValue { get; set; }
 
    [WebDisplayName("Allow Empty Value")]
    [WebPartStorage(Storage.Shared)]
    [WebBrowsable(true)]
    [FriendlyName("Allow Empty Value")]
    [Personalizable(PersonalizationScope.Shared)]
    [DefaultValue(false)]
    [Description("")]
public Boolean AllowEmptyValue { get; set; }
 
    [WebDisplayName("Allow Multiple Values")]
    [WebPartStorage(Storage.Shared)]
    [WebBrowsable(true)]
    [FriendlyName("Allow Multiple Values")]
    [Personalizable(PersonalizationScope.Shared)]
    [DefaultValue(false)]
    [Description("")]
public Boolean AllowMultipleValues { get; set; }
 
    [WebDisplayName("Query String Parameter Name")]
    [WebPartStorage(Storage.Shared)]
    [WebBrowsable(true)]
    [FriendlyName("Query String Parameter Name")]
    [Personalizable(PersonalizationScope.Shared)]
    [DefaultValue("")]
    [Description("")]
public String ParameterName { get; set; }
 
    [WebDisplayName("Multi Value Handling")]
    [WebPartStorage(Storage.Shared)]
    [WebBrowsable(true)]
    [FriendlyName("Multi Value Handling")]
    [Personalizable(PersonalizationScope.Shared)]
    [DefaultValue(MultiValueHandling.First)]
    [Description("")]
public MultiValueHandling MultiValueHandling { get; set; }
 
    [WebDisplayName("Multi Value Separator")]
    [WebPartStorage(Storage.Shared)]
    [WebBrowsable(true)]
    [FriendlyName("Multi Value Separator")]
    [Personalizable(PersonalizationScope.Shared)]
    [DefaultValue("")]
    [Description("")]
public String MultiValueSeparator { get; set; }
 
    ReadOnlyCollection<String> ITransformableFilterValues.ParameterValues
    {
        get
        {
            var list = new List<String>();
 
if (String.IsNullOrWhiteSpace(this.ParameterName) == false)
            {
if (this.AllowMultipleValues == false)
                {
                    list.Add(this.Context.Request.QueryString[this.ParameterName]);
                }
else
                {
                    var index =
                        Array.IndexOf(
this.Context.Request.QueryString.AllKeys.Select(x => (x ?? String.Empty).ToLowerInvariant()).ToArray(),
this.ParameterName.ToLowerInvariant());
 
if (index >= 0)
                    {
if (this.MultiValueHandling == MultiValueHandling.First)
                        {
                            list.Add(this.Context.Request.QueryString.GetValues(index).First());
                        }
elseif (this.MultiValueHandling == MultiValueHandling.All)
                        {
                            list.AddRange(this.Context.Request.QueryString.GetValues(index));
                        }
else
                        {
                            list.Add(String.Join(this.MultiValueSeparator, this.Context.Request.QueryString.GetValues(index)));
                        }
                    }
                }
 
if (list.Count == 0)
                {
if (String.IsNullOrWhiteSpace(this.DefaultValue) == false)
                    {
                        list.Add(this.DefaultValue);
                    }
else
                    {
if (this.AllowAllValue == false)
                        {
if (this.AllowEmptyValue == true)
                            {
                                list.Add(String.Empty);
                            }
                        }
else
                        {
                            list.Add(null);
                        }
                    }
                }
            }
 
returnnew ReadOnlyCollection<String>(list);
        }
    }
 
    [ConnectionProvider("Query String Filter", "ITransformableFilterValues", AllowsMultipleConnections = true)]
public ITransformableFilterValues GetFilterValues()
    {
returnthis;
    }
}

There are a couple of public properties:

  • ParameterName: the query string key whose value is to be returned;
  • DefaultValue: the default value to be returned, in case the query string does not contain a value for the ParameterName key;
  • AllowAllValue: whether to allow the all (null) value;
  • AllowEmptyValue: whether to allow the empty ("") value;
  • AllowMultipleValues: whether to allow multiple values or not;
  • MultiValueHandling: what to do if multiple values are found for the ParameterName key: select the first only, return all or combine them all into one;
  • MultiValueSeparator: the separator for combining multiple values.

The web part is invisible and will only show the chrome when the page is in edit mode. After you add it to a page, you can add a connection of type filter to another web part and select the field on the other web part that you want to filter by. The actual

ITransformableFilterValues implementation is returned by the GetFilterValues method, which is marked with the ASP.NET ConnectionProvider attribute so as to make it a connection provider, and can feed several other web parts. The logic inside ParameterValues is a bit tricky because of the AllowAllValue and AllowEmptyValue properties but I think you'll have no problems following it.

You would normally apply a filter using the browser interface, but you can also do it in markup:

<WebPartPages:SPProxyWebPartManagerrunat="server">
<SPWebPartConnections>
<WebPartPages:SPWebPartConnectionConsumerConnectionPointID="DFWP Filter Consumer ID"ConsumerID="listViewWebPart"ProviderConnectionPointID="ITransformableFilterValues"ProviderID="queryStringFilterWebPart">
<WebPartPages:TransformableFilterValuesToParametersTransformerConsumerFieldNames="LinkTitle"ProviderFieldNames="TaskName"/>
</WebPartPages:SPWebPartConnection>
</SPWebPartConnections>
</WebPartPages:SPProxyWebPartManager>

In this example, I am binding the LinkTitle field of a list view web part to the TaskName query string parameter provided by the QueryStringFilter web part. If the query string contains a TaskName parameter that matches the list view's LinkTitle, it will show these records.

Silverlight Resource Markup Extension

$
0
0

In case you want to use resources in your Silverlight applications, you have a couple of options. My favorite, however, is using a markup extension and RESX resource files. With this approach, you can have code such as:

<ButtonClick="OnClick"Content="{my:Resource ResourceName=Resources, ResourceKey=ButtonTitle}"/>

This is very similar to how you'd do it in ASP.NET web forms, and you can even share the resource files. Let's see the ResourceExtension class, which, by implementing IMarkupExtension<T>, becomes a markup extension, meaning that by convention we can leave out the Extension suffix:

publicsealedclass ResourceExtension : IMarkupExtension<String>
{
public String ResourceName { get; set; }
public String ResourceKey { get; set; }
 
private Type FindResourceType(params Assembly [] assemblies)
    {
foreach (var assembly in assemblies.Distinct())
        {
            var resourceType = assembly.GetTypes().SingleOrDefault(x => (x.BaseType == typeof(Object)) && (x.Name == this.ResourceName));
 
if (resourceType != null)
            {
return resourceType;
            }
        }
 
returnnull;
    }
 
public String ProvideValue(IServiceProvider serviceProvider)
    {
        var executingAssembly = Assembly.GetExecutingAssembly();
        var callingAssembly = Assembly.GetCallingAssembly();
        var applicationAssembly = Application.Current.GetType().Assembly;
 
        var resourceType = this.FindResourceType(applicationAssembly, callingAssembly, executingAssembly);
 
if (resourceType != null)
        {
            var resourceManager = new ResourceManager(resourceType);
return resourceManager.GetString(this.ResourceKey);
        }
else
        {
thrownew InvalidOperationException(String.Format("Cannot find resource {0} with key {1}.", this.ResourceName, this.ResourceKey));
        }
    }
}

ResourceExtension will try to find the class identified in the ResourceName property in a couple of assemblies and if it finds it, will try to return the resource key from the ResourceKey property; otherwise, it throws an exception, because, obviously, something is wrong.

The resource files need to be compiled as embedded resources:

image

Now, before you can use this, you need to do a couple of things:

  1. Add the NeutralResourcesAssemblyAttribute to your assembly to indicate which language the default resource file refers to (in AssemblyInfo.cs):
    [assembly: NeutralResourcesLanguage("en-US")]
  2. Set the culture of the current thread upon application startup (App.xaml.cs):
    privatevoid Application_Startup(object sender, StartupEventArgs e)
    {
        Thread.CurrentThread.CurrentCulture = new CultureInfo("pt-PT");
        Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
     
    this.RootVisual = new MainPage();
    }
  3. Add the list of supported languages to the project file (.csproj):
    <ProjectToolsVersion="4.0"DefaultTargets="Build"xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
    <ConfigurationCondition=" '$(Configuration)' == '' ">Debug</Configuration>
    <PlatformCondition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.50727</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{4AB634F1-D00D-4461-83F4-3ADA1DF2D47B}</ProjectGuid>
    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <SupportedCultures>en-US;pt-PT</SupportedCultures>
    <TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
    <TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
    <SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
    <SilverlightApplication>true</SilverlightApplication>
    <!- ... -->
    </PropertyGroup>
    </Project>

And this is it! Enjoy!

Lesser-Known NHibernate Features: Serializing Configuration

$
0
0

This isn't exactly a feature of NHibernate, but it is something that you can do with it and most people isn't aware of.

If you have a big number of classes and mappings in your domain, adding all of them to a Configuration instance can take some time.

NHibernate allows you to serialize the Configuration instance with all the mappings that it contains so that you can deserialize it later, which can result in reduced startup time. The disadvantage is that if you change any of the mappings, you have to discard the serialized file and build a new one.

Here's how you can do it:

var serializer = new BinaryFormatter();
 
//serialize
using (var stream = File.OpenWrite("Configuration.bin"))
{
    serializer.Serialize(stream, cfg);
}
 
//deserialize
using (var stream = File.OpenRead("Configuration.bin"))
{
    cfg = serializer.Deserialize(stream) as Configuration)
}

Silverlight Method Data Source

$
0
0

A quick way to obtain a data source from a method through a markup extension, without any code:

<ComboBoxItemsSource="{my:MethodDataSource MethodName=GetItems}"/>

Implementation is simple:

publicclass MethodDataSourceExtension : DependencyObject, IMarkupExtension<Object>
{
publicstaticreadonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(Object), typeof(MethodDataSourceExtension), new PropertyMetadata(null));
publicstaticreadonly DependencyProperty ArgumentProperty = DependencyProperty.Register("Argument", typeof(Object), typeof(MethodDataSourceExtension), new PropertyMetadata(null));
 
public String MethodName { get; set; }
 
public Object Target
    {
        get { returnthis.GetValue(TargetProperty); }
        set { this.SetValue(TargetProperty, value); }
    }
 
public Object Argument
    {
        get { returnthis.GetValue(ArgumentProperty); }
        set { this.SetValue(ArgumentProperty, value); }
    }
 
public Object ProvideValue(IServiceProvider serviceProvider)
    {
        var rootObjectProvider = serviceProvider.GetService<IRootObjectProvider>();
        var target = (this.Target ?? rootObjectProvider.RootObject);
        var method = target.GetType().GetMethod(this.MethodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
        var args = (this.Argument != null) ? new Object[] { this.Argument } : null;
 
return method.Invoke((method.IsStatic == true) ? null : target, args);
    }
}

The MethodDataSourceExtension has three properties:

  • MethodName: the name of a method;
  • Target: the optional element from which the data source method will be retrieved; if not specified, the containing control (Page, UserControl, etc) will be used;
  • Argument: an optional argument to the data source method.

Because we might want to use bindings, the MethodDataSourceExtension class inherits from DependencyObject. This allows us to have:

<ComboBox ItemsSource="{my:MethodDataSource MethodName=GetItems, Target={Binding ElementName=SomeControl}}"/>

We can call any method, static or instance, as long as it is public. It can have a single parameter or no parameter at all.

Day Against DRM 2015 Campaign

Lesser-Known NHibernate Features: LINQ Extensions

$
0
0

With NHibernate, you are not bound by the out-of-the box methods that LINQ provides, and their default translations to SQL. I already mentioned that you can add your own extension methods, with minimum work:

publicstaticclass StringExtensions
{
    [LinqExtensionMethod("FREETEXT")]
publicstatic Boolean Freetext(this String propertyName, String value)
    {
return (propertyName.ToUpper().Contains(value.ToUpper()));
    }
}

For this example, I am creating an extension for the FREETEXT T-SQL function, which is one of the ways by which we can do full-text searching. All it takes is the LinqExtensionMethodAttribute applied to a method, with the name for the database function (can be different from the method name), and that’s it! NHibernate will try to match the parameters:

   1: var result = session.Query<MyEntity>().Where(x => x.Name.Freetext("something") == true).ToList();

Yes… Entity Framework let’s you do this… kind of… only for some functions!

Comunidades Portuguesas de Desenvolvimento


Silverlight Transform Markup Extension

$
0
0

<ComboBox ItemsSource="{Binding ItemsSource, ElementName=MyDataSource}, Converter={StaticResource MyConverter}}" Width="100" Height="30"/>
<
ComboBoxItemsSource="{Binding ItemsSource, ElementName=MyDataSource}, Converter={StaticResource MyConverter}}"Width="100"Height="30"/>
Did you ever have one of those situations where you need to apply a data source to a control, but also convert the each of the data source elements?

Normally we set the data source to the DataContext or ItemsSource property and add an item template where we specify the converter, but sometimes this can’t be done this way, for one reason or the other. So, I worked out a simple solution: the TransformedItemSource! What it does is, it allows us to specify a converter for each of the source values in the data source collection, not the data source as a whole.

Here it is:

publicclass TransformedItemSource : DependencyObject, IEnumerable
{
publicstaticreadonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(TransformedItemSource), new PropertyMetadata(null));
publicstaticreadonly DependencyProperty ConverterProperty = DependencyProperty.Register("Converter", typeof(IValueConverter), typeof(TransformedItemSource), new PropertyMetadata(null));
publicstaticreadonly DependencyProperty ParameterProperty = DependencyProperty.Register("Parameter", typeof(Object), typeof(TransformedItemSource), new PropertyMetadata(null));
publicstaticreadonly DependencyProperty CultureProperty = DependencyProperty.Register("Culture", typeof(CultureInfo), typeof(TransformedItemSource), new PropertyMetadata(CultureInfo.CurrentCulture));
publicstaticreadonly DependencyProperty TargetTypeProperty = DependencyProperty.Register("TargetType", typeof(Type), typeof(TransformedItemSource), new PropertyMetadata(null));
 
public Type TargetType
    {
        get
        {
returnthis.GetValue(TargetTypeProperty) as Type;
        }
        set
        {
this.SetValue(TargetTypeProperty, value);
        }
    }
 
public Object Parameter
    {
        get
        {
returnthis.GetValue(ParameterProperty);
        }
        set
        {
this.SetValue(ParameterProperty, value);
        }
    }
 
public CultureInfo Culture
    {
        get
        {
returnthis.GetValue(CultureProperty) as CultureInfo;
        }
        set
        {
this.SetValue(CultureProperty, value);
        }
    }
 
public IEnumerable ItemsSource
    {
        get
        {
returnthis.GetValue(ItemsSourceProperty) as IEnumerable;
        }
        set
        {
this.SetValue(ItemsSourceProperty, value);
        }
    }
 
public IValueConverter Converter
    {
        get
        {
returnthis.GetValue(ConverterProperty) as IValueConverter;
        }
        set
        {
this.SetValue(ConverterProperty, value);
        }
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
foreach (var current inthis.ItemsSource ?? new Object[0])
        {
            var targetType = this.TargetType ?? ((current != null) ? current.GetType() : null);
 
yieldreturnthis.Converter.Convert(current, targetType, this.Parameter, this.Culture);
        }
    }
}

It inherits from DependencyObject, this is so that I can apply bindings to its properties. It will try to iterate through all the items in the ItemsSource property and convert one at a time using the supplied converter, culture and parameter. An example might be:

<UserControl.Resources>
<my:TransformedItemSourcex:Key="MyTransformer"ItemsSource="{Binding ItemsSource, ElementName=MyDatasource}"Converter="{StaticResource MyConverter}"/>
</UserControl.Resources>
<StackPanelx:Name="LayoutRoot">
<ComboBoxItemsSource="{StaticResource MyTransformer}"Width="100"Height="30"/>
<StackPanel>

A nice way to use it is through a markup extension:

publicclass TransformExtension : DependencyObject, IMarkupExtension<IEnumerable>
{
publicstaticreadonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(TransformExtension), new PropertyMetadata(null));
publicstaticreadonly DependencyProperty ConverterProperty = DependencyProperty.Register("Converter", typeof(IValueConverter), typeof(TransformExtension), new PropertyMetadata(null));
publicstaticreadonly DependencyProperty ParameterProperty = DependencyProperty.Register("Parameter", typeof(Object), typeof(TransformExtension), new PropertyMetadata(null));
publicstaticreadonly DependencyProperty CultureProperty = DependencyProperty.Register("Culture", typeof(CultureInfo), typeof(TransformExtension), new PropertyMetadata(CultureInfo.CurrentCulture));
publicstaticreadonly DependencyProperty TargetTypeProperty = DependencyProperty.Register("TargetType", typeof(Type), typeof(TransformExtension), new PropertyMetadata(null));
 
public Type TargetType
    {
        get
        {
returnthis.GetValue(TargetTypeProperty) as Type;
        }
        set
        {
this.SetValue(TargetTypeProperty, value);
        }
    }
 
public Object Parameter
    {
        get
        {
returnthis.GetValue(ParameterProperty);
        }
        set
        {
this.SetValue(ParameterProperty, value);
        }
    }
 
public CultureInfo Culture
    {
        get
        {
returnthis.GetValue(CultureProperty) as CultureInfo;
        }
        set
        {
this.SetValue(CultureProperty, value);
        }
    }
 
public IEnumerable ItemsSource
    {
        get
        {
returnthis.GetValue(ItemsSourceProperty) as IEnumerable;
        }
        set
        {
this.SetValue(ItemsSourceProperty, value);
        }
    }
 
public IValueConverter Converter
    {
        get
        {
returnthis.GetValue(ConverterProperty) as IValueConverter;
        }
        set
        {
this.SetValue(ConverterProperty, value);
        }
    }
 
public IEnumerable ProvideValue(IServiceProvider serviceProvider)
    {
returnnew TransformedItemSource { ItemsSource = this.ItemsSource, Parameter = this.Parameter, Culture = this.Culture, Converter = this.Converter };
    }
}

The TransformExtension class just delegates all of the work to the TransformedItemSource, but because it is a markup extension, it can be used as:

<ComboBoxItemsSource="{Binding ItemsSource, ElementName=MyDataSource, Converter={StaticResource MyConverter}}"Width="100"Height="30"/>

Enjoy!

Adding Custom Validation Messages in Silverlight

$
0
0

Sometimes it is useful to add our own validation messages to a Silverlight control, normally because we are doing custom validation through code, not through the reguar IDataErrorInfo, INotifyDataErrorInfo or ValidationAttributes. This is not straightforward, but it is possible. An example might be:

//this class is used for throwing an exception upon binding
//has to be public, doesn't need to implement any interfaces
publicclass TagModelError
{
privatereadonly String errorMessage;
 
public TagModelError(String errorMessage)
    {
this.errorMessage = errorMessage;
    }
 
public Object Tag
    {
        get
        {
returnnew Object();
        }
        set
        {
thrownew ValidationException(this.errorMessage);
        }
    }
}
 
//extension method for adding validation messages on a control
publicstaticvoid AddValidationError(this Control control, String errorMessage)
{
    var expression = elm.GetBindingExpression(FrameworkElement.TagProperty);
 
if (expression == null)
    {
        expression = control.SetBinding(FrameworkElement.TagProperty, new Binding("Tag")
        {                
            Mode = BindingMode.TwoWay,
            ValidatesOnExceptions = true,
            UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
            Source = new TagModelError(errorMessage)
        }) as BindingExpression;
    }
 
    expression.UpdateSource();
}

In this example I am using the TagProperty, because it is seldom used, but you can use whatever you like. The trick here is to force the update of a bound object, which throws an exception, which in turn is propagated as a validation error.

Lesser-Known NHibernate Features: Mapping By Convention

$
0
0

Did you know that NHibernate, like other O/RMs out there, allows you to map classes by convention? Yes, it’s true… Smile Let me show you how!

First, you need to create an instance of the appropriately-named ConventionModelMapper:

var mapper = new ConventionModelMapper();

Next, you need to tell it where is the assembly (or assemblies) containing the entities you want to map:

var mappings = mapper.CompileMappingFor(typeof(MyEntity).Assembly.GetExportedTypes());

Finally, you need to add the generated mappings to a Configuration instance:

cfg.AddMapping(mappings);

That’s all it takes! Really! Winking smile

Granted, this is very convenient, but we don’t know much what is happening inside; for example, what id generation strategy is it using? By default, it uses native, which means it will use the native strategy of the database engine currently being used – identity for SQL Server and MySQL, sequence for Oracle and PostgreSQL, etc. If you wish to override this, you certainly can:

mapper.BeforeMapClass += (modelInspector, type, classCustomizer) =>
{
    classCustomizer.Id(x =>
    {
        x.Generator(Generators.HighLow);
    });
};

This tells the mapper to use the high-low strategy for all entities.

What if you want to change the default naming of columns and tables? Well, you have two options:

  1. Provide an handler for the BeforeMapClass or BeforeMapProperty events and in it change the name of the physical object:
    mapper.BeforeMapClass += (modelInspector, type, classCustomizer) =>
    {
        classCustomizer.Table(this.GetTableName(type.Name));
    };
     
    mapper.BeforeMapProperty += (modelInspector, member, propertyCustomizer) => 
    {
        propertyCustomizer.Column(this.GetColumnName(member.LocalMember.Name));
    };
  2. Provide your own implementation of INamingStrategy to NHibernate:
    publicclass CustomNamingStrategy : INamingStrategy
    {
    public String ClassToTableName(String className)
        {
    return className;
        }
     
    public String ColumnName(String columnName)
        {
    return columnName;
        }
     
    public String LogicalColumnName(String columnName, String propertyName)
        {
    return columnName;
        }
     
    public String PropertyToColumnName(String propertyName)
        {
    return propertyName;
        }
     
    public String PropertyToTableName(String className, String propertyName)
        {
    return propertyName;
        }
     
    public String TableName(String tableName)
        {
    return tableName;
        }
    }
     
    cfg.SetNamingStrategy(new CustomNamingStrategy());

In general, you can trust NHibernate’s judgement, but if you wish, you can override other aspects of the mapping by providing handlers to the many events of ConventionModelMapper. Say, for example, that you want to exclude (or include) only certain classes from an assembly, you can provide an handler for the IsEntity event:

mapper.IsEntity += (type, @default, assemblyName) =>
{
returntypeof(IEntity).IsAssignableFrom(type);
};

Or you want to configure a collection as a set rather than a bag:

mapper.IsBag += (member, @default) =>
{
returnfalse;
};
 
mapper.IsSet += (member, @default) =>
{
returntrue;
};

Or even set a collection as not lazy and use inner join fetching:

mapper.BeforeMapSet += (modelInspector, member, propertyCustomizer) =>
{
    propertyCustomizer.Lazy(CollectionLazy.NoLazy);
    propertyCustomizer.Fetch(CollectionFetchMode.Join);
};

Defining ASP.NET Update Panel Template Contents Dynamically

$
0
0

The ASP.NET UpdatePanel was introduced with the ASP.NET 2.0 AJAX Extensions almost a century ago (kidding, but almost feels like it!Winking smile). It allows us to have AJAX-style effects (partial page loads) with very little effort.

The UpdatePanel has a property, ContentTemplate, which is, well, a template, which means it can only be set through markup – not so good for dynamic contents -, or by implementing our own ITemplate class – slightly more work, but this can be reused in other scenarios where an ITemplate is required:

publicclass Template : Control, ITemplate
{
publicvoid InstantiateIn(Control container)
    {
        var controls = this.Controls.OfType<Control>().ToList();
 
for (var i = 0; i < controls.Count; i++)
        {
            var control = controls[i];
            container.Controls.Add(control);
        }
    }
}
 
protectedoverridevoid OnLoad(EventArgs e)
{
    var tb = new TextBox { ID = "time" };
    var timer = new Timer { Interval = 1000, Enabled = true };
    timer.Tick += timer_Tick;
 
    var tmp = new Template();
    tmp.Controls.Add(tb);
    tmp.Controls.Add(timer);
 
    var up = new UpdatePanel();
    up.ContentTemplate = tmp;
 
this.Form.Controls.Add(up);
 
base.OnLoad(e);
}
 
void timer_Tick(object sender, EventArgs e)
{
    var time = (sender as Control).NamingContainer.FindControl("time") as TextBox;
    time.Text = DateTime.Now.ToString();
}

There is also a property, ContentTemplateContainer, which is a control to which you can add your controls, so that they are added to the UpdatePanel. This is exactly what we need:

protectedoverride OnLoad(EventArgs e)
{
    var tb = new TextBox { ID = "time" };
    var timer = new Timer { Interval = 1000, Enabled = true };
    timer.Tick += this.timer_Tick;
    var up = new UpdatePanel();
    up.ContentTemplateContainer.Controls.Add(tb);
    up.ContentTemplateContainer.Controls.Add(timer);
this.Form.Controls.Add(up);
base.OnLoad(e);
}

Lesser-Known NHibernate Features: Filters

$
0
0

Unlike other OR/Ms– which, as always, shall remain unnamed – NHibernate offers a couple of ways to automatic filter results. Basically, we have two options:

  • Static restrictions;
  • Dynamic restrictions, or filters.

Because filters offer everything that static restrictions do and more, we’ll focus on filters.

A filter can specify a restriction, in terms of a SQL clause, to either an entity as a whole (the class, not a specific query) or to a collection (bag, set, list, map, array, etc).

For example, imagine you have a table that holds values that can be translated and a translation table for that purpose:

image

You will want to retrieve only the translation for the current culture. A domain model could look like:

image

We would like to apply a restriction to the Translations property of Translatable, so as to filter the translations by the current culture.

First, we need to create a filter, this is done at Configuration level:

cfg.AddFilterDefinition(new FilterDefinition("CurrentCulture", string.Empty, new Dictionary<string, IType> { { "Culture", NHibernateUtil.String } }, false));

The restriction can be defined on the filter itself, or per entity or collection. In this case, I didn’t specify it on the filter (string.Empty), so I will need to do it at the collection level:

mapper.Class<Translatable>(x =>
{
//rest goes here
        x.Set(y => y.Translations, y =>
        {
//rest goes here
            y.Filter("CurrentCulture", z =>
            {
                z.Condition("Culture = :Culture");
            });
        });
    }
);

A filter needs to be explicitly made active, and, if it contains parameters, all of its parameters must be set:

session.EnableFilter("CurrentCulture").SetParameter("Culture", CultureInfo.CurrentCulture.Name);

Now, whenever the Translations collection is retrieved, either through a SELECT or an INNER JOIN, the “Culture = :Culture” restriction - where, of course, :Culture is replaced by the current parameter value - will be applied automatically, together with the foreign key restriction.

The other option is to filter entities as a whole. Remember soft deletes? I wrote two posts on them (here and here). Instead of using static restrictions, we can instead use filters:

cfg.AddFilterDefinition(new FilterDefinition("SoftDeletes", "deleted = 0, new Dictionary<string, IType>(), true));
 
mapper.Class<Record>(x =>
{
    x.Filter("SoftDeletes", y => {});
    x.Set(y => y.Children, y =>
        {
            y.Filter("SoftDeletes", z => {});
        });
});

In this example, I define the restriction string on the filter itself, and I apply it to both the Record entity and its Children collection. This time, no parameters, but we still need to enable the filter before we issue a query:

session.EnableFilter("SoftDeletes");

If for any reason you want to disable a filter, it’s easy:

session.DisableFilter("SoftDeletes");

Or even get its definition:

var filter = sessionFactory.GetFilterDefinition("SoftDeletes");

Enjoy your filters!

Lesser-Known NHibernate Features: Calculated Properties

$
0
0

With NHibernate you can have entity properties that are the result of a SQL expression (unlike other O/RMs). It is also possible to add arbitrary SQL restrictions to collections of an entity.

First, here’s how we define a calculated property:

publicclass MyEntity
{
//rest goes here
public Int32 ? MyCalculatedProperty { get; protected set; }
}
 
mapper.Class<MyEntity>(c =>
{
//rest goes here
    c.Property(x => x.MyCalculatedProperty, x =>
    {
        x.Formula("(SELECT MAX(SomeTable.Something) FROM SomeTable WHERE SomeTable.Id = Id)");
        x.Insert(false);
        x.Update(false);
    });
});

NHibernate is clever enough to find out that the un-prefixed Id refers to the entity’s table. This is a silly example, but I think you get the picture. Remember that this is plain SQL, not HQL, and will not be translated in any way.

As for collection restrictions, a simple example:

publicclass MyEntity
{
//rest goes here
publicvirtual IEnumerable<MyIssue> RecentIssues { get; protected set; }
}
 
mapper.Class<MyEntity>(c =>
{
//rest goes here
    c.Set(x => x.RecentIssues, x =>
    {
        c.Where("(date >= (GETDATE() - 7))");
//rest goes here
    }, c => c.OneToMany());
});

Notice that I am mapping the RecentIssues collection as IEnumerable<T>, this is because otherwise I would have to check if the values being added matched the desired constraint (“>= GETDATE() – 7”, the last 7 days). Certainly possible, but I leave it as an exercise to you, dear reader! Of course, GETDATE() is a SQL Server function, the restrictions can only be specified in native SQL.

Stay tuned for more!

Entity Framework Multitenancy

$
0
0

Introduction

Multitenancy is currently a hot topic in web development. Azure, SharePoint, and many other frameworks are offering multitenant options, because it totally makes sense to make a better use of a server by hosting many different services.

When it comes to data, there are usually three different strategies:

  • Separate databases: each tenant is stored in its own database; different connection strings must be provided, one for each tenant;
  • Shared database, separate schemas: all tenant’s data live in the same database and even share table names, but in different schemas;
  • Shared database, shared schema tenants share the same physical tables,  but use a discriminator column for distinguishing between them.

Let’s explore how we can use each of these techniques in Entity Framework (Code First, of course – is there another?).

Prerequisites

First, we need to have a way to obtain the current tenant, specifically, a tenant id or code. To simplify, let’s just assume a simple interface:

publicstaticclass TenantConfiguration
{
publicstatic String GetCurrentTenantId()
    {
//doesn't matter
    }
}

You are free to implement this in any way you want.

We’ll also have a data context:

publicclass MultitenantContext : DbContext
{
public DbSet<MultitenantEntity> MultitenantEntities { get; set; }
}

Separate Databases

We need to inject the different connection strings through the constructor:

public MultitenantContext() : base(GetConnectionString())
{
}
 
privatestatic String GetConnectionString()
{
    var currentTenant = TenantConfiguration.GetCurrentTenantId();
return ConfigurationManager.ConnectionStrings[currentTenant].ConnectionString;
}

This is a simple strategy that relies on having one connection string per tenant id, but others exist, of course.

Shared Database, Separate Schemas

Another option is to have each tenant in its own schema. For that, we need to leverage the OnModelCreating method for configuring the model:

protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
{
    var currentTenant = TenantConfiguration.GetCurrentTenantId();
 
    modelBuilder.Types().Configure(x =>
    {
        x.ToTable(x.ClrType.Name, currentTenant);
    });
 
base.OnModelCreating(modelBuilder);
}

Again, a simple example: each type gets mapped to an equally-named table and to a schema that is identical to the tenant id. Pay attention to this: SQL Server and other RDBMSs allows users to have a default schema, so in theory, if you use integrated security, you may leave out the explicit schema. However, Entity Framework will always include the schema with the database objects, so you have to explicitly configure it, as we have.

Shared Database, Shared Schema

The final option depends on a discriminator column that is not mapped, but contains a different value for each tenant. Again, we need to configure the model accordingly (warning: reflection ahead):

privatestaticvoid Map<T>(EntityMappingConfiguration<T> cfg) where T : class
{
    var currentTenant = TenantConfiguration.GetCurrentTenantId();
 
    cfg.Requires("Tenant").HasValue(currentTenant);
}
 
protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
{
    var modelConfiguration = modelBuilder.GetType().GetProperty("ModelConfiguration", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(modelBuilder, null);
    var entities = modelConfiguration.GetType().GetProperty("Entities", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(modelConfiguration, null) as IEnumerable<Type>;
 
foreach (var entity in entities)
    {
        var entityTypeConfiguration = modelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(entity).Invoke(modelBuilder, null);
        var mapMethod = entityTypeConfiguration.GetType().GetMethods().First(m => m.Name == "Map");
 
        var localMethod = this.GetType().GetMethod("Map", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(entity);
        var delegateType = typeof(Action<>).MakeGenericType(localMethod.GetParameters().First().ParameterType);
 
        var del = Delegate.CreateDelegate(delegateType, localMethod);
 
        mapMethod.Invoke(entityTypeConfiguration, new Object[] { del });
    }
 
base.OnModelCreating(modelBuilder);
}

This code is required because we need to iterate through all mapped entities, and Entity Framework doesn’t expose everything as public APIs. Some parts could be cached for performance or slightly improved, but I leave that as an exercise to you. In the end, all queries to entities will take an additional restriction “WHERE Tenant = @tenant”, where @tenant will take a different value for each tenant and Tenant is a physical column that isn’t mapped, nor does it need to be.

Conclusion

We’ve seen several techniques for mapping multitenant Entity Framework contexts; ultimately, the one we choose will depend on a number of factors. It will mostly be about having more or less isolation of your data.










Entity Framework Multitenancy Part 2 – Conventions

$
0
0

In my last post, I talked about different scenarios for achieving multitenancy with Entity Framework contexts. This time, I am going to show how to use conventions, wrapping the code I provided then.

First, a base convention class to serve as our root hierarchy of multitenant conventions:

publicabstractclass MultitenantConvention : IConvention
{
}

Only worthy of mention is the implementation of IConvention. This is a marker interface for letting Entity Framework know that this is a convention.

Next, a convention for the separate databases approach:

publicclass SeparateDatabasesConvention : MultitenantConvention
{
public SeparateDatabasesConvention(DbContext ctx)
    {
        var currentTenantId = TenantConfiguration.GetCurrentTenant();
        ctx.Database.Connection.ConnectionString = ConfigurationManager.ConnectionStrings[currentTenantId].ConnectionString;
    }
}

This convention needs a reference to the DbContext, because it needs to change the connection string dynamically, something that you can’t do through the basic convention interfaces and classes – there’s no way to get to the context.

Now, shared database, different schemas. This time, we need to implement IStoreModelConvention<T>, using EntitySet as the generic parameter, so as to gain access to the Schema property:

publicclass SharedDatabaseSeparateSchemaConvention : MultitenantConvention, IStoreModelConvention<EntitySet>
{
publicvoid Apply(EntitySet item, DbModel model)
    {
        var currentTenantId = TenantConfiguration.GetCurrentTenant();
        item.Schema = currentTenantId;
    }
}

Finally, shared database, shared schema:

publicclass SharedDatabaseSharedSchemaConvention : MultitenantConvention
{
public String DiscriminatorColumnName { get; private set; }
 
privatevoid Map<T>(EntityMappingConfiguration<T> cfg) where T : class
    {
        var currentTenantId = TenantConfiguration.GetCurrentTenant();
        cfg.Requires(this.DiscriminatorColumnName).HasValue(currentTenantId);
    }
 
public SharedDatabaseSharedSchemaConvention(DbModelBuilder modelBuilder, String discriminatorColumnName = "Tenant")
    {
this.DiscriminatorColumnName = discriminatorColumnName;
 
        var modelConfiguration = modelBuilder.GetType().GetProperty("ModelConfiguration", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(modelBuilder, null);
        var entities = modelConfiguration.GetType().GetProperty("Entities", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(modelConfiguration, null) as IEnumerable<Type>;
 
foreach (var entity in entities)
        {
            var entityTypeConfiguration = modelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(entity).Invoke(modelBuilder, null);
            var mapMethod = entityTypeConfiguration.GetType().GetMethods().First(m => m.Name == "Map");
 
            var localMethod = this.GetType().GetMethod("Map", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(entity);
            var delegateType = typeof(Action<>).MakeGenericType(localMethod.GetParameters().First().ParameterType);
 
            var del = Delegate.CreateDelegate(delegateType, this, localMethod);
 
            mapMethod.Invoke(entityTypeConfiguration, new Object[] { del });
        }
    }
}

And a nice way to wrap all this using extension methods:

publicstaticclass DbModelBuilderExtensions
{
publicstatic DbModelBuilder UseSeparateDatabases(this DbModelBuilder modelBuilder, DbContext ctx)
    {
        modelBuilder.Conventions.Remove<MultitenantConvention>();
        modelBuilder.Conventions.Add(new SeparateDatabasesConvention(ctx));
return modelBuilder;
    }
publicstatic DbModelBuilder UseSharedDatabaseSeparateSchema(this DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<MultitenantConvention>();
        modelBuilder.Conventions.Add(new SharedDatabaseSeparateSchemaConvention());
return modelBuilder;
    }
publicstatic DbModelBuilder UseSharedDatabaseSharedSchema(this DbModelBuilder modelBuilder, String discriminatorColumnName = "Tenant")
    {
        modelBuilder.Conventions.Remove<MultitenantConvention>();
        modelBuilder.Conventions.Add(new SharedDatabaseSharedSchemaConvention(modelBuilder, discriminatorColumnName));
return modelBuilder;
    }
}

Usage: just uncomment one of the Use calls.

protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
{
//uncomment one of the following lines
//modelBuilder.UseSeparateDatabases(this);
//modelBuilder.UseSharedDatabaseSeparateSchema();
//modelBuilder.UseSharedDatabaseSharedSchema(discriminatorColumnName: "Tenant");
 
base.OnModelCreating(modelBuilder);
}

Packt Publishing Free Learning Library

$
0
0

FL - 590x295

From 30th April, 2015 Packt Publishing has thrown open the virtual doors of its new Free Learning Library and offering its customers a daily chance to grab a fresh free eBook from its website. The Free Learning Library will be open all year-round but each title will only be up for 24 hours, so make sure you keep checking back to get your hands on the latest book! All you'll have to do is simply click on the day’s free eBook and it will instantly be added to your account.

See more information here: http://bit.ly/1Kjmip6.

Free Training Materials for Development

$
0
0

A random list that will grow, eventually, with time.

Training Kits

Microsoft: https://weblogs.asp.net/ricardoperes/microsoft-developer-training-kits


E-books

Syncfusion

http://www.syncfusion.com/resources/techportal/ebooks/

Microsoft

http://blogs.msdn.com/b/mssmallbiz/archive/2014/07/07/largest-collection-of-free-microsoft-ebooks-ever-including-windows-8-1-windows-8-windows-7-office-2013-office-365-office-2010-sharepoint-2013-dynamics-crm-powershell-exchange-server-lync-2013-system-center-azure-cloud-sql.aspx


Videos

ASP.NET

ASP.NET MVC: http://www.asp.net/mvc/videos, http://www.asp.net/mvc/videos/mvc-5

ASP.NET Web Forms: http://www.asp.net/web-forms/videos

ASP.NET Single Page Application: http://www.asp.net/single-page-application/videos

Web API: http://www.asp.net/web-api/videos

Web Pages: http://www.asp.net/web-pages/videos

ASP.NET MVC (Pluralsight): http://www.asp.net/mvc/pluralsight

ASP.NET Web Forms (Pluralsight): http://www.asp.net/web-forms/pluralsight


Microsoft

SharePoint:https://technet.microsoft.com/en-us/library/cc262880.aspx

How do I?: https://msdn.microsoft.com/en-us/bb629407.aspx

SharePoint 2013 training for IT pros: https://technet.microsoft.com/en-us/office/dn756397.aspx

Azure: http://azure.microsoft.com/en-gb/documentation/videos/index/

Entity Framework: https://msdn.microsoft.com/en-us/data/jj590134.aspx

Data (WCF Data Services, Entity Framework, ADO.NET, LINQ to SQL): https://msdn.microsoft.com/en-us/data/videos.aspx

SharePoint Online: https://support.office.com/en-us/article/SharePoint-Online-training-courses-videos-and-tutorials-2eb5e190-1c90-4987-908d-7c2263f40c5e?CorrelationId=ca3182e4-df93-4d45-8f4c-87e558fd1cbb&ui=en-US&rs=en-US&ad=US&fromAR=1

Office 365 Jump Start: https://technet.microsoft.com/en-us/video/office-365-jump-start-01-microsoft-office-365-overview-for-it-pros.aspx


Channel 9

C#: http://channel9.msdn.com/Search?term=c%23

ASP.NET: http://channel9.msdn.com/Search?term=asp.net

SharePoint:http://channel9.msdn.com/Search?term=sharepoint

HTML5: http://channel9.msdn.com/Search?term=html5

SQL Server:http://channel9.msdn.com/Search?term=sql%20server

Azure: http://channel9.msdn.com/Search?term=azure

Office 365: http://channel9.msdn.com/Search?term=office%20365

Entity Framework: http://channel9.msdn.com/Search?term=entity%20framework

Web API: http://channel9.msdn.com/Search?term=web%20api


Microsoft Virtual Academy

C#: http://www.microsoftvirtualacademy.com/training-topics/c-app-development

ASP.NET: http://www.microsoftvirtualacademy.com/training-topics/web-development

SharePoint:http://www.microsoftvirtualacademy.com/product-training/sharepoint

HTML5: http://www.microsoftvirtualacademy.com/training-topics/html5

SQL Server:http://www.microsoftvirtualacademy.com/product-training/sql-server

Azure: http://www.microsoftvirtualacademy.com/training-topics/cloud-app-development

Office 365: http://www.microsoftvirtualacademy.com/product-training/office-development


YouTube

C#: https://www.youtube.com/results?search_query=C%23

ASP.NET: https://www.youtube.com/results?search_query=asp.net

SharePoint:https://www.youtube.com/results?search_query=sharepoint+2013

HTML5: https://www.youtube.com/results?search_query=html5

SQL Server: https://www.youtube.com/results?search_query=sql+server

Azure: https://www.youtube.com/results?search_query=azure

Office 365: https://www.youtube.com/results?search_query=office+365

Entity Framework: https://www.youtube.com/results?search_query=entity+framework

Web API: https://www.youtube.com/results?search_query=web+api

Porto Tech Hub 2015

$
0
0

Manifesto

I have the pleasure to announce that I will be speaking next Saturday, May 30th, in the Porto Tech Hub event!

The sad part is, I will be replacing my friend Roberto Cortez (@radcortez), of Java fame, who can’t make it. Anyway, the topic will be Open Source @ Microsoft and I hope to see you all there! Winking smile

Thanks to the organization, namely, to my friend and colleague Luís Ribeiro (@luisafonso) for the invitation, and I hope I am up to it!

Porto Tech Hub 2015 – Wrap Up

$
0
0

Porto Tech Hub is over… for now! I was interesting, I got the chance to listen to some good presentations, learn a bit, and see old friends. I really liked the presentations by my former colleagues @ CRITICAL Software, Norberto Leite (@nleite), from MongoDB and Rui Ferreira, from Facebook, and, of course, the one from my friend and colleague João Esteves, which you can grab from the event’s site.

My presentation, Microsoft ♥ Open Source, is now available at my SlideShare. I tried to show a few open source projects and initiatives from Microsoft, so as to demonstrate that it has always supported open source for some time now.

As always, I’d like to hear from you!

Viewing all 404 articles
Browse latest View live