Periodic update of live tiles with Azure

by Danielvg 20. February 2013 22:07

Providing live tiles for a Windows 8 App can be unnecessarily expensive when done wrong. Here I will present a relatively cheap solution for providing impersonal live tiles when using periodic updated live tiles (polling). The solution scales to the limits of a single Azure storage account, but could be modified to support multiple storage accounts.

The basic idea of the solution is to setup tasks that retrieve data and create xml files that define tiles. A task scheduler will make sure that each task runs with a specific interval. Tasks are classes that implement the following interface and are located in the Tasks assembly.

 

public interface ITileUpdateTask

{

    TimeSpan UpdateRecurrenceTime { get; }

    Task<List<TileNotification>> GetTiles();

}

 

Here is what an implementation could look like:

public class MyTestTask : ITileUpdateTask

{

    publicTimeSpan UpdateRecurrenceTime

    {

        get { return new TimeSpan(0, 20, 0); }

    }

 

    public async Task<List<TileNotification>> GetTiles()

    {

        var headline = await SomeRepository.SomeHttpCall();

        var textTile = TileContentFactory.CreateTileWideText03();

        textTile.TextHeadingWrap.Text = headline;

        var tileNotification = new TileNotification("myKey", textTile.ToString());

        return new List<TileNotification> { tileNotification };;

    }

}

 

And on the client side you would simply start the polling with:

var pollUri = new Uri(http://STORAGEACCOUNTNAME.blob.core.windows.net/Tiles/myKey.xml);

TileUpdateManager.CreateTileUpdaterForApplication()

        .StartPeriodicUpdate(pollUri, PeriodicUpdateRecurrence.Hour);

 

 

The solution can run in a multi instance setup if you require SLA uptime, otherwise a single XS VM should be fine. In a multi instance setup, the framework will ensure that tasks only run once every recurrence time.

It is hard to calculate the exact price of using this solution, as it depends on the size of the tile xml files, image sizes, and user activity. With a one hour tile update recurrence time and a 500 byte tile xml file without images, the price would be 80$ each month for 1 million users. It would likely be much less since the calculation assumes that all users are constantly using their pc/tablet.

 

p.s. It would be cool to create a “Mobile services”-like service to share the worker role cost with others :)

 

NotificationService.zip (33,17 kb)

REST Web Services - Post and Get for WP7

by Danielvg 29. August 2011 21:51

I have been playing around with WP7 and Reactive Extensions for a few days, and wanted to share a RestRequest object for doing REST based web service post and get. The requests are done with HttpWebRequest  and support basic authentication.

Following is the main part of the RestRequest object, see the attachment for the complete implementation.

public IObservable<string> HttpGet(Uri uri, Action<HttpStatusCode, string> error, 
    Action<Exception> onException)
{
    ValidateStandardParameters(uri, error, onException);
    var result = new Subject<string>();

    var request = CreateRequest(uri, MethodType.Get);
    var requestAsync = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse);
    requestAsync().Subscribe(respons =>
    {
        ResponseHandler(respons, result, error);
    }, onException);
    return result;
}

public IObservable<string> HttpPost(Uri uri, string postData, 
    Action<HttpStatusCode, string> error, Action<Exception> onException)
{
    ValidateStandardParameters(uri, error, onException);
    var result = new Subject<string>();

    var request = CreateRequest(uri, MethodType.Post);
    var requestAsync = Observable.FromAsyncPattern<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream);
    var responseAsync = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse);

    requestAsync().SelectMany(requestStream =>
    {
        using (var writer = new StreamWriter(requestStream))
        {
            writer.Write(postData);
        }
        return responseAsync();
    }).Subscribe(response =>
    {
        ResponseHandler(response, result, error);
    }, onException);
    return result;
}

 

And to use it:

var uri = new Uri("https://www.somehost.dk/Users/1");
_restRequest.HttpGet(uri).Subscribe(s =>
    {
        using (var stream = new StringReader(s))
        {
            var serializer = new XmlSerializer(typeof(User));
            var user = (User)serializer.Deserialize(stream);
            // do something with user..
        }
    });

 

When I have a use case for it I will add support for additional headers and PUT/DELETE.

This is the first time I have worked with Reactive Extensions so I am sure there is room for improvements. Any feedback is appreciated!

RestRequest.cs (4,67 kb)

Code Contracts

by Danielvg 29. January 2011 16:21

Code contracts is a Microsoft Research project that is set to “..provide a language-agnostic way to express coding assumptions in .NET programs.” What this basically means is that Microsoft Research is creating the next generation of defensive programming into the .Net Framework. Code contracts provides a language based way of specifying and checking for invariants, pre and post-conditions in objects. Everyone has written defensive code like the following:

public void MyMethod(Foo foo, int length)
{
    if (foo == null)
    {
        throw new ArgumentNullException("foo");
    }
    if (length < 10)
    {
        throw new ArgumentOutOfRangeException("length", "Length cannot be less then 10");
    }
}

One of the problems with this code is that there is no comments to the parameters, so if you do not look at the implementation you have no way of knowing that Foo cannot be null and length has to be above 9. In some way this is documented by the code, but you cannot expect that everyone looks at the code. This is one of the issues Code Contracts can help with, following is the same method with Code Contracts:

public void MyMethod(Foo foo, int length)
{
    Contract.Requires<ArgumentNullException>(foo != null, "foo");
    Contract.Requires<ArgumentOutOfRangeException>(length > 9, "Length cannot …");
}

The result is the same, if you invoke MyMethod where Foo is null or length is less then 9 an exception will be throw, but wow anyone that has the Code Contracts plugin installed will be presented with the following warnings inside Visual Studio when they try to invoke MyMethod with parameters that does not follow the contracts:

image_thumb1

image_thumb6

(Some Paint manipulation has been done to show both errors at the same time )

 

What have been shown here is the static analysis (Design time) part of Code Contracts, via project properties in visual studio you can decide what part of the contracts you want at runtime. Since there is some overhead in both pre and post-conditions you can turn both off and Code Contracts will not modify the MSIL to include the contracts, thereby eliminating the overhead. Following are the two basic functions that comes with code contracts:

Requires
Contract.Requires insure preconditions, this can be parameter and object state validation, examples:

Contract.Requires<ArgumentNullException>(foo != null, "foo");
Contract.Requires(length > 9);
Contract.Requires(length > 9, "Length cannot be less then 10");
 

Ensures
Contract.Ensures sets postconditions, this is mainly to ensure object state at the end of a method and validate the return value of a method, examples:

public int MyMethod(Foo foo, int length)
{
    Contract.Ensures(foo.Price == Contract.OldValue<double>(foo.Price));
    Contract.Ensures(Contract.Result<double>() > length);
    …
}

There are a number of other functions in code contracts like: assert, assume, ContractInvariantMethod, ContractClass and ContractClassFor, but I have stuck with the basics here. Following are some snippets that are included in the Code Contract plugin:

cim
[ContractInvariantMethod]
void ObjectInvariant()
{
    Contract.Invariant(false);
}
cr
Contract.Requires(false);
crn
Contract.Requires(arg != null);
crsn
Contract.Requires(!String.IsNullOrEmpty(arg));
ce
Contract.Ensures();
cen
Contract.Ensures(Contract.Result<string>() != null);
cesn
Contract.Ensures(!String.IsNullOrEmpty(
Contract.Result<string>()));
cam
Contract.Assume(false);
cca
Contract.Assert(false);
cintf
[ContractClass(typeof(IFooContract))]
public partial interface IFoo
{
}

[ContractClassFor(typeof(IFoo))]
abstract class IFooContract : IFoo
{
}

For more detailed information on Code Contracts see http://msdn.microsoft.com/en-us/magazine/ee236408.aspx

 

UPDATE:
What I might not have made clear is that Code Contracts will compile and run on any machine, but to get “design time” warnings and the ability to decide if contracts should be included or not, then you will need the plugin .

Disposing SPRequest

by Danielvg 17. January 2011 22:20
I was recently asked about the disposing of SPRequest related objects like SPWeb and SPSite, since I was unsure on some of the questions I decided to do some small examples to determine when the following two errors related to disposing of SPRequest show in the ULC:
 
Error 1, Unexpected:
Detected use of SPRequest for previously closed SPWeb object.  Please close SPWeb objects when you are done with all objects obtained from them, but not before.
 
Error 2, High:
An SPRequest object was not disposed before the end of this thread.  To avoid wasting system resources, dispose of this object or its parent (such as an SPSite or SPWeb) as soon as you are done using it.
 
 
Test case Result Id*
public void TestOne() 
{ 
    using (var site = new SPSite(SPContext.Current.Web.Url)) 
    using (var web = site.OpenWeb()) 
    { 
        this.Literal1.Text = web.Title; 
    } 
}
Nothing  
public void TestTwo() 
{ 
    var site = new SPSite(SPContext.Current.Web.Url); 
    var web = site.OpenWeb(); 
    this.Literal1.Text = web.Title; 
}
Error 2
120
110
public void TestThree() 
{ 
    this.Literal1.Text = TestThreeHelper().Title; 
}


public SPWeb TestThreeHelper() 
{ 
    using (var site = new SPSite(SPContext.Current.Web.Url)) 
    using (var web = site.OpenWeb()) 
    { 
        return web; 
    }
}
Error 1
 
public void TestFour() 
{ 
    var site = new SPSite(SPContext.Current.Web.Url); 
    using (var web = site.OpenWeb()) 
    { 
        site.Dispose(); 
        this.Literal1.Text = web.Title; 
    } 
}
Error 1
 
public void TestFive() 
{ 
    SPWeb web = null; 
    using (var site = new SPSite(SPContext.Current.Web.Url)) 
    using (web = site.OpenWeb()) 
    { 
    } 
    this.Literal1.Text = web.Title; 
}
Error 1
 
public void TestSix() 
{ 
    using (var site = new SPSite(SPContext.Current.Web.Url)) 
    { 
        var web = site.OpenWeb(); 
        this.Literal1.Text = web.Title + "test3"; 
    } 
}
Nothing
(SPSite disposes all sub webs, including rootWeb)
120
public void TestSeven() 
{ 
    SPList list = null; 
    using (var site = new SPSite(SPContext.Current.Web.Url)) 
    using (var web = site.OpenWeb()) 
    { 
        list = web.Lists["SomeList"]; 
    } 
    this.Literal1.Text = list.Title; 
}
Error 1
 
public void TestEight() 
{ 
    SPListItem item; 
    using (var site = new SPSite(SPContext.Current.Web.Url)) 
    using (var web = site.OpenWeb()) 
    { 
        var list = web.Lists["SomeList"]; 
        item = list.Items[0]; 
    } 
    this.Literal1.Text = item.Title; 
}
Nothing
(Some properties of SPListItem can be used, however other will reopen the Site, Web and list)
 
public void TestNine() 
{ 
    var site = new SPSite(SPContext.Current.Web.Url); 
    using (var web = site.OpenWeb()) 
    { 
        this.Literal1.Text = web.Title; 
    } 
}
Nothing
(I am pretty sure this will cause an Error 2 like problem when the thread that created the site ends)
110
public void TestTen() 
{ 
    using (var web = TestTenHelper()) 
    { 
        this.Literal1.Text = web.Title; 
    } 
}

public SPWeb TestTenHelper() 
{ 
    var site = new SPSite(SPContext.Current.Web.Url); 
    var web = site.OpenWeb(); 
    return web; 
}
Nothing
(Same as test nice)
120
110
*SPDisposeChecker warning id
I must admit that I am surprised about some of the results, but this at least clears everything up for me. Following is an example of how some of these errors can be resolved:
 
public void TestTreeFix() 
{ 
    Action<SPWeb> action = (openWeb) => { this.Literal1.Text = openWeb.Title; }; 
    TestTreeHelperFix(action); 
}

public void TestTreeHelperFix(Action<SPWeb> action) 
{ 
    using (var site = new SPSite(SPContext.Current.Web.Url)) 
    using (var web = site.OpenWeb()) 
    { 
        action(web); 
    } 
}
 
Dispose them objects! Else you will end up with no available ports and a starving farm!

Host does not support MVC 2

by Danielvg 8. November 2010 23:39

I have been doing a school project in MVC2 and .Net 4 for the last few weeks, and when the time came to deploy the web application to my host (surftown.dk) the fun began…

First Problem: host does not support .Net 4, reason? “The control panel does not support .Net 4”.
Solution: Somewhat easy downgrade to .Net 3.5, the only problem was Entity Framework 4.

Second Problem: MVC2 has not been installed on the host server.
Solution: Thanks to a post by Mr. Haackedthis was an easy fix, since you can deploy the MVC assemblies to the web applications bin folder (Full trust is not needed).

Third Problem: MVC2 routing has not been configured in the host IIS, aka “The website declined to show this webpage”
Solution: Change routing in Global.asax to use “{controller}.Mvc.aspx” instead of “{controller}”, this will trick IIS into routing as expected by MVC2.

Example:
From:

routes.MapRoute("ProfileSearch", "Profile/Search/{query}", new { controller = "Profile"
                action = "Search" });

routes.MapRoute("Default", "{controller}/{action}/{id}"
        new { controller = "Home", action = "Index", id = UrlParameter.Optional });

routes.MapRoute("ProfileView", "Profile/{nickName}", new { controller = "Profile"
               action = "Index" });

To:

routes.Add(new Route("{controller}.mvc.aspx/{action}", new MvcRouteHandler())
{
    Defaults = new RouteValueDictionary(new { controller = "Home" })
});

routes.Add(new Route("{controller}.mvc.aspx/{action}/{query}", new MvcRouteHandler())
{
    Defaults = new RouteValueDictionary(new { controller = "Home", action = "Search" })
});

routes.Add(new Route("Profile.mvc.aspx/{nickName}", new MvcRouteHandler())
{
    Defaults = new RouteValueDictionary(new { controller = "Profile" })
});

The downside is that URLs are ugly ugly ugly.
From “/Profile/MyNickName” to “/Profile.mvc.aspx/MyNickName”

Tags: , ,

Code

SharePoint: Show Ribbon by Default

by Danielvg 11. July 2010 10:53

While working on a project at work I got a Bug work item that said “Ribbon is not shown by default when a new web is created”. Long story short the project involves a custom way of creating new webs in SharePoint, the webs are based on a publishing web but the Ribbon is not shown when the web is created and the users have to click “show Ribbon” which is not desired.

Sadly google/bing was no help so I had to turn to some Javascript debug and reflector to solve to problem. It got me to a property called “__DisplayShowHideRibbonActionId” that is not in a webs propertybag by default and when set to false the ribbon is shown to all users.

Simple code snippet of how this could be implemented(demo code, should not be used in production):


public static string CreateWeb(SPWeb parentWeb, string title, string webTemplate)
{
    parentWeb.AllowUnsafeUpdates = true;
    using (SPWeb newWeb = parentWeb.Webs.Add(title, title, string.Empty, parentWeb.Language, 
                                                              webTemplate, false, false))
    {
        // Show Ribbon by default
        newWeb.AllProperties["__DisplayShowHideRibbonActionId"] = false.ToString();
        newWeb.Update();
        return newWeb.Url;
    }
}

It feels a bit hackish to do it this way, so if anyone knows of a better way to make the ribbon visible when a web is created then please to tell :)

SharePoint 2010 Ribbon Builder (proof of concept)

by Danielvg 6. March 2010 22:57

In an attempt to save some of my WPF and XAML knowledge before it completely faded away,  I decided to make a small application build with the MVVM pattern. The result is a proof of concept application that I been thinking about making for some time. The application is pretty simple, it is possible to add tabs, groups and buttons, to what should look like a ribbon, and get a xml file that can be used to define a SharePoint 2010 Ribbon.

An example; a ribbon with two tabs, the first tab has two groups with a total of 5 buttons and the second tab has one group with one button, as the screenshot below shows:

image

When the “Generate xml” button is pushed, in the example above, the following xml will be saved to a file:


<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="RibbonTest.LeRibbon" Sequence="0">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition>
          <Tab Sequence="0" Title="A New Tab">
            <Scaling />
            <Groups>
              <Group Sequence="0">
                <Controls>
                  <Button Sequence="0" LabelText="Hello" />
                  <Button Sequence="0" LabelText="Hello" />
                </Controls>
              </Group>
              <Group Sequence="0">
                <Controls>
                  <Button Sequence="0" LabelText="Hello" />
                  <Button Sequence="0" LabelText="Hello" />
                  <Button Sequence="0" LabelText="Hello" />
                </Controls>
              </Group>
            </Groups>
          </Tab>
        </CommandUIDefinition>
        <CommandUIDefinition>
          <Tab Sequence="0" Title="A New Tab">
            <Scaling />
            <Groups>
              <Group Sequence="0">
                <Controls>
                  <Button Sequence="0" LabelText="Hello" />
                </Controls>
              </Group>
            </Groups>
          </Tab>
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers />
    </CommandUIExtension>
  </CustomAction>
</Elements>

The generated xml is fare from complete but it does give a decent template to work with.

The application should only be seen as a test for now, but I hope to develop it further and maybe start a Codeplex project at some point. I have been looking for a “pet” project and maybe this will be it, if for nothing else then at least to keep WPF in the fingertips.

EDIT: ups, source code can be found here:

SharePointRibbonBuilder.zip (96,12 kb)

Unity vs MEF vs PRISM

by Danielvg 13. February 2010 21:15

As said in an earlier post I wanted to clarify, at least for myself, the difference between Unity, MEF and PRISM. This post will shortly try to do just that by listing some of the main points and advantages of each technology.

Unity vs MEF

Unity is mostly used for static dependencies and is more an “internal” technology, meaning outsiders do not need to know that unity is being used in the application.

MEF should be used for dynamic dependencies, it is grate at dynamically loading components and with the use of attributes it make creating application extensions or an application that can be extended by others easy and streamlined.

MEF vs PRISM

I would say the main difference between PRISM and MEF is that PRISM is targeted for UI module based application and MEF is more for components with focus on extensibility. 

PRISM has RegionManager, EventAggregator and Commands all with the purpose of solving decoupling issues related to UI components, whereas MEF only focuses on dynamically loading components.

With that said, it seems the PRISM team will use a mixture of MEF and Unity in the next release. Drops of PRISM 4, should be just around the corner!

 

Bottom-line is:

  • MEF is not an IoC container, even though it uses IoC concepts.
  • Unity is an IoC container, which is its main purpose.
  • MEF focuses on application extensibility with component discovery and composition.
  • PRISM is “a complete solution” for developing UI applications.
  • MEF is not “a complete solution” for developing UI applications although it can be used for creating applications that can be extended.

Tags: , ,

General | Code

First Look: Managed Extensibility Framework (MEF)

by Danielvg 14. January 2010 18:36

MEFis a new framework set to simplify the creation of extensible applications, it will ship with the .Net 4 Framework later this year, but there is a preview included in the beta2 version of .Net 4.
I will here try and cover some of the basics by creating a small pointless application, if you want more advanced information and more code examples then the standard documentation is very nice!

MEF pretty much works as an IoC Container, you put everything into a mixing bowl and the rest is just magic. With MEF, attributes are used to mark what properties are dependencies and what objects can be used for injection. These attributes are [Import] for dependencies and [Export] for injectable objects.

The first example is a basic logger function for a WPF app, the logger is injected by MEF, notice the use of Import and Export attributes:


using System.Collections.ObjectModel;
using System.ComponentModel.Composition;

namespace Demo.Mef
{
    public interface ILogger
    {
        ObservableCollection<string> Logs { get; }
        void Log(string message);
    }

    // We set the Logger class to be exported as ILogger
    [Export(typeof(ILogger))]
    public class Logger : ILogger
    {
        private ObservableCollection<string> logs = new ObservableCollection<string>();

        public void Log(string message)
        {
            Logs.Add(message);
        }

        public ObservableCollection<string> Logs
        {
            get { return logs; }
        }
    }

    public partial class MainWindow : Window
    {
         // Mark the Logger to be imported by MEF
         [Import]
         public ILogger Logger { get; set; }

      }
}

Having defined exports and imports the only thing left to do it create the container and mix everything. MEF uses a CompositionContainer as its mixing bowl, the container is able to take catalogs and look through them for known attributes.

using System.ComponentModel.Composition;

namespace Demo.Mef
{
    public partial class MainWindow : Window
    {
        // Mark the Logger to be imported by MEF
        [Import]
        public ILogger Logger { get; set; }

        public MainWindow()
        { 
            InitializeComponent(); 
            Compose(); 
        }

        private void Compose()
        {
            // AssemblyCatalog takes an assembly and  looks for all Imports and Exports within it
            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            // Create the container with the above catalog 
            var container = new CompositionContainer(catalog); 
            // Here we tell the container to mix everything up
            container.ComposeParts(this);
        } 
    }
}

And that is about it, when you run the above code MEF will look through the running assembly for import and export attributes and try to make everything match.
If you want to make sure that all Imports are filled before doing anything else then the interface IPartImportsSatisfiedNotification will help you. If your object implements the interface the method OnImportsSatisfied() will be called.

 

Nothing fancy there, most of the above could be done with Unity, so on to the next example that builds on top of the previous example.

In the following example MEF looks for buttons with the Export contract “Demo.Mef.MenuItems” in all assemblies found in the PlugIns directory.  Then when the form has loaded and MEF is done, all the buttons found are added to a stackpanel.

The following is located in a separate assembly that builds to /PlugIns/, it is simply a button with some behavior and most importantly two attributes.

namespace Demo.Mef.Extra
{
    // Mark the class as export with the contract Demo.Mef.MenuItems
    // so it can be matched with the contract in the main app(),
    // this could be used as a filter
    [Export("Demo.Mef.MenuItems", typeof(Button))]
    // This attribute makes MEF create a new instance each time the button is requested
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class MessageBoxButton : Button
    {
        public MessageBoxButton()
        {
            this.Content = "PresssMeee";
            this.Click += new System.Windows.RoutedEventHandler(MessageBoxButton_Click);
        }

        void MessageBoxButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            MessageBox.Show("Yes this demo also has Hello world!");
        }
    }
}

Since we want more then just one button in the menu we use the Import attribute ImportMany which fills the property with all the Export buttons MEF was able to find with the specified contract

[ImportMany("Demo.Mef.MenuItems")]
public IEnumerable<Button> MenuItems { get; set; }

To make MEF look in the PlugIns directory a few changes has to be made to the Compose method in the main application:


private void Compose()
{
    // AssemblyCatalog takes an assembly and  looks for Imports/Exports within it
    var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
    // DirectoryCatalog takes all assemblies within a given dir and looks for Imports/Exports
    var directoryCatalog = new DirectoryCatalog("PlugIns");

    // AggregateCatalog holds multiple  ComposablePartCatalogs
    var aggregator = new AggregateCatalog();
    aggregator.Catalogs.Add(assemblyCatalog);
    aggregator.Catalogs.Add(directoryCatalog);

    var container = new CompositionContainer(aggregator);
    container.ComposeParts(this);
}

Only thing left to do now is add the buttons to the stackpanel, as said we can use the IPartImportsSatisfiedNotification interface to make sure MEF is done.

public void OnImportsSatisfied()
{
    foreach (var menuItem in MenuItems)
    {
        stackPanelMenu.Children.Add(menuItem);
    }
}

That is about it, should I want to add a new button to the menu all it takes is a button with the export attribute, no need to recompile the main application just drop the dll in the PlugIns directory and the button is added next time the application is started! While all of this has been possible before it is very nice to get an easy integrated way of creating extensible applications.

The result of the above code:

 image

Source code can be found here Demo.Mef.zip (39,03 kb)

 

On a side note, I am somewhat confused about MEF vs Unity vs PRISM and will try to make a post about it soon.

Tags: , , ,

Code

Tuples in .Net 4 (System.Tuple)

by Danielvg 2. January 2010 15:16

.Net 4 brings with it the Tuple type! I have never worked with tuples in programming before and have a hard time seeing their purpose but here is an example of a tuple in C#:


var tupleOne = new Tuple<int, string>(1, "Hello World");
Console.WriteLine("Tuple contains: " + tupleOne.Item1 + " and " + tupleOne.Item2);

There are also some factory methods for creating tuples:


var tupleTwo = Tuple.Create("Hello World", 2010);
var tupleThree = Tuple.Create("Hello World", 2010, new SomeClass());
Console.WriteLine("Tuple contains: " + tupleThree.Item1 + ", " + tupleThree.Item2 + " and " + tupleThree.Item3.MyProperty);

Tuple facts:

  • Tuples are immutable
  • Tuples are reference types
  • Located in System.Tuple
  • Can take up to 8 generic parameters, but can contain an infinite number of elements.
  • Elements are always named Item1, Item2… Item7, Rest

See msdn for more information.

Tags: , ,

Code