private void SetKeys(string keys, SqlNamingMapper mapper, Type dataMappingType) { if (keys != null) { // from constructor keys = DataContract.Map(AutoMap.Keys, keys); } else if (DataContract.KeyColumns != null) { // from attributes keys = DataContract.KeyColumns; } else { // from mapper PrimaryKeyField = mapper.GetPrimaryKeyFieldNames(dataMappingType); keys = DataContract.Map(AutoMap.On, PrimaryKeyField); } if (keys == null) { PrimaryKeyColumnList = new List <string>(); } else { PrimaryKeyColumn = keys; if (PrimaryKeyField == null) { PrimaryKeyField = DataContract.ReverseMap(PrimaryKeyColumn); } PrimaryKeyColumnList = keys.Split(',').Select(k => k.Trim()).ToList(); } }
/// <summary> /// Constructor /// </summary> /// <param name="isGeneric"></param> /// <param name="type"></param> /// <param name="columns"></param> /// <param name="mapper"></param> internal DataContractKey(bool isGeneric, Type type, string columns, SqlNamingMapper mapper) { IsGeneric = isGeneric; foreach (var attr in type #if !NETFRAMEWORK .GetTypeInfo() #endif .GetCustomAttributes(false)) { if (attr is DatabaseTableAttribute) { DatabaseTableSettings = (DatabaseTableAttribute)attr; } } // TO DO: Should we really be calling the mapper in the data contract *key* constructor? // (Whatever calls are made here are called *every time* we check whether we already have a contract.) if (DatabaseTableSettings == null) { // we don't ever need to look up the mapper values if they have been overridden by the user-attribute; // these will be from the user-mapper if defined, or the default mapper if not DatabaseTableSettings = new DatabaseTableAttribute( mapper.TableNameMapping(type), mapper.CaseSensitiveColumns(type), mapper.AutoMap(type)); } HasMapperColumnsMapping = mapper.ColumnNameMapping != SqlNamingMapper.IdentityColumnMapping || mapper.ColumnDataDirection != SqlNamingMapper.ColumnDataDirectionUnspecified || mapper.IgnoreColumn != SqlNamingMapper.NeverIgnoreColumn; // If the user is trying to map column names in a dynamic instance of Mighty, then there must be a columns spec and columns auto-mapping must be left on if (!IsGeneric && HasMapperColumnsMapping) { if (columns == null || columns == "*") { throw new InvalidOperationException($"You must provide an explicit `columns` specification to any dynamic instance of {nameof(MightyOrm)} with column name mapping"); } if ((DatabaseTableSettings.AutoMap & AutoMap.Columns) == 0) { throw new InvalidOperationException($"You must enable {nameof(AutoMap)}.{nameof(AutoMap.Columns)} in your {nameof(DatabaseTableSettings.AutoMap)} settings for any dynamic instance of {nameof(MightyOrm)} with column name mapping"); } // Columns is not needed in the data contract except if we're here; // where needed, normalise it to improve caching DynamicColumnSpec = NormaliseColumns(columns); } ColumnName = mapper.ColumnNameMapping; ColumnDataDirection = mapper.ColumnDataDirection; IgnoreColumn = mapper.IgnoreColumn; DataItemType = type; DynamicNullContract = !IsGeneric && DynamicColumnSpec == null; }
/// <summary> /// Get (from store, or creating the first time it is needed) data contract for the type, columns spec and data mapper. /// </summary> /// <param name="IsGeneric"></param> /// <param name="type"></param> /// <param name="columns"></param> /// <param name="mapper"></param> /// <returns></returns> /// <remarks> /// In theory, mapping depends on Plugin, Factory, and ConnectionString as well; /// in practice, including those would make it much harder to provide the very useful /// <see cref="DataContract.Map(string)"/> feature. /// I think it seems (more or less?) reasonable to suppose that any one class will only /// be read from and written one database with one mapping at a time? In fact, since /// Mighty only supports one mapping per class, maybe this is effectively enforced anyway? /// </remarks> internal DataContract Get(bool IsGeneric, Type type, string columns, SqlNamingMapper mapper) { DataContractKey key = new DataContractKey(IsGeneric, type, columns, mapper); CacheHits++; return(store.GetOrAdd(key, k => { CacheHits--; CacheMisses++; return new DataContract(k); })); }
/// <summary> /// Manage key(s) and sequence or identity. /// </summary> /// <param name="IsGeneric"></param> /// <param name="dataContract"></param> /// <param name="xplugin"></param> /// <param name="dataMappingType"></param> /// <param name="xmapper"></param> /// <param name="keyNames"></param> /// <param name="sequence"></param> internal PrimaryKeyInfo( bool IsGeneric, DataContract dataContract, PluginBase xplugin, Type dataMappingType, SqlNamingMapper xmapper, string keyNames, string sequence) { Plugin = xplugin; SqlNamingMapper = xmapper; DataItemType = dataContract.Key.DataItemType; DataContract = dataContract; SetKeys(keyNames, xmapper, dataMappingType); SetSequence(xplugin, xmapper, sequence); SetPkMemberInfo(IsGeneric, dataContract); }
/// <summary> /// Get (from store, or creating the first time it is needed) data contract for the type, columns spec and data mapper. /// </summary> /// <param name="IsGeneric"></param> /// <param name="type"></param> /// <param name="columns"></param> /// <param name="mapper"></param> /// <returns></returns> /// <remarks> /// In theory, mapping depends on Plugin, Factory, and ConnectionString as well; /// in practice, including those would make it much harder to provide the very useful /// <see cref="DataContract.Map(string)"/> feature. /// I think it seems (more or less?) reasonable to suppose that any one class will only /// be read from and written one database with one mapping at a time? In fact, since /// Mighty only supports one mapping per class, maybe this is effectively enforced anyway? /// </remarks> internal DataContract Get(bool IsGeneric, Type type, string columns, SqlNamingMapper mapper) { DataContractKey key = new DataContractKey(IsGeneric, type, columns, mapper); DataContract value; if (store.TryGetValue(key, out value)) { CacheHits++; } else { CacheMisses++; value = new DataContract(key); store.Add(key, value); } return(value); }
private void SetSequence(PluginBase plugin, SqlNamingMapper mapper, string sequence) { // At the end of this next block of code, SequenceNameOrIdentityFunction should only be non-null if we // are actually expecting to use it later (which entails a simple (single column) PK). // It makes no sense to attempt to retrieve an auto-generated value for a compund primary key. if (PrimaryKeyColumnList.Count != 1) { // No exception here if database is identity-based since we want to allow, e.g., an override // of `sequence: "@@IDENTITY"` on all instances of Mighty, even if some instances are then // used for tables with no or compound primary keys. if (plugin.IsSequenceBased && !string.IsNullOrEmpty(sequence)) { throw new InvalidOperationException($"It is not possible to specify a sequence name for a table with {(PrimaryKeyColumnList.Count > 1 ? "a compound (multi-column)" : "no")} primary key"); } SequenceNameOrIdentityFunction = null; } else { if (sequence == "") { // empty string specifies that PK is manually controlled SequenceNameOrIdentityFunction = null; } else { if (plugin.IsSequenceBased) { // sequence-based, non-null, non-empty specifies sequence name SequenceNameOrIdentityFunction = sequence ?? mapper.QuoteDatabaseIdentifier(sequence); } else { // identity-based, non-null, non-empty specifies non-default identity retrieval function (e.g. use "@@IDENTITY" on SQL CE) SequenceNameOrIdentityFunction = sequence != null ? sequence : plugin.IdentityRetrievalFunction; } } } }