Example #1
0
    /// <summary>
    /// <para>Converts NotificationObject's property to ReactiveProperty. Value is two-way synchronized.</para>
    /// <para>PropertyChanged raise on selected scheduler.</para>
    /// </summary>
    /// <typeparam name="TSubject">The type of the subject.</typeparam>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="subject">The subject.</param>
    /// <param name="propertySelector">Argument is self, Return is target property.</param>
    /// <param name="raiseEventScheduler">The raise event scheduler.</param>
    /// <param name="mode">ReactiveProperty mode.</param>
    /// <param name="ignoreValidationErrorValue">Ignore validation error value.</param>
    /// <returns></returns>
    public static ReactiveProperty <TProperty> ToReactivePropertyAsSynchronized <TSubject, TProperty>(
        this TSubject subject,
        Expression <Func <TSubject, TProperty> > propertySelector,
        IScheduler raiseEventScheduler,
        ReactivePropertyMode mode       = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe,
        bool ignoreValidationErrorValue = false)
        where TSubject : INotifyPropertyChanged
    {
        if (ExpressionTreeUtils.IsNestedPropertyPath(propertySelector))
        {
            var observer = PropertyObservable.CreateFromPropertySelector(subject, propertySelector);
            var result   = Observable.Using(() => observer, x => x)
                           .ToReactiveProperty(raiseEventScheduler, initialValue: observer.GetPropertyPathValue(), mode: mode);
            result
            .Where(_ => !ignoreValidationErrorValue || !result.HasErrors)
            .Subscribe(x => observer.SetPropertyPathValue(x));
            return(result);
        }
        else
        {
            var getter = AccessorCache <TSubject> .LookupGet(propertySelector, out var _);

            var result = subject.ObserveSimpleProperty(propertySelector, isPushCurrentValueAtFirst: false)
                         .ToReactiveProperty(raiseEventScheduler, initialValue: getter(subject), mode: mode);
            var setter = AccessorCache <TSubject> .LookupSet(propertySelector, out _);

            result
            .Where(_ => !ignoreValidationErrorValue || !result.HasErrors)
            .Subscribe(x => setter(subject, x));
            return(result);
        }
    }
    /// <summary>
    /// Data binding method.
    /// </summary>
    /// <typeparam name="TView">View type</typeparam>
    /// <typeparam name="TProperty">Property type</typeparam>
    /// <param name="self">View</param>
    /// <param name="propertySelector">Target property selector</param>
    /// <param name="source">Source property</param>
    /// <param name="updateSourceTrigger">Update source trigger</param>
    /// <returns>Data binding token</returns>
    public static IDisposable SetBindingTableViewDataSource <TView, TProperty>(
        this TView self,
        Expression <Func <TView, TProperty> > propertySelector,
        ReactiveProperty <TProperty> source, Func <TView, IObservable <Unit> > updateSourceTrigger = null)
        where TView : IUITableViewDataSource
    {
        var d = new CompositeDisposable();

        var isUpdating = false;
        var setter     = AccessorCache <TView> .LookupSet(propertySelector, out var propertyName);

        source
        .Where(_ => !isUpdating)
        .Subscribe(x => setter(self, x))
        .AddTo(d);
        if (updateSourceTrigger != null)
        {
            var getter = AccessorCache <TView> .LookupGet(propertySelector, out propertyName);

            updateSourceTrigger(self).Subscribe(_ =>
            {
                isUpdating = true;
                try
                {
                    source.Value = getter(self);
                }
                finally
                {
                    isUpdating = false;
                }
            }).AddTo(d);
        }

        return(d);
    }
        /// <summary>
        /// バルク方式で指定のデータを挿入するためのコマンドを生成します。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <param name="data">挿入するデータ</param>
        /// <returns>コマンド</returns>
        private DbCommand CreateBulkInsertCommand <T>(IEnumerable <T> data)
        {
            //--- 実体化
            data = data.Materialize();

            //--- build DbCommand
            var     factory = DbProvider.GetFactory(this.DbKind);
            dynamic command = factory.CreateCommand();

            command.Connection     = (dynamic)this.Connection;
            command.CommandText    = PrimitiveSql.CreateInsert <T>(this.DbKind, false, true);
            command.BindByName     = true;
            command.ArrayBindCount = data.Count();
            if (this.Timeout.HasValue)
            {
                command.CommandTimeout = this.Timeout.Value;
            }

            //--- bind params
            foreach (var x in TableMappingInfo.Create <T>().Columns)
            {
                var getter = AccessorCache <T> .LookupGet(x.PropertyName);

                dynamic parameter = factory.CreateParameter();
                parameter.ParameterName = x.PropertyName;
                parameter.DbType        = x.ColumnType;
                parameter.Value         = data.Select(y => getter(y)).ToArray();
                command.Parameters.Add(parameter);
            }
            return(command);
        }
Example #4
0
        /// <summary>
        /// 指定したデータからバルクインサート用のSQL文を生成します。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <param name="data">挿入するデータ</param>
        /// <returns>SQL文</returns>
        private string CreateBulkInsertSql <T>(IEnumerable <T> data)
        {
            var prefix      = this.DbKind.GetBindParameterPrefix();
            var table       = TableMappingInfo.Create <T>();
            var columnNames = table.Columns.Select(x => "    " + x.ColumnName);
            var builder     = new StringBuilder();

            builder.AppendLine($"insert into {table.FullName}");
            builder.AppendLine("(");
            builder.AppendLine(string.Join($",{Environment.NewLine}", columnNames));
            builder.AppendLine(")");
            builder.Append("values");

            var getters = table.Columns.Select(c => AccessorCache <T> .LookupGet(c.PropertyName)).ToArray();

            foreach (var x in data)
            {
                builder.AppendLine();
                builder.Append("(");
                var values = getters.Select(f => ToSqlLiteral(f(x)));
                builder.Append(string.Join(", ", values));
                builder.Append("),");
            }
            builder.Length--;  //--- 最後の「,」を削除

            return(builder.ToString());
        }
        private static IDisposable CreateTowWayBinding <T, TTarget, TProperty>(IReactiveProperty <T> self, TTarget target, Expression <Func <TTarget, TProperty> > propertySelector, Func <T, TProperty> convert, Func <TProperty, T> convertBack, IObservable <Unit> targetUpdateTrigger, TProperty propertyFallbackValue, T sourceFallbackValue)
        {
            if (targetUpdateTrigger == null)
            {
                throw new NotSupportedException("TwoWay binding required targetUpdateTrigger parameter.");
            }

            var propertyName   = default(string);
            var d              = new CompositeDisposable();
            var targetUpdating = false;
            var sourceUpdating = false;

            targetUpdateTrigger
            .Subscribe(_ =>
            {
                if (sourceUpdating)
                {
                    return;
                }

                targetUpdating = true;
                try
                {
                    self.Value = convertBack(AccessorCache <TTarget> .LookupGet(propertySelector, out propertyName)(target));
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                    self.Value = sourceFallbackValue;
                }
                targetUpdating = false;
            })
            .AddTo(d);
            self.Subscribe(value =>
            {
                if (targetUpdating)
                {
                    return;
                }

                var setter     = AccessorCache <TTarget> .LookupSet(propertySelector, out propertyName);
                sourceUpdating = true;
                try
                {
                    setter(target, convert(value));
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                    setter(target, propertyFallbackValue);
                }
                sourceUpdating = false;
            })
            .AddTo(d);
            return(d);
        }
Example #6
0
 private object?GetPropertyValue()
 {
     EnsureDispose();
     if (Source == null)
     {
         return(null);
     }
     return((_getAccessor ?? (_getAccessor = AccessorCache.LookupGet(Source.GetType(), PropertyName)))
            .DynamicInvoke(Source));
 }
Example #7
0
    /// <summary>
    /// Converts target property's ErrorsChanged to an observable sequence.
    /// </summary>
    /// <typeparam name="TSubject">The type of the subject.</typeparam>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="subject">The subject.</param>
    /// <param name="propertySelector">Argument is self, Return is target property.</param>
    /// <returns></returns>
    public static IObservable <TProperty> ObserveErrorInfo <TSubject, TProperty>(
        this TSubject subject, Expression <Func <TSubject, TProperty> > propertySelector)
        where TSubject : INotifyDataErrorInfo
    {
        var accessor = AccessorCache <TSubject> .LookupGet(propertySelector, out var propertyName);

        var result = subject.ErrorsChangedAsObservable()
                     .Where(e => e.PropertyName == propertyName)
                     .Select(_ => accessor.Invoke(subject));

        return(result);
    }
Example #8
0
    /// <summary>
    /// Observe collection element's IObservable sequence.
    /// </summary>
    /// <typeparam name="TCollection">Type of collection</typeparam>
    /// <typeparam name="TElement">Type of collection element</typeparam>
    /// <typeparam name="TProperty">Type of observable property element</typeparam>
    /// <param name="source">Source collection</param>
    /// <param name="propertySelector">IObservable selection expression</param>
    /// <returns>IObservable sequence</returns>
    public static IObservable <PropertyPack <TElement, TProperty> > ObserveElementObservableProperty <TCollection, TElement, TProperty>(TCollection source, Expression <Func <TElement, IObservable <TProperty> > > propertySelector)
        where TCollection : INotifyCollectionChanged, IEnumerable <TElement>
        where TElement : class
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        if (propertySelector == null)
        {
            throw new ArgumentNullException(nameof(propertySelector));
        }

        if (!(propertySelector.Body is MemberExpression memberExpression))
        {
            if (!(propertySelector.Body is UnaryExpression unaryExpression))
            {
                throw new ArgumentException(nameof(propertySelector));
            }
            var operand = unaryExpression.Operand as MemberExpression;
            if (operand == null)
            {
                throw new ArgumentException(nameof(propertySelector));
            }
            memberExpression = operand;
        }

        var propertyInfo = memberExpression.Member as PropertyInfo;

        if (propertyInfo == null)
        {
            throw new ArgumentException($"{nameof(propertySelector)} is not property expression");
        }

        // no use
        var getter = AccessorCache <TElement> .LookupGet(propertySelector, out var propertyName);

        return(ObserveElementCore <TCollection, TElement, PropertyPack <TElement, TProperty> >
               (
                   source,
                   (x, observer) => getter(x).Subscribe(y =>
        {
            var pair = PropertyPack.Create(x, propertyInfo, y);
            observer.OnNext(pair);
        })
               ));
    }
        public void LookupGetAndSet()
        {
            var get = AccessorCache <Person> .LookupGet(p => p.Name, out var prop);

            Assert.AreEqual("Name", prop);

            var set = AccessorCache <Person> .LookupSet(p => p.Name, out prop);

            Assert.AreEqual("Name", prop);

            var person = new Person {
                Name = "tanaka"
            };

            get(person).Is("tanaka");
            set(person, "kimura");
            person.Name.Is("kimura");
        }
Example #10
0
    private static IObservable <TProperty> ObserveSimpleProperty <TSubject, TProperty>(
        this TSubject subject, Expression <Func <TSubject, TProperty> > propertySelector,
        bool isPushCurrentValueAtFirst = true)
        where TSubject : INotifyPropertyChanged
    {
        var isFirst  = true;
        var accessor = AccessorCache <TSubject> .LookupGet(propertySelector, out var propertyName);

        return(Observable.Defer(() =>
        {
            var flag = isFirst;
            isFirst = false;

            var q = subject.PropertyChangedAsObservable()
                    .Where(e => e.PropertyName == propertyName || string.IsNullOrEmpty(e.PropertyName))
                    .Select(_ => accessor.Invoke(subject));
            return (isPushCurrentValueAtFirst && flag) ? q.StartWith(accessor.Invoke(subject)) : q;
        }));
    }
Example #11
0
        /// <summary>
        /// バルク方式での挿入処理の準備を行います。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <param name="executor">バルク処理実行機能</param>
        /// <param name="data">挿入する生データ</param>
        /// <returns>挿入するデータ</returns>
        private DataTable SetupBulkInsert <T>(SqlBulkCopy executor, IEnumerable <T> data)
        {
            //--- タイムアウト
            if (this.Timeout.HasValue)
            {
                executor.BulkCopyTimeout = this.Timeout.Value;
            }

            //--- 対象テーブル名
            var info = TableMappingInfo.Create <T>();

            executor.DestinationTableName = info.FullName;

            //--- 列のマップ
            var table   = new DataTable();
            var getters = new List <Func <T, object> >();

            foreach (var x in info.Columns)
            {
                executor.ColumnMappings.Add(x.PropertyName, x.ColumnName);
                table.Columns.Add(new DataColumn {
                    ColumnName  = x.PropertyName,
                    DataType    = x.IsNullable ? Nullable.GetUnderlyingType(x.PropertyType) : x.PropertyType,
                    AllowDBNull = x.IsNullable
                });
                getters.Add(AccessorCache <T> .LookupGet(x.PropertyName));
            }

            //--- データ生成
            foreach (var x in data)
            {
                var row = table.NewRow();
                for (int i = 0; i < getters.Count; i++)
                {
                    row[i] = getters[i](x);
                }
                table.Rows.Add(row);
            }
            return(table);
        }
        /// <summary>
        /// InsertAndGetするためのパラメーターを生成します。
        /// </summary>
        /// <typeparam name="T">テーブルにマッピングされた型</typeparam>
        /// <param name="data">挿入するデータ</param>
        /// <returns>パラメーター</returns>
        private Tuple <DbCommand, DbParameter> CreateInsertAndGetParameter <T>(T data)
        {
            //--- command
            var     factory = DbProvider.GetFactory(this.DbKind);
            dynamic command = factory.CreateCommand();

            command.BindByName = true;
            command.Connection = (dynamic)this.Connection;
            if (this.Timeout.HasValue)
            {
                command.CommandTimeout = this.Timeout.Value;
            }

            //--- parameters
            DbParameter output = null;

            foreach (var x in TableMappingInfo.Create <T>().Columns)
            {
                dynamic parameter = factory.CreateParameter();
                parameter.ParameterName = x.PropertyName;
                parameter.DbType        = x.ColumnType;
                if (x.IsPrimaryKey)
                {
                    parameter.Direction = ParameterDirection.Output;
                    output = parameter;
                    command.CommandText =
                        $@"{PrimitiveSql.CreateInsert<T>(this.DbKind)}
returning {x.ColumnName} into :{x.PropertyName}";
                }
                else
                {
                    parameter.Direction = ParameterDirection.Input;
                    parameter.Value     = AccessorCache <T> .LookupGet(x.PropertyName)(data);
                }
                command.Parameters.Add(parameter);
            }

            //--- ok
            return(Tuple.Create((DbCommand)command, output));
        }
        private static IDisposable CreateOneWayToSourceBinding <T, TTarget, TProperty>(IReactiveProperty <T> self, TTarget target, Expression <Func <TTarget, TProperty> > propertySelector, Func <TProperty, T> convertBack, IObservable <Unit> targetUpdateTrigger, T sourceFallbackValue)
        {
            var propertyName = default(string);

            if (targetUpdateTrigger == null)
            {
                throw new NotSupportedException("OneWayToSource binding required targetUpdateTrigger parameter.");
            }
            return(targetUpdateTrigger
                   .Subscribe(_ =>
            {
                try
                {
                    self.Value = convertBack(AccessorCache <TTarget> .LookupGet(propertySelector, out propertyName)(target));
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                    self.Value = sourceFallbackValue;
                }
            }));
        }