/// <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); }
/// <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); }
private object?GetPropertyValue() { EnsureDispose(); if (Source == null) { return(null); } return((_getAccessor ?? (_getAccessor = AccessorCache.LookupGet(Source.GetType(), PropertyName))) .DynamicInvoke(Source)); }
/// <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); }
/// <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"); }
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; })); }
/// <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; } })); }