• Stars
    star
    108
  • Rank 321,259 (Top 7 %)
  • Language
    C#
  • Created almost 13 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Extensible dynamic types for .NET

Expando Class

Extensible dynamic types for .NET that support both static and dynamic properties

Expando is a .NET class that allows you to create extensible types that mix the functionality of static and dynamic types. You can create static types inherited from Expando that have all the features of the static type, but when cast to dynamic support extensibility via dynamic C# features that allow you to add properties and methods at runtime. You can also create mix-ins that combine the properties from two objects into a single object.

The library supports two-way serialization with JSON.NET and XmlSerializer which allows building extensible types that can persist and restore themselves. This is very useful for data models that can expose extensible custom properties that can be persisted as serialized strings for example.

This class provides functionality similar to the native ExpandoObject class, but with many more features for working with existing statically typed Types, the ability to serialize and to inherit from to extend existing types.

Features

Expando has the following features:

  • Allows strongly typed classes (must inherit from Expando)
  • Supports strongly typed Properties and Methods
  • Supports dynamically added Properties and Methods
  • Allows extension of strongly typed classes with dynamic features
  • Supports string based collection access to properties
    (both on static and dynamic properties)
  • Create Mix-ins of two types that combine properties
  • Supports two-way JSON.NET and XML serialization

You can find out more detail from this blog post: Creating a dynamic, extensible C# Expando Object

Installation

You can use this class with source code provided here or by using the Westwind.Utilities NuGet package.

pm> Install-Package Westwind.Utilities

Example Usage

This class essentially acts as a mix-in where you can create a strongly typed object and add dynamic properties to it.

To start create a class that inherits from the Expando class and simply create your class as usual by adding properties:

public class User : Westwind.Utilities.Dynamic.Expando
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string Name { get; set; }
    public bool Active { get; set; }
    public DateTime? ExpiresOn { get; set; }

    public User() : base()
    { }

    // only required if you want to mix in seperate instance
    public User(object instance)
        : base(instance)
    {
    }
}

Then simply instantiate the class. If you reference the strongly typed class properties you get the strongly typed interface - ie. your declared properties:

var user = new User();
user.Email = "[email protected]"

If you cast the object to dynamic you can also attach and read any new properties.

dynamic duser = user;
duser.WhatsUp = "Hella"
duser["WhatTime"] = DateTime.Now;

string wu = duser.WhatsUp;
string wt = duser["WhatTime"];
wu = duser.["WhatsUp"]; // also works
wt = duser.WhatTime;  // also works

The following sequence demonstrates in more detail:

var user = new User();

// Set strongly typed properties
user.Email = "[email protected]";
user.Password = "nonya123";
user.Name = "Rickochet";
user.Active = true;

// Now add dynamic properties
dynamic duser = user;
duser.Entered = DateTime.Now;
duser.Accesses = 1;

// you can also add dynamic props via indexer 
user["NickName"] = "AntiSocialX";
duser["WebSite"] = "http://www.west-wind.com/weblog";
        
// Access strong type through dynamic ref
Assert.AreEqual(user.Name,duser.Name);

// Access strong type through indexer 
Assert.AreEqual(user.Password,user["Password"]);
        

// access dyanmically added value through indexer
Assert.AreEqual(duser.Entered,user["Entered"]);
        
// access index added value through dynamic
Assert.AreEqual(user["NickName"],duser.NickName);
        

// loop through all properties dynamic AND strong type properties (true)
foreach (var prop in user.GetProperties(true))
{ 
    object val = prop.Value;
    if (val == null)
        val = "null";

    Console.WriteLine(prop.Key + ": " + val.ToString());
}

Serialization

The Expando class supports JSON.NET and XmlSerializer two-way serialization. So you can create objects that contain combined static and dynamic properties and have both persist and restore to and from serialized content.

// Set standard properties
var ex = new User()
{
    Name = "Rick",
    Email = "[email protected]",
    Password = "Seekrit23",
    Active = true
};

// set dynamic properties
dynamic exd = ex;
exd.Entered = DateTime.Now;
exd.Company = "West Wind";
exd.Accesses = 10;

// set dynamic properties as dictionary
ex["Address"] = "32 Kaiea";
ex["Email"] = "[email protected]";
ex["TotalOrderAmounts"] = 51233.99M;

// *** Should serialize both static properties dynamic properties
var json = JsonConvert.SerializeObject(ex, Formatting.Indented);
Console.WriteLine("*** Serialized Native object:");
Console.WriteLine(json);

Assert.IsTrue(json.Contains("Name")); // static
Assert.IsTrue(json.Contains("Company")); // dynamic


// *** Now deserialize the JSON back into object to 
// *** check for two-way serialization
var user2 = JsonConvert.DeserializeObject<User>(json);
json = JsonConvert.SerializeObject(user2, Formatting.Indented);
Console.WriteLine("*** De-Serialized User object:");
Console.WriteLine(json);

Assert.IsTrue(json.Contains("Name")); // static
Assert.IsTrue(json.Contains("Company")); // dynamic

This produces the following JSON that mixes both the static and dynamic properties as a single JSON object literal:

{
  "Email": "[email protected]",
  "Password": "Seekrit23",
  "Name": "Rick",
  "Active": true,
  "ExpiresOn": null,
  "Entered": "2015-01-28T11:22:18.3548271-10:00",
  "Company": "West Wind",
  "Accesses": 10,
  "Address": "32 Kaiea",
  "Email": "[email protected]",
  "TotalOrderAmounts": 51233.99
}

The same code using XML Serialization:

// Set standard properties
var ex = new User();
ex.Name = "Rick";
ex.Active = true;


// set dynamic properties
dynamic exd = ex;
exd.Entered = DateTime.Now;
exd.Company = "West Wind";
exd.Accesses = 10;

// set dynamic properties as dictionary
ex["Address"] = "32 Kaiea";
ex["Email"] = "[email protected]";
ex["TotalOrderAmounts"] = 51233.99M;

// Serialize creates both static and dynamic properties
// dynamic properties are serialized as a 'collection'
string xml;
SerializationUtils.SerializeObject(exd, out xml);
Console.WriteLine("*** Serialized Dynamic object:");
Console.WriteLine(xml);

Assert.IsTrue(xml.Contains("Name")); // static
Assert.IsTrue(xml.Contains("Company")); // dynamic

// Serialize
var user2 = SerializationUtils.DeSerializeObject(xml,typeof(User));
SerializationUtils.SerializeObject(exd, out xml);
Console.WriteLine(xml);

Assert.IsTrue(xml.Contains("Rick")); // static
Assert.IsTrue(xml.Contains("West Wind")); // dynamic

Produces the following XML:

<?xml version="1.0" encoding="utf-8"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Properties>
      <item>
         <key>Entered</key>
         <value type="datetime">2015-01-28T11:24:27.0119386-10:00</value>
      </item>
      <item>
         <key>Company</key>
         <value>West Wind</value>
      </item>
      <item>
         <key>Accesses</key>
         <value type="integer">10</value>
      </item>
      <item>
         <key>Address</key>
         <value>32 Kaiea</value>
      </item>
      <item>
         <key>Email</key>
         <value>[email protected]</value>
      </item>
      <item>
         <key>TotalOrderAmounts</key>
         <value type="decimal">51233.99</value>
      </item>
   </Properties>
   <Name>Rick</Name>
   <Active>true</Active>
   <ExpiresOn xsi:nil="true" />
</User>

Note that the XML serializes the dynaimc properties as a collection courtesy of the PropertyBag() custom XML serializer. Although this XML schema isn't as clean as the JSON, it does work with two-way serialization to properly deserialize the object.

Downsides

There are a few issues you should be aware of when you use this class. They're not show stoppers by any means, but keep the following in mind:

Must inherit

First, you have to inherit from Expando in order to use it to extend an existing type. So you'll always introduce an extra layer of inheritance. For many use cases this isn't a problem, but if you need to inherit multiple levels this can become a problem. A work around for this is to use composition by adding an Expando object property to an existing object to provide the dynamic extensibility and mix-in features to your type.

Noisy Class Interface

This class inherits from DynamicObject and implements the required methods on that class to provide the dynamic features. These methods end up on the class interface you inherit. Not clean, but luckily most base methods group together (TryXXXX methods).

Performance

If you stick purely to the static interface of the class performance should be excellent. But once you start accessing the dynamic properties you're into late runtime binding (or at least one-time late binding) and there will be some performance hit for using dynamic properties. However, dynamic can actually be very fast after the first access of each member. It's a tradeoff for the flexibility you get. Make sure you use a static reference for the static interface where possible and explicitly use the dynamic interface when accessing the dynamic properties.

License

This Expando library is an open source and licensed under MIT license, and there's no charge to use, integrate or modify the code for this project. You are free to use it in personal, commercial, government and any other type of application. Commercial licenses are also available.

All source code is copyright West Wind Technologies, regardless of changes made to them. Any source code modifications must leave the original copyright code headers intact.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

copyright, West Wind Technologies, 2012 - 2015

More Repositories

1

MarkdownMonster

An extensible Markdown Editor, Viewer and Weblog Publisher for Windows
HTML
1,584
star
2

Westwind.Globalization

Database driven resource localization for .NET applications
C#
543
star
3

AlbumViewerVNext

West Wind Album Viewer ASP.NET Core and Angular Sample
JavaScript
505
star
4

Westwind.AspnetCore.LiveReload

ASP.NET Core Live Reload Middleware that monitors file changes in your project and automatically reloads the browser's active page
C#
470
star
5

WestWindWebSurge

Quick and easy URL and Load Testing for your Web applications on Windows
388
star
6

WestwindToolkit

A utility toolkit for .NET development from Core to Web
C#
273
star
7

Westwind.Utilities

A general purpose utility and helper library for .NET development
C#
255
star
8

Westwind.AspNetCore.Markdown

An ASP.NET Core Markdown support library that provides Markdown parsing, a Markdown TagHelper and Markdown Page Handler Middleware
C#
248
star
9

jquery-resizable

A small jQuery plug-in to make DOM components resizable
PowerShell
234
star
10

Westwind.Scripting

Small C# library to provide dynamic runtime code compilation from source code for code and expressions execution
C#
205
star
11

Westwind.ApplicationConfiguration

Strongly typed, code-first configuration classes for .NET applications
C#
178
star
12

Westwind.RazorHosting

Hosting the Razor Runtime outside of ASP.NET/MVC for use in non-Web .NET applications.
C#
144
star
13

jquery-watch

A jQuery plug-in to watch CSS style and attribute changes and get notified when a change occurs
JavaScript
133
star
14

SetResolution

Quickly set Windows Display Resolution via Command Line
C#
128
star
15

Westwind.AspNetCore

ASP.NET Core Helpers and Utilities
C#
126
star
16

LiveReloadServer

A self-contained, local, cross-platform, static file Web Server based on .NET with automatic Live Reloading, Markdown rendering and loose Razor Pages support.
CSS
100
star
17

json.date-extensions

Date parsing extensions for the JavaScript JSON parser to provide real dates from JSON.parse()
JavaScript
93
star
18

wwDotnetBridge

.NET Interop for Visual FoxPro made easy
C#
73
star
19

AspNetWebApiArticle

Source code for ASP.NET WebApi Article for Code Magazine
JavaScript
66
star
20

DeleteFiles

Windows Console Utility to delete files and folders recursively with optional date filtering
C#
62
star
21

CodePaste.NET

CodePaste.NET site code
JavaScript
58
star
22

highlightjs-badge

Addon component to highlightjs that lets you copy code snippets to the clipboard and displays the active syntax
CSS
46
star
23

HtmlSanitizer

A base Html Sanitizer for .NET
C#
35
star
24

Westwind.HtmlPackager

A small utility class used to package HTML content into a self contained HTML document both as a single file, or a folder with all dependencies copied to local.
C#
35
star
25

AspNetFrameworksPerformance

ASP.NET Raw Throughput Performance Post Sample
C#
31
star
26

AspNetCoreRawRequestSample

Sample code for Accepting Raw Request Body in Asp.NET Core Applications
C#
31
star
27

CordovaAlbumViewer

Sample code for "Taming Mobile Apps with Cordova and Visual Studio" CODE magazine article
JavaScript
30
star
28

Westwind.plUploadHandler

An ASP.NET base HttpHandler library to handle plUpload content on the server.
JavaScript
28
star
29

wwMongoDb

Access MongoDb from Visual FoxPro
xBase
25
star
30

WestWind.WebView.HtmlToPdf

Creating Pdf output from Html with .NET on Windows using the WebView2 control
HTML
25
star
31

Westwind.Wpf.Statusbar

A small WPF library to provide animated status bar operations
C#
24
star
32

VisualStudioSnippetConverter

Utility to convert Visual Studio Code Snippets to VS Code and Rider.
C#
22
star
33

vue-mover

A 2 list mover component implemented as a VueJs Component
JavaScript
19
star
34

Westwind.Web.Markdown

Markdown support for ASP.NET WebForms and MVC applications
C#
19
star
35

DotnetDesktopRuntimeInstaller

A tiny Windows console executable that can be distributed with an application and called from an installer to download and install the .NET runtime.
C#
18
star
36

GistIntegration-MarkdownMonster-Addin

Markdown Monster addin to create embeddable Gists for Markdown documents, and to load and save documents from Gists.
C#
17
star
37

Westwind.QueueMessageManager

.NET Library to provide a simple, two-way messaging queue for enabling offloading of long running operations to other processes/machines.
JavaScript
16
star
38

Westwind.WebView

A .NET support library for the `Microsoft.Web.WebView2` control to aid with common operations and .NET / JavaScript interop.
HTML
14
star
39

AspNetCoreFromScratchSample

Working example to experiment with building Asp.net core from scratch
C#
14
star
40

SignalrChatSample

Sample SignalR Chat application that demonstrates VueJs, Angular, FoxPro and .NET clients to SignalR
JavaScript
12
star
41

Westwind.AspNetCore.HostedWebServer

Sample that demonstrates how to create a minmal ASP.NET Web Server that can be hosted in a desktop (or other non-Web) application easily.
C#
12
star
42

Westwind.Data.EfCore

Lightweight Business Object wrapper for Entity framework that handles CRUD operations and error handling.
C#
12
star
43

BlogPosts

HTML
11
star
44

MarkdownMonsterAddinsRegistry

Markdown Monster Addin Registry
10
star
45

Pandoc-MarkdownMonster-Addin

A Pandoc Markdown Parser and Pandoc Operations Library for Markdown Monster
C#
10
star
46

Commander-MarkdownMonster-Addin

Commander C# Scripting Addin that can be used to automate simple tasks in Markdown Monster without creating an Addin. Launch external tools, update documents or mail merge content into the editor.
C#
10
star
47

WpfWebView2Playground

JavaScript
9
star
48

DI2017-AspNet-Core-Angular

Session Materials for Dev Intersection ASP.NET Core and Angular Session
TypeScript
9
star
49

CodeMagazine-DotnetTools

Code Magazine Article Materials for Dotnet Tools Article
C#
8
star
50

VirtualFoxFest2021-FoxProRest

JavaScript
8
star
51

SaveToAzureBlob-MarkdownMonster-Addin

Markdown Monster Addin to open or paste images from your local machine and save them in Azure Blob Storage and embed link into your post.
C#
8
star
52

anti-trust-guilty-album

Anti-Trust Guilty Album - Punk Rock Music
JavaScript
7
star
53

Westwind.Webstore

West Wind Web Store Sample application
JavaScript
7
star
54

HelpBuilderReleases

West Wind Html Help Builder Releases and Bug Reporting
6
star
55

MarkdownMonsterReleases

6
star
56

Snippets-MarkdownMonster-Addin

A Snippet Expansion Manager for embedding templated text into Markdown Monster Markdown documents
C#
6
star
57

West-Wind-Message-Board

West Wind Message Board Web Connection (FoxPro) Sample Application
JavaScript
6
star
58

AspetCoreIISInprocessHostingSample

Simple test project optimized for Hello World style controller ops for comparison of hosting options on Windows
C#
5
star
59

DI2017-ASP.NET-Core-Localization

Session materials for DevIntersection 2017 - ASP.NET Core Localization
JavaScript
5
star
60

Padnug-2016-Angular-AspNet

Sample application and Slides from Rick's May 2nd, 2016 Angular ASP.NET Core Presentation
TypeScript
5
star
61

Console-MarkdownMonster-Addin

A simple Console/Terminal addin for Markdown Monster that displays a terminal window attached to the main Markdown Monster Window
C#
5
star
62

Westwind.Ai

Sample project that demonstrates OpenAI Image Generation
C#
5
star
63

datepicker-native

JavaScript and Vue helper components to make it easier to bind dates to the input/date control and a date button picker.
JavaScript
5
star
64

Westwind.AI

C#
4
star
65

Blazor_Playground

Blazor Sample Application
C#
4
star
66

WestwindWebSurgeReleases

Repository that holds releases for West Wind WebSurge
4
star
67

GithubRespositoryParser

C# Github Repository and Content Retrieval Examples using regular and GraphQL APIs.
C#
3
star
68

MarkdownMonsterAddinProjectTemplate

A Visual Studio Project Template for creating a Markdown Monster Addin
C#
3
star
69

Base64

C#
3
star
70

QfxToQboConverter

A small utility to fix up Quicken QIF files (.qfx) so they can be opened as QBO in Quickbooks.
C#
3
star
71

VirtualFoxFest2022-FoxProDotnet

Session Notes and Samples for Virtual FoxFest FoxPro .NET Interop Session
xBase
3
star
72

Live-Writer-SnagIt-Screen-Capture-Plugin

A Live Writer plug-in to provide screen capturing using TechSmith's SnagIt
C#
3
star
73

SWFOX2019_Vue

JavaScript
3
star
74

SWFOX18_WebConnectionSecurity

2
star
75

samples.west-wind.com

West Wind Samples Web Site
HTML
2
star
76

ChromiumPreview-MarkdownMonster-Addin

A Chromium Preview Browser Addin for the Markdown Monster Markdown Editor
C#
2
star
77

Westwind.Weblog

Source for Westwind Weblog
C#
2
star
78

KavaDocs-MarkdownMonster-Addin

KavaDocs Integration for Markdown Monster
C#
2
star
79

EmptyWebContentProjectTemplate

Empty Content Web Site Project that publishes from the root folder and works optionally with WebDeploy
HTML
2
star
80

SWFOX2019_DotnetCore

JavaScript
2
star
81

CODE.Framework.Core.ServiceHandler

C#
1
star
82

MarkdownMonsterDocumentation

JavaScript
1
star
83

swfox2024-wwdotnetbridge-revisited

Samples, Session Notes and Slides for Southwest Fox 2024 Sessions
HTML
1
star
84

dotnet-tools-padnug2020

1
star
85

WebSurgeDocumentation

Documentation for West Wind WebSurge
JavaScript
1
star
86

WebConnection-WebDemo

Web Connection Sample Application for the Getting Started Walk Through
JavaScript
1
star
87

HighPerformanceAspNet

C#
1
star
88

SWFOX2019_WebConnectionDeployment

PowerShell
1
star
89

SWFOX2018_MarkdownWithFoxPro

JavaScript
1
star