Defining Object Mappings

There are two main ways to define object mappings for use by Simol:
  • Apply attributes to your persistent object properties and let Simol construct the mappings automatically
  • Construct ItemMappings manually for use with Simol typeless operations
The same Simol functionality is available with either mapping method, but for most applications the automatic method is simpler and requires less code. The ability to construct and use dynamic mappings is primarily required for tool and infrastructure code that must interface with Simol without referencing persistent object classes explicitly at compile time. Simol also includes utility methods for easily generating ItemMappings from your data classes, for use by applications that need to mix the two approaches. See the section on Typeless and Partial-Object Operations for more information.

Default Mapping Behavior

The following rules are applied by Simol when building default (non-customized) mappings:
  • The SimpleDB domain name is derived from Type.Name.
  • SimpleDB attribute names are derived from PropertyInfo.Name.
  • The SimpleDB item name property must be marked by an ItemNameAttribute.
Properties are automatically included in a mapping if they are:
  1. Both readable and writable
  2. One of the default supported property types. These include String, all value types (i.e. Boolean, Int32, Double, etc.), and concrete generic collections that hold strings or value types (i.e. List<String>, Collection<int>, etc.).

Default Formatting Behavior

The following formatting rules are applied by Simol for default (non-customized) mappings:
  • DateTime values are formatted using the custom date format string 'yyyy-MM-ddTHH:mm:ss.fffK', which follows the ISO 8601 format to allow lexicographical ordering. The "K" specifier is used to preserve the DateTimeKind information. For more information see: http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx#KSpecifier
  • Numeric type values are zero-padded to the maximum number of whole digits supported by the numeric type. For example, a Byte value of 3 will be stored as "003", a UInt32 value of 3 as "0000000003", etc.
  • Signed numeric types are offset by 10 raised to the nth power, where n is the maximum number of whole digits supported by the numeric type. For example, an SByte value of 3 will be stored as "1003", an Int32 value of 3 will be stored as "10000000003", etc. This allows lexicographical ordering of a full range of positive and negative values. (Negative number offsets may be disabled completely by setting SimolConfig.OffsetNumbers to Offset.None.)
  • Unsigned numeric types are not offset.
  • Floating point types are formatted so as to provide a reasonable balance between size and precision. This can be easily customized on a case-by-case basis so as to store floating point values that are either larger or more precise.
  • Default formatters are also provided for the following .NET built-in value types: DateTime, TimeSpan, Guid and Enum.

Here are full details on the default formatting behavior for all numeric types:

Type Whole Digits Decimal Digits Offset Example
Byte 3 0 No 005
SByte 3 0 Yes 1005
Int16 5 0 Yes 100005
UInt16 5 0 No 00005
Int32 10 0 Yes 10000000005
UInt32 10 0 No 0000000005
Int64 19 0 Yes 10000000000000000005
UInt64 20 0 No 00000000000000000005
Single 3 4 Yes 1005.0000
Double 7 8 Yes 10000005.00000000
Decimal1 18 10 Yes 1000000000000000005.0000000000

1The range of Decimal values is limited to one digit less than described in the .NET type library documentation as a result of being stored in SimpleDB. All other numeric types are capable of holding the full range of values described in the .NET API documents.

Null Property Values

Simol offers two options for handling null property values when storing data in SimpleDB:
  • NullBehavior.Ignore - Simol doesn't do anything with null property values. Your application is responsible for explicitly deleting previously saved attribute values mapped to null properties.
  • NullBehavior.MarkAsNull - Simol stores a single null character ('\0') to indicate null properties. Previously saved attribute values that are mapped to null properties will be overwritten.
To change the way null properties are handled by Simol set the SimolConfig.NullPutBehavior configuration property. The default setting is MarkAsNull.

Mapping and Formatting Customization

Several Attributes are available for altering the default mapping and formatting rules:
  • DomainNameAttribute allows customization of domain names.
  • AttributeNameAttribute allows customization of attribute names.
  • SimolExcludeAttribute can be used to exclude properties from the mapping.
  • SimolIncludeAttribute can be used to explicitly include properties in the mapping. Once the include attribute is used all other properties are excluded by default unless they also are explicitly included. A property is explicitly included if marked with any Simol property attribute.
  • NumberFormatAttribute allows custom formatting of numeric properties.
  • CustomFormatAttribute allows custom formatting of any property.
Custom number formatting allows you to tune the size and precision of numeric values stored in SimpleDB. If, for example, you had a numeric property that would never be larger than 10, you might wish to squeeze the number down to 1 character. For example, if we wanted to store the day of the week as an ordinal value from 1-7 we could define the following mapping:

    public class Appointment
    {
        [ItemName]
        public Guid Id { get; set; }
        
        [NumberFormat(1, 0, false)]
        public byte DayOfWeek { get; set; }
    }

The mapping customization above tells Simol to store DayOfWeek using 1 whole digit and 0 decimal digits, with no negative-number offset. For example: "1", "3", etc.

Or if we needed to store extremely small, precise values for scientific purposes we could shift the default Double format toward greater precision by increasing the number of decimal digits and reducing the number of whole digits. For example, we might need up to 12 decimal places to store the weights of feathers in tons:

    public class FeatherWeight
    {
        [ItemName]
        public Guid Id { get; set; }

        [NumberFormat(0, 12, false)]
        public double Tons { get; set; }
    }

This tells Simol to store Tons using 0 whole digits and 12 decimal digits, again with no offset: For example: ".0012345", ".000000009", etc.

The CustomFormatAttribute allows two types of customization. The first option is to provide a format string for use with properties whose types implement System.IFormattable. Here's an example that stores only the year, month, and day of a DateTime property:

    public class Employee
    {
        [ItemName]
        public Guid Id { get; set; }

        [CustomFormat("yyyy-MM-dd")]
        public DateTime HireDate { get; set; }
    }

Warning: Round-trip formatting is not always supported when using format strings in this manner. For example, some custom date format strings do not contain enough information to be converted back into DateTime objects.

You can also use the CustomFormatAttribute to plug in custom formatting code using the ITypeFormatter interface. In the example below we're applying a custom formatter that always converts the Employee.Email property to lower case before storing it in SimpleDB. One nice side effect of this approach is that we would never again need to worry about case mismatches when comparing email addresses, as the same formatter would be applied when referencing the email address property in select queries.

    public class Employee
    {
        [ItemName]
        public Guid Id { get; set; }

        [CustomFormat("yyyy-MM-dd")]
        public DateTime HireDate { get; set; }

        [CustomFormat(typeof(LowerCaseFormatter))]
        public string Email { get; set; }
    }    

    public class LowerCaseFormatter : ITypeFormatter
    {
        public string ToString(object value)
        {
            return value.ToString().ToLower();
        }

        public object ToType(string valueString, Type expected)
        {
            return valueString;
        }
    }

Collections, Arrays, and Multi-Valued Attributes

Here are the most important rules to remember when dealing with collections and arrays:
  • Property types that implement ICollection<T> are stored and formatted as multi-valued attributes except arrays.
  • Property types must be concrete--interfaces and abstract classes are not supported by Simol.

Dictionaries

Dictionaries are supported, but you must provide a custom ITypeFormatter that serializes the KeyValuePair<TKey, TValue> objects returned by the dictionary's ICollection<T> interface. Use a CustomFormatAttribute to associate your formatter with the dictionary property.

Arrays

Arrays are always treated as single-valued attributes, even when they contain complex objects. Just as with dictionaries you must provide your own custom formatter and mark array properties with CustomFormatAttribute. Simol does include one custom formatter (ByteArrayFormatter) for serializing byte[] properties. Here's a usage example:

    public class Employee
    {
        [ItemName]
        public Guid Id { get; set; }

        [CustomFormat(typeof(ByteArrayFormatter))]
        public byte[] ThumbPrint { get; set; }
    }

Global Formatter Replacement

There may be times when you need to globally install a custom formatter for a particular type, rather than applying custom formatting over and over again on your property mappings. In such cases you can get the formatter registry from SimolConfig and register the new formatter:

    PropertyFormatter formatter = simol.Config.Formatter;
    ITypeFormatter dateFormatter = new MyCustomDateFormatter();
    formatter.SetFormatter(typeof (DateTime), dateFormatter);

The code above changes the default formatting logic for all DateTime properties stored or retrieved by that instance of SimolClient.

You can use this same access to get and use the default formatters for your own purposes:

    PropertyFormatter formatter = simol.Config.Formatter;
    ITypeFormatter dateFormatter = formatter.GetFormatter(typeof (DateTime));
    string date = dateFormatter.ToString(DateTime.Now);

Last edited Jul 7, 2011 at 2:31 PM by ashleytate, version 42

Comments

ashleytate May 10, 2011 at 7:18 PM 
Thanks!

Foreverlearninig May 8, 2011 at 10:35 AM 
Excellent work.