Example #1
0
        /// <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);
            }));
        }
Example #2
0
        /// <summary>
        /// Create a new data contract corresponding to the values in the key
        /// </summary>
        /// <param name="Key">All the items on which the contract depends</param>
        public DataContract(DataContractKey Key)
        {
            this.Key = Key;
            if (!Key.DynamicNullContract)
            {
                var  ReadColumnList = new List <string>();
                var  KeyColumnsList = new List <string>();
                bool foundControlledColumn;
                bool foundRenamedColumn;

                // These two don't need to be ConcurrentDictionary - they can't be written to concurrently (they are set up in the constructor then not modified), even though they may be read concurrently
                ColumnNameToMemberInfo = new Dictionary <string, DataContractMemberInfo>(Key.DatabaseTableSettings.CaseSensitiveColumnMapping ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase);
                MemberNameToColumnName = new Dictionary <string, string>(Key.DatabaseTableSettings.CaseSensitiveColumnMapping ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase);

                if (Key.IsGeneric)
                {
                    AddReflectedColumns(out bool fc1, out bool fr1, ReadColumnList, KeyColumnsList, Key, BindingFlags.Instance | BindingFlags.Public);
                    AddReflectedColumns(out bool fc2, out bool fr2, ReadColumnList, KeyColumnsList, Key, BindingFlags.Instance | BindingFlags.NonPublic);
                    foundControlledColumn = fc1 || fc2;
                    foundRenamedColumn    = fr1 || fr2;
                }
                else
                {
                    foreach (var column in Key.DynamicColumnSpec.Split(','))
                    {
                        AddReflectedColumn(out bool fc, out bool fr, ReadColumnList, KeyColumnsList, Key, null, column, true);
                    }
                    foundControlledColumn = true;
                    foundRenamedColumn    = true;
                }

                if (foundControlledColumn)
                {
                    // We have a read column list if there are any controlled columns (including e.g. ignored columns) in the contract
                    ReadColumns = string.Join(", ", ReadColumnList);
                }

                if (foundRenamedColumn)
                {
                    // This switches on auto-mapping by defaut if there are any actually renamed columns
                    AutoMapSettings = Key.DatabaseTableSettings.AutoMap;
                }

                if (KeyColumnsList.Count > 0)
                {
                    KeyColumns = string.Join(", ", KeyColumnsList);
                }
            }
        }
Example #3
0
 /// <summary>
 /// Include reflected columns
 /// </summary>
 /// <param name="foundControlledColumn"></param>
 /// <param name="foundRenamedColumn"></param>
 /// <param name="ReadColumnList"></param>
 /// <param name="KeyColumnsList"></param>
 /// <param name="key"></param>
 /// <param name="bindingFlags"></param>
 /// <returns>Whether a controlled column (<see cref="DatabaseColumnAttribute"/> or <see cref="DatabaseIgnoreAttribute"/>) was found</returns>
 protected void AddReflectedColumns(
     out bool foundControlledColumn, out bool foundRenamedColumn,
     List <string> ReadColumnList, List <string> KeyColumnsList, DataContractKey key, BindingFlags bindingFlags)
 {
     foundControlledColumn = false;
     foundRenamedColumn    = false;
     foreach (var member in key.DataItemType.GetMembers(bindingFlags)
              .Where(m => m is FieldInfo || m is PropertyInfo))
     {
         AddReflectedColumn(
             out bool fc, out bool fr,
             ReadColumnList, KeyColumnsList, key, member, null, (bindingFlags & BindingFlags.Public) != 0);
         foundControlledColumn = fc || foundControlledColumn;
         foundRenamedColumn    = fr || foundRenamedColumn;
     }
 }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <summary>
        /// Add a reflected field to the column list
        /// </summary>
        /// <param name="foundControlledColumn"></param>
        /// <param name="foundRenamedColumn"></param>
        /// <param name="ReadColumnList"></param>
        /// <param name="KeyColumnsList"></param>
        /// <param name="key"></param>
        /// <param name="member"></param>
        /// <param name="name"></param>
        /// <param name="include">The initial default include status (depending on public, non-public or columns-driven)</param>
        /// <returns>Whether a controlled column (<see cref="DatabaseColumnAttribute"/> or <see cref="DatabaseIgnoreAttribute"/>) was found</returns>
        protected void AddReflectedColumn(
            out bool foundControlledColumn, out bool foundRenamedColumn,
            List <string> ReadColumnList, List <string> KeyColumnsList, DataContractKey key, MemberInfo member, string name, bool include)
        {
            foundControlledColumn = false;
            foundRenamedColumn    = false;
            bool          isKey         = false;
            string        sqlColumnName = null;
            DataDirection dataDirection = 0;
            string        transformSql  = null;

            // Control things by attributes
            if (member != null)
            {
                foreach (var attr in member.GetCustomAttributes(false))
                {
                    if (attr is DatabaseColumnAttribute)
                    {
                        var colAttr = (DatabaseColumnAttribute)attr;
                        include               = true;
                        sqlColumnName         = colAttr.Name;
                        dataDirection         = colAttr.Direction;
                        transformSql          = colAttr.Transform;
                        foundControlledColumn = true;
                    }
                    if (attr is DatabaseIgnoreAttribute)
                    {
                        include = false;
                        foundControlledColumn = true;
                    }
                    if (attr is DatabasePrimaryKeyAttribute)
                    {
                        isKey = true;
                    }
                }
            }

            name = name ?? member.Name;

            // Also control things by the mapper
            DataDirection mapperDirection = key.ColumnDataDirection(key.DataItemType, name);

            if (mapperDirection != 0)
            {
                // We could do one of several things, but to make it like the ignore setting, we're
                // OR-ing the direction settings
                dataDirection |= mapperDirection;

                //// Not sure about this, but since having a column name in the mapper doesn't
                //// switch this on, then probably this shouldn't either?
                //foundcontrolledcolumn = true;
            }
            // The column will be ignored if the mapper or the attribute say so
            if (key.IgnoreColumn(key.DataItemType, name))
            {
                include = false;
                foundControlledColumn = true;
            }

            if (include)
            {
                // the column name from the attribute has precedence
                sqlColumnName = sqlColumnName ?? key.ColumnName(key.DataItemType, name);
                if (sqlColumnName != name)
                {
                    foundRenamedColumn = true;
                }
                ColumnNameToMemberInfo.Add(sqlColumnName, new DataContractMemberInfo(key.DataItemType, member, name, dataDirection));
                MemberNameToColumnName.Add(name, sqlColumnName);
                ReadColumnList.Add($"{(transformSql != null ? $"{transformSql} AS " : "")}{sqlColumnName}");
                if (isKey)
                {
                    KeyColumnsList.Add(sqlColumnName);
                }
            }
        }