/// <summary> /// Save the specified data of <see cref="IInternalState"/> into database. /// </summary> /// <param name="query">The <see cref="IAdfQuery"/> that defines the datasource name and query statement.</param> /// <param name="data">The data of <see cref="IInternalState"/> that needs to be saved.</param> /// <returns>True if data is successfully saved; otherwise, false. /// This method also returns false if <see cref="IAdfQuery"/> or <see cref="IInternalState"/> is null.</returns> /// <exception cref="System.InvalidOperationException">The current state of the connection is closed.</exception> /// <exception cref="System.Data.DBConcurrencyException">An attempt to execute an INSERT, UPDATE, or DELETE statement resulted in zero records affected.</exception> public bool Save(IAdfQuery query, IInternalState data) { if (query == null) throw new ArgumentNullException("query"); if (data == null) throw new ArgumentNullException("data"); if (!data.IsAltered) return true; var state = data as DictionaryState; if (state == null) throw new InvalidOperationException("State is not a " + typeof(DictionaryState)); var table = query.Tables[0]; var q = new AdfQuery() .From(table); q.QueryType = state.IsNew ? QueryType.Insert : QueryType.Update; foreach (var col in state) { if (col.Key.IsAutoIncrement || col.Key.IsTimestamp) continue; // auto increment fields are not saved if (col.Key.IsIdentity && !state.IsNew) continue; // primary key is not saved when object is not new q.Selects.Add(new Expression { Column = col.Key, Type = ExpressionType.Column }); q.Wheres.Add(new Where { Column = col.Key, Parameter = new Parameter(col.Value, ParameterType.QueryParameter) }); } if (!state.IsNew) { var timestamp = state.Keys.FirstOrDefault(col => col.IsTimestamp); if (timestamp == null) throw new InvalidOperationException("Cannot save this state as it does not have a timestamp"); q .Where(timestamp).IsEqual(state[timestamp]) .Where(state.PrimaryKey).IsEqual(state[state.PrimaryKey]); } return RunSave(state, q); }