public SaveConstraintAttribute(SaveConstraint behaviour)
 {
     Behaviour = behaviour;
 }
        /// <summary>
        /// Adds entity to corresponding db set if an entity with same primary key does not exist in database,
        /// or updates existing instance with the same primary key.
        /// </summary>
        /// <typeparam name="TEntity">Entity class</typeparam>
        /// <param name="ctx">Context instance</param>
        /// <param name="entity">Entity instance to add or update.</param>
        public static void AddOrUpdateEntity <TEntity>(this DbContext ctx, TEntity entity) where TEntity : class
        {
            Type       clrType    = typeof(TEntity);
            EntityType entityType = ctx.GetEntityType(clrType);

            if (entityType == null)
            {
                throw new ArgumentException($"Class `{clrType.Name}` is not an entity type for database context {ctx.GetType().Name}.");
            }

            // name of key property
            string keyPropertyName = entityType.KeyProperties.First().Name;

            // key property
            PropertyInfo keyProperty = clrType.GetProperty(keyPropertyName);

            // key value
            object key = keyProperty.GetValue(entity);

            // Create "e" portion of lambda expression
            ParameterExpression parameter = Expression.Parameter(clrType, "e");

            // create "e.Id" portion of lambda expression
            MemberExpression expProperty = Expression.Property(parameter, keyProperty.Name);

            // create "'id'" portion of lambda expression
            var expKeyConstant = Expression.Constant(key);

            // create "e.Id == 'id'" portion of lambda expression
            var expEqual = Expression.Equal(expProperty, expKeyConstant);

            // finally create entire expression: "e => e.Id == 'id'"
            Expression <Func <TEntity, bool> > predicate = Expression.Lambda <Func <TEntity, bool> >(expEqual, new[] { parameter });

            // search existing entity
            var existing = ctx.QueryEntitiesWithRelated <TEntity>().FirstOrDefault(predicate);

            if (existing != null)
            {
                var navProperties = entityType.NavigationProperties.Select(np => ctx.Entry(entity).Member(np.Name)).ToArray();

                var collectionNavProperties = navProperties.OfType <DbCollectionEntry>().ToArray();
                if (collectionNavProperties.Any())
                {
                    UpdateRelatedCollections(ctx, existing, entity, collectionNavProperties);
                }

                var singleNavProperties = navProperties.OfType <DbReferenceEntry>().Where(np => np.CurrentValue != null).ToArray();
                foreach (var navProperty in singleNavProperties)
                {
                    ctx.Entry(navProperty.CurrentValue).State = EntityState.Added;
                }

                ctx.Entry(existing).CurrentValues.SetValues(entity);
            }
            else
            {
                ctx.Set <TEntity>().Add(entity);
            }

            var saveBehaviourProperties = clrType.GetProperties().Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(SaveConstraintAttribute))).ToList();

            foreach (var property in saveBehaviourProperties)
            {
                SaveConstraint constraint = property.GetCustomAttribute <SaveConstraintAttribute>().Behaviour;

                var edmMember = entityType.DeclaredMembers.First(p => p.Name == property.Name);

                // do not modify property if new value is default value
                if (constraint == SaveConstraint.NotEmpty && GetDefaultValueOfType(property.PropertyType) == property.GetValue(entity))
                {
                    if (edmMember.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
                    {
                        // do nothing, navigation property with null value not saved by default
                    }
                    else if (edmMember.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
                    {
                        ctx.Entry(existing).Property(property.Name).IsModified = false;
                    }
                }

                // do not modify property if it's a referenced entity and it's already exist in db
                if (constraint == SaveConstraint.NotExists && existing != null)
                {
                    if (edmMember.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
                    {
                        object newPropertyValue      = property.GetValue(entity);
                        object existingPropertyValue = ctx.SearchEntityByKey(newPropertyValue);
                        if (newPropertyValue != null && existingPropertyValue != null)
                        {
                            ctx.Entry(newPropertyValue).State = EntityState.Detached;
                        }
                    }
                    else if (edmMember.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
                    {
                        throw new Exception($"{property.Name} property of type {clrType.Name} is not navigation property. {nameof(SaveConstraint.NotExists)} constraint can be applied for navigation properties only.");
                    }
                }

                // do not save properties marked with "Never"
                if (constraint == SaveConstraint.Never)
                {
                    if (edmMember.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
                    {
                        object newPropertyValue = property.GetValue(entity);
                        if (newPropertyValue != null)
                        {
                            ctx.Entry(newPropertyValue).State = EntityState.Detached;
                        }
                    }
                    else if (edmMember.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
                    {
                        ctx.Entry(existing).Property(property.Name).IsModified = false;
                    }
                }
            }
        }