示例#1
0
        internal static int[] DeterminePrimaryKeyIndexes(IDataReader reader, ORMEntity entity)
        {
            var pks = entity.GetPrimaryKeyPropertyInfo();
            var primaryKeyIndexes = new int[pks.Length];
            int found             = 0;

            for (int i = 0; i < reader.FieldCount && found < pks.Length; i++)
            {
                var name = reader.GetName(i);

                var columnAttribute = pks[i].GetCustomAttributes(typeof(ORMColumnAttribute), false).FirstOrDefault() as ORMColumnAttribute;

                if ((columnAttribute != null && columnAttribute.ColumnName == name) ||
                    pks.Any(x => string.Compare(x.Name, name, true) == 0))
                {
                    primaryKeyIndexes[found] = i;
                    found++;
                }
            }

            if (found != pks.Length)
            {
                throw new ArgumentException($"Expected {pks.Length} primary key columns but recieved {found}");
            }

            return(primaryKeyIndexes);
        }
示例#2
0
        internal static void FinaliaseEntity(ORMEntity entity)
        {
            entity.IsNew = false;

            if (!entity.DisableChangeTracking)
            {
                entity.GetType()
                .GetProperty(nameof(ORMEntity.OriginalFetchedValue), entity.NonPublicFlags)
                .SetValue(entity, entity.ShallowCopy());

                foreach (var relation in entity.Relations.Where(x => x != null && !x.IsNew))
                {
                    entity.OriginalFetchedValue[relation.GetType().Name] = (entity[relation.GetType().Name] as ORMEntity).OriginalFetchedValue;
                }
            }

            if (UnitTestUtilities.IsUnitTesting)
            {
                // With unit tests, the ORMPrimaryKey isn't always set - e.g:
                // A (child) object is spawned from within the framework, therefore the private/internal
                // constructor is called. In this case, the ORMPrimaryKey value will never be set.
                // ~ Rick, 12/01/2021

                foreach (var primaryKey in entity.PrimaryKey.Keys)
                {
                    primaryKey.Value = entity[primaryKey.PropertyName];
                }
            }
        }
示例#3
0
        internal static void PopulateChildEntity(ORMEntity parentEntity, ORMEntity childEntity, IDataReader reader, SQLBuilder sqlBuilder)
        {
            var(ReaderStartIndex, ReaderEndIndex) = CalculateJoinIndexes(parentEntity, childEntity, sqlBuilder);

            for (int i = 0; i < (ReaderEndIndex - ReaderStartIndex); i++)
            {
                var value = reader.GetValue(ReaderStartIndex + i);

                var propertyName = reader.GetName(ReaderStartIndex + i);

                if (UnitTestUtilities.IsUnitTesting)
                {
                    UnitTestUtilities.PopulateChildEntity(ref propertyName, ref value, childEntity);
                }

                childEntity[propertyName] = value;
            }

            childEntity.ExecutedQuery = "Initialised through parent";

            FinaliaseEntity(childEntity);
        }
示例#4
0
        internal IORMCollection <EntityType> Fetch(ORMEntity entity, long maxNumberOfItemsToReturn, Expression internalEntityJoinExpression = null)
        {
            var sqlBuilder = new SQLBuilder();

            sqlBuilder.BuildQuery(TableAttribute, SelectExpression, JoinExpression ?? InternalJoinExpression ?? internalEntityJoinExpression, WhereExpression ?? InternalWhereExpression, SortExpression, maxNumberOfItemsToReturn);

            if (ExecutedQuery.Equals(sqlBuilder.GeneratedQuery, StringComparison.InvariantCultureIgnoreCase))
            {
                return(this);
            }

            if (entity == null)
            {
                SQLExecuter.ExecuteCollectionQuery(this, sqlBuilder);
            }
            else
            {
                SQLExecuter.ExecuteEntityQuery(entity, sqlBuilder);
            }

            ExecutedQuery = sqlBuilder.GeneratedQuery;

            return(this);
        }
示例#5
0
        internal static void PopulateChildEntity(ref string propertyName, ref object value, ORMEntity childEntity)
        {
            propertyName = propertyName.Split('_').Last();

            var childPropertyType = childEntity.GetPropertyInfo(propertyName).PropertyType;

            // Unit tests columns are all of type string, therefore they require to be converted to their respective type.
            if (Nullable.GetUnderlyingType(childPropertyType) != null && value != DBNull.Value)
            {
                value = Convert.ChangeType(value, Nullable.GetUnderlyingType(childPropertyType));
            }
            else if (!childPropertyType.IsSubclassOf(typeof(ORMEntity)) && value != DBNull.Value)
            {
                value = Convert.ChangeType(value, childPropertyType);
            }
        }
示例#6
0
        private static IDataReader ApplyEntityJoinsToReader(ORMEntity entity, IDataReader reader, SQLBuilder sqlBuilder)
        {
            foreach (var join in sqlBuilder.Joins)
            {
                if (join.IsManyToMany)
                {
                    if (join.LeftPropertyInfo.ReflectedType == entity.GetType())
                    {
                        foreach (var property in entity.GetType().GetProperties())
                        {
                            if (property.PropertyType == join.RightTableAttribute.CollectionTypeRight &&
                                property.CustomAttributes.Any(x => x.AttributeType == typeof(ORMManyToMany)))
                            {
                                var parentDataTable = new DataTable();
                                parentDataTable.Load(reader);

                                var childTableName  = join.RightTableAttribute.EntityType.Name;
                                var childEntity     = Activator.CreateInstance(join.RightTableAttribute.EntityType, true) as ORMEntity;
                                var childCollection = Activator.CreateInstance(join.RightTableAttribute.CollectionType) as IEnumerable <ORMEntity>;

                                foreach (var childProperty in childEntity.GetType().GetProperties(childEntity.PublicFlags))
                                {
                                    var executedQuery = (string)childCollection.GetType().GetProperty(nameof(ORMCollection <ORMEntity> .ExecutedQuery)).GetValue(childCollection);

                                    if (!string.IsNullOrEmpty(executedQuery))
                                    {
                                        break;
                                    }

#pragma warning disable IDE0019 // Use pattern matching
                                    var fkAttribute = childProperty.GetCustomAttributes(typeof(ORMForeignKeyAttribute), false).FirstOrDefault() as ORMForeignKeyAttribute;
#pragma warning restore IDE0019 // Use pattern matching

                                    if (fkAttribute != null && fkAttribute.Relation == entity.GetType())
                                    {
                                        BinaryExpression whereExpression = null;

                                        for (int i = 0; i < childEntity.PrimaryKey.Count; i++)
                                        {
                                            if (!childEntity.IsForeignKeyOfType(childEntity.PrimaryKey.Keys[i].PropertyName, entity.GetType()))
                                            {
                                                continue;
                                            }

                                            // Contains the id represented as a MemberExpression: {x.InternalPropertyName}.
                                            var memberExpression = Expression.Property(Expression.Parameter(childEntity.GetType(), $"x"), childEntity.GetPrimaryKeyPropertyInfo()[i]);

                                            // Contains the actual id represented as a ConstantExpression: {id_value}.
                                            var constantExpression = Expression.Constant(entity.PrimaryKey.Keys[i].Value, entity.PrimaryKey.Keys[i].Value.GetType());

                                            // Combines the expressions represtend as a Expression: {(x.InternalPropertyName == id_value)}
                                            if (whereExpression == null)
                                            {
                                                whereExpression = Expression.Equal(memberExpression, constantExpression);
                                            }
                                        }

                                        // Sets the InternalWhere with the WhereExpression.
                                        childCollection.GetType().GetMethod(nameof(ORMCollection <ORMEntity> .InternalWhere), entity.NonPublicFlags, null, new Type[] { typeof(BinaryExpression) }, null).Invoke(childCollection, new object[] { whereExpression });

                                        // Fetches the data.
                                        childCollection.GetType().GetMethod(nameof(ORMCollection <ORMEntity> .Fetch), childEntity.NonPublicFlags, null, new Type[] { typeof(ORMEntity), typeof(long), typeof(Expression) }, null).Invoke(childCollection, new object[] { null, -1, null });
                                    }
                                }

                                var childCollectionRight = Activator.CreateInstance(join.RightTableAttribute.CollectionTypeRight) as IEnumerable <ORMEntity>;
                                var childEntityRight     = Activator.CreateInstance(ORMUtilities.CollectionEntityRelations[childCollectionRight.GetType()]) as ORMEntity;

                                BinaryExpression whereExpressionRight = null;

                                foreach (var childEntityLeft in childCollection)
                                {
                                    for (int i = 0; i < childEntityLeft.PrimaryKey.Count; i++)
                                    {
                                        if (!childEntity.IsForeignKeyOfType(childEntityLeft.PrimaryKey.Keys[i].PropertyName, childEntityRight.GetType()))
                                        {
                                            continue;
                                        }

                                        // Contains the id represented as a MemberExpression: {x.InternalPropertyName}.
                                        var memberExpressionRight = Expression.Property(Expression.Parameter(childEntityRight.GetType(), $"x"), childEntityRight.GetPrimaryKeyPropertyInfo()[0]);

                                        // Contains the actual id represented as a ConstantExpression: {id_value}.
                                        var constantExpressionRight = Expression.Constant(childEntityLeft.PrimaryKey.Keys[i].Value, childEntityLeft.PrimaryKey.Keys[i].Value.GetType());

                                        // Combines the expressions represtend as a Expression: {(x.InternalPropertyName == id_value)}
                                        if (whereExpressionRight == null)
                                        {
                                            whereExpressionRight = Expression.Equal(memberExpressionRight, constantExpressionRight);
                                        }
                                        else
                                        {
                                            whereExpressionRight = Expression.Or(whereExpressionRight, Expression.Equal(memberExpressionRight, constantExpressionRight));
                                        }
                                    }
                                }

                                // We no longer need the old collection;
                                childCollection = Activator.CreateInstance(join.RightTableAttribute.CollectionTypeRight) as IEnumerable <ORMEntity>;

                                // Sets the InternalWhere with the WhereExpression.
                                childCollection.GetType().GetMethod(nameof(ORMCollection <ORMEntity> .InternalWhere), entity.NonPublicFlags, null, new Type[] { typeof(BinaryExpression) }, null).Invoke(childCollection, new object[] { whereExpressionRight });

                                // Fetches the data.
                                childCollection.GetType().GetMethod(nameof(ORMCollection <ORMEntity> .Fetch), entity.NonPublicFlags, null, new Type[] { typeof(ORMEntity), typeof(long), typeof(Expression) }, null).Invoke(childCollection, new object[] { null, -1, null });

                                // Sets the ManyToMany collection.
                                property.SetValue(entity, childCollection);

                                reader = parentDataTable.CreateDataReader();
                                break;
                            }
                        }
                    }
                }
                else
                {
                    foreach (var field in entity.TableScheme)
                    {
                        if (join.LeftPropertyInfo.PropertyType == entity.GetPropertyInfo(field).PropertyType)
                        {
                            var parentDataTable = new DataTable();
                            parentDataTable.Load(reader);

                            var childTableName = ORMUtilities.CollectionEntityRelations[join.LeftPropertyInfo.PropertyType].Name;
                            var childEntity    = Activator.CreateInstance(join.LeftPropertyInfo.PropertyType);
                            var childId        = parentDataTable.Rows[0][entity.TableScheme.IndexOf(field)];

                            var childReader = ORMUtilities.MemoryEntityDatabase.FetchEntityById(childTableName, entity.PrimaryKey.Keys[0], childId);

                            var childDataTable = new DataTable();
                            childDataTable.Load(childReader);

                            foreach (DataColumn column in childDataTable.Columns)
                            {
                                if (parentDataTable.Columns.Contains(column.ColumnName))
                                {
                                    childDataTable.Columns[column.ColumnName].ColumnName = $"{ childDataTable.TableName }_{ column.ColumnName }";
                                }
                            }

                            parentDataTable.Merge(childDataTable);

                            var left  = parentDataTable.Rows[0];
                            var right = parentDataTable.Rows[1];

                            foreach (DataColumn column in left.Table.Columns)
                            {
                                if (left[column] == DBNull.Value)
                                {
                                    left[column] = right[column];
                                }
                            }

                            parentDataTable.Rows.Remove(right);

                            reader = parentDataTable.CreateDataReader();
                            break;
                        }
                    }
                }
            }

            return(reader);
        }
示例#7
0
 public bool AnyDirtyRelations(ORMEntity entity)
 {
     return(DirtyList.Any(x => entity.Relations.Any(e => e.GetType().Name != x.Key)));
 }
示例#8
0
        private static (int ReaderStartIndex, int ReaderEndIndex) CalculateJoinIndexes(ORMEntity parentEntity, ORMEntity childEntity, SQLBuilder sqlBuilder)
        {
            var startIndex = 0;

            foreach (var(name, type) in sqlBuilder.TableOrder)
            {
                // The TableOrder (as the name suggests) provides the order from the SqlReader
                // with all the involved tables. Therefore the parent will always be set first
                // and we can break once the current join has been found.
                if (type == parentEntity.GetType())
                {
                    startIndex = sqlBuilder.TableNameColumnCount[name];
                }
                // If there are multiple joins, we want to continue adding up the total tally
                // untill our current join type is found so we know the current index.
                else if (type != parentEntity.GetType() &&
                         type != childEntity.GetType())
                {
                    startIndex += sqlBuilder.TableNameColumnCount[name];
                }
                else if (type == childEntity.GetType())
                {
                    // We found the indexes based on the parent entity fields and the previous
                    // joins within the sqlBuilder.
                    var endIndex = startIndex + sqlBuilder.TableNameColumnCount[name];

                    return(startIndex, endIndex);
                }
            }

            // This shouldn't happen, but leaving an exception for now to make sure all cases work as expected.
            // -Rick, 11 December 2020
            throw new NotImplementedException();
        }
示例#9
0
        internal static void SetEntityProperty(ORMEntity entity, IDataReader reader, SQLBuilder sqlBuilder, int iteration, bool isEntityManyTomany = false)
        {
            // All joins (child-entities) are filled through PopulateChildEntity, therefore we can
            // skip anything past the current entity (parent) within the reader.
            if (iteration >= sqlBuilder.TableNameColumnCount.First().Value&& !isEntityManyTomany)
            {
                // Skipping.
                return;
            }

            var propertyName = reader.GetName(iteration);

            if (UnitTestUtilities.IsUnitTesting)
            {
                propertyName = propertyName.Split('_').Last();
            }

            var entityPropertyInfo = entity.GetPropertyInfo(propertyName);

            if (null == entityPropertyInfo)
            {
                if (propertyName == entity.GetType().Name)
                {
                    throw new ORMIllegalColumnNameException($"The column [{propertyName}] has not been implemented in entity [{entity.GetType().Name}], but can't have the same name as its enclosing type.");
                }

                throw new NotImplementedException($"The column [{propertyName}] has not been implemented in entity [{entity.GetType().Name}].");
            }
            else if (!entityPropertyInfo.CanWrite)
            {
                throw new ReadOnlyException($"Property [{propertyName}] is read-only in [{entity.GetType().Name}].");
            }

            object value = null;

            switch (entityPropertyInfo.PropertyType)
            {
            case Type type when type == typeof(DateTime?):
                value = reader.GetValue(iteration);
                break;

            case Type type when type == typeof(DateTime):
                if (reader.GetValue(iteration) == DBNull.Value)
                {
                    throw new ORMPropertyNotNullableException($"Property [{propertyName}] is not nullable, but the database column equivelant is.");
                }

                value = reader.GetValue(iteration);
                break;

            case Type type when type.IsSubclassOf(typeof(ORMEntity)):
                if (reader.GetValue(iteration) == DBNull.Value)
                {
                    break;
                }

                // If there are no joins provided or none matched the current type we don't want
                // to fetch the child-object.
                if (sqlBuilder.Joins.Count == 0 || !sqlBuilder.Joins.Any(x => x.LeftPropertyInfo.PropertyType == type))
                {
                    value = null;
                    break;
                }

                foreach (var join in sqlBuilder.Joins)
                {
                    if (join.LeftPropertyInfo.PropertyType == type)
                    {
                        var subEntity = Activator.CreateInstance(type.UnderlyingSystemType) as ORMEntity;

                        PopulateChildEntity(entity, subEntity, reader, sqlBuilder);

                        value = subEntity;

                        entity.Relations.Add(value as ORMEntity);

                        if (entityPropertyInfo == null)
                        {
                            entityPropertyInfo = entity.GetType().GetProperty("Organisation", entity.PublicIgnoreCaseFlags);
                        }

                        break;
                    }
                }

                break;

            default:
                value = reader.GetValue(iteration);
                break;
            }

            if (UnitTestUtilities.IsUnitTesting)
            {
                // Unit tests columns are all of type string, therefore they require to be converted to their respective type.
                if (Nullable.GetUnderlyingType(entityPropertyInfo.PropertyType) != null && value != DBNull.Value)
                {
                    value = Convert.ChangeType(value, Nullable.GetUnderlyingType(entityPropertyInfo.PropertyType));
                }
                else if (!entityPropertyInfo.PropertyType.IsSubclassOf(typeof(ORMEntity)) && value != DBNull.Value)
                {
                    value = Convert.ChangeType(value, entityPropertyInfo.PropertyType);
                }
            }

            if (reader.GetValue(iteration) == DBNull.Value)
            {
                entityPropertyInfo.SetValue(entity, null);
            }
            else
            {
                entityPropertyInfo.SetValue(entity, value);
            }
        }
示例#10
0
        private static void PopulateManyToManyEntity(ORMEntity entity, IDataReader reader, SQLBuilder sqlBuilder)
        {
            Dictionary <ORMPrimaryKey, Dictionary <string, List <ORMEntity> > > manyToManyData = new Dictionary <ORMPrimaryKey, Dictionary <string, List <ORMEntity> > >(new ORMPrimaryKey());

            var manyToManyJoinIndexes = new List <(string, int[])>();
            var manyToManyJoinTypes   = new Dictionary <string, Type>();

            var tableIndex = 0;

            foreach (var(name, _) in sqlBuilder.TableOrder)
            {
                var objectPath       = sqlBuilder.TableNameResolvePaths.ContainsKey(name) ? sqlBuilder.TableNameResolvePaths[name] : string.Empty;
                var tableColumnCount = sqlBuilder.TableNameColumnCount[name];

                if (objectPath.StartsWith(SQLBuilder.MANY_TO_MANY_JOIN_DATA, StringComparison.Ordinal))
                {
                    var indexes = new List <int>();
                    for (int i = 0; i < tableColumnCount; i++)
                    {
                        indexes.Add(tableIndex + i);
                    }
                    manyToManyJoinIndexes.Add((objectPath.Split('.')[1], indexes.ToArray()));
                }
                tableIndex += tableColumnCount;
            }

            void AddManyToManyObject(ORMPrimaryKey key, IDataReader reader)
            {
                Dictionary <string, List <ORMEntity> > relations;

                if (manyToManyData.ContainsKey(key))
                {
                    relations = manyToManyData[key];
                }
                else
                {
                    relations           = new Dictionary <string, List <ORMEntity> >();
                    manyToManyData[key] = relations;
                }

                foreach (var(fieldName, indexes) in manyToManyJoinIndexes)
                {
                    bool IsRowEmpty(List <(string, int[])> manyToManyJoinIndexes, IDataReader reader)
                    {
                        foreach (var(fieldName, indexes) in manyToManyJoinIndexes)
                        {
                            for (int i = 0; i < indexes.Length; i++)
                            {
                                if (reader.GetValue(indexes[i]) == DBNull.Value)
                                {
                                    if ((i + 1) == indexes.Length)
                                    {
                                        return(true);
                                    }
                                }
                                else
                                {
                                    return(false);
                                }
                            }
                        }

                        return(true);
                    }

                    if (!IsRowEmpty(manyToManyJoinIndexes, reader))
                    {
                        var instance = (ORMEntity)Activator.CreateInstance(manyToManyJoinTypes[fieldName]);
                        foreach (var index in indexes)
                        {
                            SetEntityProperty(instance, reader, sqlBuilder, index, true);
                        }

                        if (relations.ContainsKey(fieldName))
                        {
                            relations[fieldName].Add(instance);
                        }
                        else
                        {
                            relations[fieldName] = new List <ORMEntity>()
                            {
                                instance
                            };
                        }
                    }
                }
            }

            int[] primaryKeyIndexes = ORMPrimaryKey.DeterminePrimaryKeyIndexes(reader, entity);

            foreach (var(fieldName, _) in manyToManyJoinIndexes)
            {
                var type = entity.GetPropertyInfo(fieldName).PropertyType;
                if (!typeof(ORMEntity).IsAssignableFrom(type.GetType()))
                {
                    type = ORMUtilities.CollectionEntityRelations[type];
                }
                manyToManyJoinTypes.Add(fieldName, type);
            }

            ORMPrimaryKey pk = new ORMPrimaryKey(reader, primaryKeyIndexes);

            //PopulateEntity(entity, reader, sqlBuilder);
            AddManyToManyObject(pk, reader);

            foreach (var kvPair in manyToManyData)
            {
                foreach (var data in kvPair.Value)
                {
                    var property = entity.GetType().GetProperty(data.Key, entity.PublicFlags);
                    if (typeof(IORMCollection <ORMEntity>).IsAssignableFrom(property.PropertyType))
                    {
                        var propertyValue = entity.GetType().GetProperty(data.Key, entity.PublicFlags).GetValue(entity);

                        if (propertyValue == null)
                        {
                            var subcollection = Activator.CreateInstance(property.PropertyType);

                            var collectionProperty = property.PropertyType.GetProperty(nameof(ORMCollection <ORMEntity> .MutableEntityCollection), entity.NonPublicFlags);
                            var list = collectionProperty.GetValue(subcollection) as IList;
                            foreach (var item in data.Value)
                            {
                                list.Add(item);
                            }
                            property.SetValue(entity, subcollection);
                        }
                        else
                        {
                            foreach (var item in data.Value)
                            {
                                propertyValue.GetType().GetMethod("Add", entity.PublicFlags).Invoke(propertyValue, new object[] { item });
                            }
                        }
                    }
                    else
                    {
                        throw new Exception("Something went wrong trying to cast to a subcollection");
                    }
                }
            }
        }