Beispiel #1
0
        /// <summary>
        /// Extract property metadata from the match expression
        /// </summary>
        /// <typeparam name="TEntity">Type of the entity being upserted</typeparam>
        /// <param name="entityType">Metadata type of the entity being upserted</param>
        /// <param name="matchExpression">The match expression provided by the user</param>
        /// <param name="queryOptions">Options for the current query that will affect it's behaviour</param>
        /// <returns>A list of model properties used to match entities</returns>
        protected static ICollection <IProperty> ProcessMatchExpression <TEntity>(IEntityType entityType, Expression <Func <TEntity, object> >?matchExpression, RunnerQueryOptions queryOptions)
        {
            if (entityType == null)
            {
                throw new ArgumentNullException(nameof(entityType));
            }

            List <IProperty> joinColumns;

            if (matchExpression is null)
            {
                joinColumns = entityType.GetProperties()
                              .Where(p => p.IsKey())
                              .ToList();
            }
            else if (matchExpression.Body is NewExpression newExpression)
            {
                joinColumns = new List <IProperty>();
                foreach (MemberExpression arg in newExpression.Arguments)
                {
                    if (arg == null || arg.Member is not PropertyInfo || !typeof(TEntity).Equals(arg.Expression?.Type))
                    {
                        throw new InvalidOperationException(Resources.MatchColumnsHaveToBePropertiesOfTheTEntityClass);
                    }
                    var property = entityType.FindProperty(arg.Member.Name);
                    if (property == null)
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.UnknownProperty, arg.Member.Name));
                    }
                    joinColumns.Add(property);
                }
            }
            else if (matchExpression.Body is UnaryExpression unaryExpression)
            {
                if (unaryExpression.Operand is not MemberExpression memberExp || memberExp.Member is not PropertyInfo || !typeof(TEntity).Equals(memberExp.Expression?.Type))
                {
                    throw new InvalidOperationException(Resources.MatchColumnsHaveToBePropertiesOfTheTEntityClass);
                }
                var property = entityType.FindProperty(memberExp.Member.Name);
                if (property == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.UnknownProperty, memberExp.Member.Name));
                }
                joinColumns = new List <IProperty> {
                    property
                };
            }
            else if (matchExpression.Body is MemberExpression memberExpression)
            {
                if (!typeof(TEntity).Equals(memberExpression.Expression?.Type) || memberExpression.Member is not PropertyInfo)
                {
                    throw new InvalidOperationException(Resources.MatchColumnsHaveToBePropertiesOfTheTEntityClass);
                }
                var property = entityType.FindProperty(memberExpression.Member.Name);
                if (property == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.UnknownProperty, memberExpression.Member.Name));
                }
                joinColumns = new List <IProperty> {
                    property
                };
            }
            else
            {
                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Resources.ArgumentMustBeAnAnonymousObjectInitialiser, "match"), nameof(matchExpression));
            }

            if (!queryOptions.AllowIdentityMatch && joinColumns.Any(p => p.ValueGenerated != ValueGenerated.Never))
            {
                throw new InvalidMatchColumnsException();
            }

            return(joinColumns);
        }
Beispiel #2
0
 /// <inheritdoc/>
 public abstract Task <int> RunAsync <TEntity>(DbContext dbContext, IEntityType entityType, ICollection <TEntity> entities, Expression <Func <TEntity, object> >?matchExpression,
                                               Expression <Func <TEntity, TEntity, TEntity> >?updateExpression, Expression <Func <TEntity, TEntity, bool> >?updateCondition, RunnerQueryOptions queryOptions,
                                               CancellationToken cancellationToken) where TEntity : class;
Beispiel #3
0
        private void RunCore <TEntity>(DbContext dbContext, IEntityType entityType, ICollection <TEntity> entities, Expression <Func <TEntity, object> >?matchExpression,
                                       Expression <Func <TEntity, TEntity, TEntity> >?updateExpression, Expression <Func <TEntity, TEntity, bool> >?updateCondition, RunnerQueryOptions queryOptions) where TEntity : class
        {
            // Find matching entities in the dbContext
            var matches = FindMatches(entityType, entities, dbContext, matchExpression);

            Action <TEntity, TEntity>?    updateAction = null;
            Func <TEntity, TEntity, bool>?updateTest   = updateCondition?.Compile();

            if (updateExpression != null)
            {
                // If update expression is specified, create an update delegate based on that
                if (!(updateExpression.Body is MemberInitExpression entityUpdater))
                {
                    throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Resources.ArgumentMustBeAnInitialiserOfTheTEntityType, "updater"), nameof(updateExpression));
                }

                var properties = entityUpdater.Bindings.Select(b => b.Member).OfType <PropertyInfo>();
                var updateFunc = updateExpression.Compile();
                updateAction = (dbEntity, newEntity) =>
                {
                    var tmp = updateFunc(dbEntity, newEntity);
                    foreach (var prop in properties)
                    {
                        var property = entityType.FindProperty(prop.Name);
                        prop.SetValue(dbEntity, prop.GetValue(tmp) ?? property.GetDefaultValue());
                    }
                };
            }
            else if (!queryOptions.NoUpdate)
            {
                // Otherwise create a default update delegate that updates all non match, non auto generated columns
                var joinColumns = ProcessMatchExpression(entityType, matchExpression, queryOptions);

                var properties = entityType.GetProperties()
                                 .Where(p => p.ValueGenerated == ValueGenerated.Never)
                                 .Select(p => typeof(TEntity).GetProperty(p.Name))
                                 .Where(p => p != null)
                                 .Except(joinColumns.Select(c => c.PropertyInfo));
                updateAction = (dbEntity, newEntity) =>
                {
                    foreach (var prop in properties)
                    {
                        var property = entityType.FindProperty(prop.Name);
                        prop.SetValue(dbEntity, prop.GetValue(newEntity) ?? property.GetDefaultValue());
                    }
                };
            }

            foreach (var(dbEntity, newEntity) in matches)
            {
                if (dbEntity == null)
                {
                    foreach (var prop in typeof(TEntity).GetProperties())
                    {
                        if (prop.GetValue(newEntity) == null)
                        {
                            var property = entityType.FindProperty(prop.Name);
                            if (property != null)
                            {
                                var defaultValue = property.GetDefaultValue();
                                if (defaultValue != null)
                                {
                                    prop.SetValue(newEntity, defaultValue);
                                }
                            }
                        }
                    }
                    dbContext.Add(newEntity);
                    continue;
                }

                if (updateTest?.Invoke(dbEntity, newEntity) == false)
                {
                    continue;
                }

                updateAction?.Invoke(dbEntity, newEntity);
            }
        }
Beispiel #4
0
 /// <inheritdoc/>
 public abstract int Run <TEntity>(DbContext dbContext, IEntityType entityType, ICollection <TEntity> entities, Expression <Func <TEntity, object> >?matchExpression,
                                   Expression <Func <TEntity, TEntity, TEntity> >?updateExpression, Expression <Func <TEntity, TEntity, bool> >?updateCondition, RunnerQueryOptions queryOptions)
     where TEntity : class;
Beispiel #5
0
        /// <inheritdoc/>
        public override Task <int> RunAsync <TEntity>(DbContext dbContext, IEntityType entityType, ICollection <TEntity> entities, Expression <Func <TEntity, object> >?matchExpression,
                                                      Expression <Func <TEntity, TEntity, TEntity> >?updateExpression, Expression <Func <TEntity, TEntity, bool> >?updateCondition, RunnerQueryOptions queryOptions,
                                                      CancellationToken cancellationToken)
        {
            if (dbContext is null)
            {
                throw new ArgumentNullException(nameof(dbContext));
            }
            if (entityType == null)
            {
                throw new ArgumentNullException(nameof(entityType));
            }

            RunCore(dbContext, entityType, entities, matchExpression, updateExpression, updateCondition, queryOptions);
            return(dbContext.SaveChangesAsync(cancellationToken));
        }