Property Attributes
You use properties attributes to define the metadata that the Mapper requires. For example, given this very simple model class:
using System;
public class Subscriber
{
public int SubscriberId { get; set; }
public string Name { get; set; }
public DateTime Expiration { get; set; }
}
Adding mapping attributes to this class provides the metadata to automatically map these properties to stored procedures or SQL statements:
using System;
using ArgentSea.Sql;
public class Subscriber
{
[MapToSqlInt("@SubID", true)]
public int SubscriberId { get; set; }
[MapToSqlNVarChar("@SubscriberName", 255)]
public string Name { get; set; }
[MapToSqlDateTime2("@EndDate")]
public DateTime Expiration { get; set; }
}
Note
The “@” parameter prefix is optional — ArgentSea will always add the “@” automatically for parameters and remove it automatically when reading data reader rows.
Often, due to different naming conventions or development drift, database column names and the corresponding .NET properties names do not match. That is why every attribute requires a “name” argument — which should correspond to the database name. The Mapper will create query parameters and reference DataReader columns based on this name.
Important
Database parameters and columns should be named as consistently as possible. In most cases, this means the parameters have the same name as the columns they reference. If you like to use varying parameter names or alias columns in your result, you will find the Mapper unhelpful.
Properties without a mapping attribute are simply ignored.
Attribute Types
A mapping attribute is defined for most common database types. Attributes for spatial data types, CLR types, XML, and JSON types (for example) are missing because there is no straightforward mapping between the core .NET base types and these database types. ArgentSea supports writing a custom handler to render any of these complex types; such work is no more difficult than writing the same processing in ADO.NET.
The attribute itself defines the underlying database type. Naturally, the attribute type and the property type must match. For example, a long
(Int64) property must map to a bigint
database type. The Mapper will throw an error if these types do not match. There is no attempt to cast data to a different type, even if the cast would be successful.
Many data attribute types have an additional parameters. The length argument, for example, on string and array types, helps optimize data access performance by ensuring that buffers are sized appropriately.
Here is catalog of the current attributes, along with their arguments and corresponding .NET types:
Attribute | Arguments | .NET types | SqlType |
---|---|---|---|
MapToSqlNVarCharAttribute | length¹ | String, Enum², Nullable<Enum> | NVarChar |
MapToSqlNCharAttribute | length | String, Enum², Nullable<Enum> | NChar |
MapToSqlVarCharAttribute | length¹, localeid³ | String, Enum², Nullable<Enum> | VarChar |
MapToSqlCharAttribute | length, localeid³ | String, Enum², Nullable<Enum> | Char |
MapToSqlBigIntAttribute | Int64, Enum⁴, Nullable<Int64>, Nullable<Enum> | BigInt | |
MapToSqlIntAttribute | Int32, Enum⁴, Nullable<Int32>, Nullable<Enum> | Int | |
MapToSqlSmallIntAttribute | Int16, Enum⁴, Nullable<Int16>, Nullable<Enum> | SmallInt | |
MapToSqlTinyIntAttribute | Byte, Enum⁴, Nullable<Byte>, Nullable<Enum> | TinyInt | |
MapToSqlBitAttribute | Boolean, Nullable<Boolean> | Bit | |
MapToSqlDecimalAttribute | precision, scale | Decimal, Nullable<Decimal> | Decimal |
MapToSqlMoneyAttribute | Decimal, Nullable<Decimal> | Money | |
MapToSqlSmallMoneyAttribute | Decimal, Nullable<Decimal> | SmallMoney | |
MapToSqlFloatAttribute | Double, Nullable<Double> | Float | |
MapToSqlRealAttribute | Float, Nullable<Float> | Real | |
MapToSqlDateTimeAttribute | DateTime, Nullable<DateTime> | DateTime | |
MapToSqlDateTime2Attribute | precision | DateTime, Nullable<DateTime> | DateTime2 |
MapToSqlDateAttribute | DateTime, Nullable<DateTime> | Date | |
MapToSqlTimeAttribute | TimeSpan, Nullable<TimeSpan> | Time | |
MapToSqlDateTimeOffsetAttribute | DateTimeOffset, Nullable<DateTimeOffset> | DateTimeOffset | |
MapToSqlVarBinaryAttribute | length¹ | byte[] | VarBinary |
MapToSqlBinaryAttribute | length | byte[] | Binary |
MapToSqlUniqueIdentifierAttribute | Guid, Nullable<Guid> | UniqueIdentifier |
¹ For “max” values (nvarchar(max), varbinary(max), etc.) use length of -1.
² The Enum name is saved as string.
³ Locale Id is the Ansi code page to use for Unicode conversion. For en-US locale, for example, use 1033.
⁴ The Enum value is saved based on its underlying numeric value. The Enum integer base type (int, short, byte, etc.) must match the database type.
Required
Finally, the the data attributes have an optional required
(sic) parameter. If a database field is DbNull, the Mapper will normally set the corresponding property to null. However, the missing value may represent an entirely absent record. In this case, the correct result is a null object, not a valid instance with null/default properties. Setting a property attribute’s required
argument to True causes the Mapper to return a null object if the property would be null. By default (if not specified), required
is false.
Handling Nulls and Empty Types
Because the Mapper is handling database values, there is generally a possibility that the database value is DbNull. How this is converted to a .NET type depends upon the type.
Strings and Arrays
A .NET string with a value of null or a null array will be saved as a DbNull. Empty strings will save as a zero-length string.
Integers
Integers cannot be null, so the advent of nullable types is a godsend for mapping to database storage. To save or retrieve an integer (byte, Int16, Int32, or Int64) database value from a column that allows null, you should declare a nullable value type.
Floating Point Numbers
Like integer types, floating point types (Double and Float) can be wrapped in a nullable value. However, ArgentSea also handles NaN as a DbNull. If the floating point value is presented as a nullable type, then ArgentSea will save or retrieve NaN; if floating point type is presented, then NaN will be converted to/from a DbNull.
Guids
Rather like floating point types, Guid.Empty (00000000-0000-0000-0000-000000000000) will be converted to a data DbNull when read from or written to the database. Also like floats, if you need to write an empty Guid value, wrap it in a nullable type.
Enum Types
.NET enum values can be stored as either numbers or strings. Writing to a text column will automatically save the name of the enum; writing to a numeric column saves the number value.
Warning
Enums can inherit from several base types (byte, short, int, etc.). If you are saving to a numeric database column, the base type must correctly correspond to the database data type. Enums are Int32 by default.
Nullable Enum types will read or write as a DbNull when the value is null.
Model Inheritance
Models which inherit from other Model classes also inherit the attributes of the parent class. This can be very useful when some queries return a subset of the data entities overall columns.
Properties with Object Types
This page has described simple 1:1 mapping between database columns and model properties. This is the heart of the Mapper. However, mapping sometimes requires more complex types, built from these simple relationships.
In addition to the types already described, the Mapper supports three additional types of Model properties:
- Properties which contain a second Model class
- Properties which contain lists of Model classes
- Sharded record identifiers
These will be explored in the next section.
Next: Complex Attributes