using System; using System.Collections.Generic; using System.Data; using System.Data.Linq; using System.Data.Linq.Mapping; using System.Globalization; namespace LinqToSqlShared.Mapping { /// /// DatabaseMapping and related classes represent a parsed version of the /// XML mapping string. This unvalidated intermediate representation is /// necessary because unused mappings are intentially never validated. /// internal class DatabaseMapping { string databaseName; string provider; List tables; List functions; internal DatabaseMapping() { this.tables = new List(); this.functions = new List(); } internal string DatabaseName { get { return this.databaseName; } set { this.databaseName = value; } } internal string Provider { get { return this.provider; } set { this.provider = value; } } internal List Tables { get { return this.tables; } } internal List Functions { get { return this.functions; } } internal TableMapping GetTable(string tableName) { foreach (TableMapping tmap in this.tables) { if (string.Compare(tmap.TableName, tableName, StringComparison.Ordinal) == 0) return tmap; } return null; } internal TableMapping GetTable(Type rowType) { foreach (TableMapping tableMap in this.tables) { if (this.IsType(tableMap.RowType, rowType)) { return tableMap; } } return null; } private bool IsType(TypeMapping map, Type type) { if (string.Compare(map.Name, type.Name, StringComparison.Ordinal) == 0 || string.Compare(map.Name, type.FullName, StringComparison.Ordinal) == 0 || string.Compare(map.Name, type.AssemblyQualifiedName, StringComparison.Ordinal) == 0) return true; foreach (TypeMapping subMap in map.DerivedTypes) { if (this.IsType(subMap, type)) return true; } return false; } internal FunctionMapping GetFunction(string functionName) { foreach (FunctionMapping fmap in this.functions) { if (string.Compare(fmap.Name, functionName, StringComparison.Ordinal) == 0) return fmap; } return null; } } /// /// Constants in the mapping schema. /// class XmlMappingConstant { internal const string Association = "Association"; internal const string AutoSync = "AutoSync"; internal const string Column = "Column"; internal const string Database = "Database"; internal const string DbType = "DbType"; internal const string DeleteRule = "DeleteRule"; internal const string DeleteOnNull = "DeleteOnNull"; internal const string Direction = "Direction"; internal const string ElementType = "ElementType"; internal const string Expression = "Expression"; internal const string False = "false"; internal const string Function = "Function"; internal const string InheritanceCode = "InheritanceCode"; internal const string IsComposable = "IsComposable"; internal const string IsDbGenerated = "IsDbGenerated"; internal const string IsDiscriminator = "IsDiscriminator"; internal const string IsPrimaryKey = "IsPrimaryKey"; internal const string IsInheritanceDefault = "IsInheritanceDefault"; internal const string IsForeignKey = "IsForeignKey"; internal const string IsUnique = "IsUnique"; internal const string IsVersion = "IsVersion"; internal const string MappingNamespace = "http://schemas.microsoft.com/linqtosql/mapping/2007"; internal const string Member = "Member"; internal const string Method = "Method"; internal const string Name = "Name"; internal const string CanBeNull = "CanBeNull"; internal const string OtherKey = "OtherKey"; internal const string Parameter = "Parameter"; internal const string Provider = "Provider"; internal const string Return = "Return"; internal const string Storage = "Storage"; internal const string Table = "Table"; internal const string ThisKey = "ThisKey"; internal const string True = "true"; internal const string Type = "Type"; internal const string UpdateCheck = "UpdateCheck"; } internal class TableMapping { string tableName; string member; TypeMapping rowType; internal TableMapping() { } internal string TableName { get { return this.tableName; } set { this.tableName = value; } } internal string Member { get { return this.member; } set { this.member = value; } } internal TypeMapping RowType { get { return this.rowType; } set { this.rowType = value; } } } internal class FunctionMapping { string name; string methodName; bool isComposable; List parameters; List types; ReturnMapping funReturn; internal FunctionMapping() { this.parameters = new List(); this.types = new List(); } internal string Name { get { return this.name; } set { this.name = value; } } internal string MethodName { get { return this.methodName; } set { this.methodName = value; } } internal bool IsComposable { get { return this.isComposable; } set { this.isComposable = value; } } internal string XmlIsComposable { get { return this.isComposable ? XmlMappingConstant.True : null; } set { this.isComposable = (value != null) ? bool.Parse(value) : false; } } internal List Parameters { get { return this.parameters; } } internal List Types { get { return this.types; } } internal ReturnMapping FunReturn { get { return this.funReturn; } set { this.funReturn = value; } } } internal enum MappingParameterDirection { In, Out, InOut } internal class ParameterMapping { string name; string parameterName; string dbType; MappingParameterDirection direction; internal string Name { get { return this.name; } set { this.name = value; } } internal string ParameterName { get { return this.parameterName; } set { this.parameterName = value; } } internal string DbType { get { return this.dbType; } set { this.dbType = value; } } public string XmlDirection { get { return this.direction == MappingParameterDirection.In ? null : this.direction.ToString(); } set { this.direction = (value == null) ? MappingParameterDirection.In : (MappingParameterDirection)Enum.Parse(typeof(MappingParameterDirection), value, true); } } public MappingParameterDirection Direction { get { return this.direction; } set { this.direction = value; } } } internal class ReturnMapping { string dbType; internal string DbType { get { return this.dbType; } set { this.dbType = value; } } } internal class TypeMapping { string name; TypeMapping baseType; List members; string inheritanceCode; bool isInheritanceDefault; List derivedTypes; internal TypeMapping() { this.members = new List(); this.derivedTypes = new List(); } internal TypeMapping BaseType { get { return this.baseType; } set { this.baseType = value; } } internal string Name { get { return this.name; } set { this.name = value; } } internal List Members { get { return this.members; } } internal string InheritanceCode { get { return this.inheritanceCode; } set { this.inheritanceCode = value; } } internal bool IsInheritanceDefault { get { return this.isInheritanceDefault; } set { this.isInheritanceDefault = value; } } internal string XmlIsInheritanceDefault { get { return this.isInheritanceDefault ? XmlMappingConstant.True : null; } set { this.isInheritanceDefault = (value != null) ? bool.Parse(value) : false; } } internal List DerivedTypes { get { return this.derivedTypes; } } } internal abstract class MemberMapping { string name; string member; string storageMember; internal MemberMapping() { } internal string DbName { get { return this.name; } set { this.name = value; } } internal string MemberName { get { return this.member; } set { this.member = value; } } internal string StorageMemberName { get { return this.storageMember; } set { this.storageMember = value; } } } internal sealed class ColumnMapping : MemberMapping { string dbType; string expression; bool isPrimaryKey; bool isDBGenerated; bool isVersion; bool isDiscriminator; bool? canBeNull = null; UpdateCheck updateCheck; AutoSync autoSync; internal ColumnMapping() { } internal string DbType { get { return this.dbType; } set { this.dbType = value; } } internal bool? CanBeNull { get { return this.canBeNull; } set { this.canBeNull = value; } } internal string XmlCanBeNull { get { if (this.canBeNull == null) return null; return this.canBeNull == true ? null : XmlMappingConstant.False; } set { this.canBeNull = (value != null) ? bool.Parse(value) : true; } } internal string Expression { get { return this.expression; } set { this.expression = value; } } internal bool IsPrimaryKey { get { return this.isPrimaryKey; } set { this.isPrimaryKey = value; } } internal string XmlIsPrimaryKey { get { return this.isPrimaryKey ? XmlMappingConstant.True : null; } set { this.isPrimaryKey = (value != null) ? bool.Parse(value) : false; } } internal bool IsDbGenerated { get { return this.isDBGenerated; } set { this.isDBGenerated = value; } } internal string XmlIsDbGenerated { get { return this.isDBGenerated ? XmlMappingConstant.True : null; } set { this.isDBGenerated = (value != null) ? bool.Parse(value) : false; } } internal bool IsVersion { get { return this.isVersion; } set { this.isVersion = value; } } internal string XmlIsVersion { get { return this.isVersion ? XmlMappingConstant.True : null; } set { this.isVersion = (value != null) ? bool.Parse(value) : false; } } internal bool IsDiscriminator { get { return this.isDiscriminator; } set { this.isDiscriminator = value; } } internal string XmlIsDiscriminator { get { return this.isDiscriminator ? XmlMappingConstant.True : null; } set { this.isDiscriminator = (value != null) ? bool.Parse(value) : false; } } internal UpdateCheck UpdateCheck { get { return this.updateCheck; } set { this.updateCheck = value; } } internal string XmlUpdateCheck { get { return this.updateCheck != UpdateCheck.Always ? this.updateCheck.ToString() : null; } set { this.updateCheck = (value == null) ? UpdateCheck.Always : (UpdateCheck)Enum.Parse(typeof(UpdateCheck), value); } } internal AutoSync AutoSync { get { return this.autoSync; } set { this.autoSync = value; } } internal string XmlAutoSync { get { return this.autoSync != AutoSync.Default ? this.autoSync.ToString() : null; } set { this.autoSync = (value != null) ? (AutoSync)Enum.Parse(typeof(AutoSync), value) : AutoSync.Default; } } } internal sealed class AssociationMapping : MemberMapping { string thisKey; string otherKey; string deleteRule; bool deleteOnNull; bool isForeignKey; bool isUnique; internal AssociationMapping() { } internal string ThisKey { get { return this.thisKey; } set { this.thisKey = value; } } internal string OtherKey { get { return this.otherKey; } set { this.otherKey = value; } } internal string DeleteRule { get { return this.deleteRule; } set { this.deleteRule = value; } } internal bool DeleteOnNull { get { return this.deleteOnNull; } set { this.deleteOnNull = value; } } internal bool IsForeignKey { get { return this.isForeignKey; } set { this.isForeignKey = value; } } internal string XmlIsForeignKey { get { return this.isForeignKey ? XmlMappingConstant.True : null; } set { this.isForeignKey = (value != null) ? bool.Parse(value) : false; } } internal string XmlDeleteOnNull { get { return this.deleteOnNull ? XmlMappingConstant.True : null; } set { this.deleteOnNull = (value != null) ? bool.Parse(value) : false; } } internal bool IsUnique { get { return this.isUnique; } set { this.isUnique = value; } } internal string XmlIsUnique { get { return this.isUnique ? XmlMappingConstant.True : null; } set { this.isUnique = (value != null) ? bool.Parse(value) : false; } } } /// /// Shared rules governing the mapping system. /// internal static class MappingSystem { /// /// Return true if this is a clr type supported as an inheritance discriminator. /// /// /// internal static bool IsSupportedDiscriminatorType(Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { type = type.GetGenericArguments()[0]; } switch (Type.GetTypeCode(type)) { case TypeCode.Byte: case TypeCode.SByte: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: case TypeCode.Char: case TypeCode.String: case TypeCode.Boolean: return true; } return false; } /// /// Return true if this is a clr type supported as an inheritance discriminator. /// /// /// internal static bool IsSupportedDiscriminatorType(SqlDbType type) { switch (type) { case SqlDbType.BigInt: case SqlDbType.Bit: case SqlDbType.Char: case SqlDbType.Int: case SqlDbType.NChar: case SqlDbType.NVarChar: case SqlDbType.SmallInt: case SqlDbType.TinyInt: case SqlDbType.VarChar: return true; } return false; } /// /// Return true if this is a CLR type supported as an identity member. Since identity /// management (caching) relies on key members being hashable, only types implementing /// GetHashCode are supported. Also, the runtime relies on identity members being comparable, /// so only types implementing Equals are supported. /// internal static bool IsSupportedIdentityType(Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { type = type.GetGenericArguments()[0]; } if (type == typeof(Guid) || type == typeof(DateTime) || type == typeof(DateTimeOffset) || type == typeof(TimeSpan) || type == typeof(Binary)) { return true; } switch (Type.GetTypeCode(type)) { case TypeCode.Byte: case TypeCode.SByte: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: case TypeCode.Char: case TypeCode.String: case TypeCode.Boolean: case TypeCode.Decimal: case TypeCode.Single: case TypeCode.Double: return true; } return false; } /// /// Types that do not support comparison cannot be used as primary keys. The /// database will restrict this, but we can't rely on that, since it is possible /// to create a key mapping to a column that isn't truly a key in the DB. /// internal static bool IsSupportedIdentityType(SqlDbType type) { switch (type) { case SqlDbType.NText: case SqlDbType.Image: case SqlDbType.Xml: case SqlDbType.Text: case SqlDbType.Variant: case SqlDbType.Udt: return false; default: return true; } } } }