当前位置: 首页>>代码示例>>C#>>正文


C# SqlCommand.SqlCommandInfo类代码示例

本文整理汇总了C#中NHibernate.SqlCommand.SqlCommandInfo的典型用法代码示例。如果您正苦于以下问题:C# SqlCommandInfo类的具体用法?C# SqlCommandInfo怎么用?C# SqlCommandInfo使用的例子?那么恭喜您, 这里精选的类代码示例或许可以为您提供帮助。


SqlCommandInfo类属于NHibernate.SqlCommand命名空间,在下文中一共展示了SqlCommandInfo类的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C#代码示例。

示例1: PerformInsert

		public object PerformInsert(SqlCommandInfo insertSQL, ISessionImplementor session, IBinder binder)
		{
			try
			{
				// prepare and execute the insert
				IDbCommand insert = session.Batcher.PrepareCommand(insertSQL.CommandType, insertSQL.Text, insertSQL.ParameterTypes);
				try
				{
					binder.BindValues(insert);
					session.Batcher.ExecuteNonQuery(insert);
				}
				finally
				{
					session.Batcher.CloseCommand(insert, null);
				}
			}
			catch (DbException sqle)
			{
				throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, sqle,
				                                 "could not insert: " + persister.GetInfoString(), insertSQL.Text);
			}

			SqlString selectSQL = SelectSQL;
			using (new SessionIdLoggingContext(session.SessionId)) 
			try
			{
				//fetch the generated id in a separate query
				IDbCommand idSelect = session.Batcher.PrepareCommand(CommandType.Text, selectSQL, ParametersTypes);
				try
				{
					BindParameters(session, idSelect, binder.Entity);
					IDataReader rs = session.Batcher.ExecuteReader(idSelect);
					try
					{
						return GetResult(session, rs, binder.Entity);
					}
					finally
					{
						session.Batcher.CloseReader(rs);
					}
				}
				finally
				{
					session.Batcher.CloseCommand(idSelect, null);
				}
			}
			catch (DbException sqle)
			{
				throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, sqle,
				                                 "could not retrieve generated id after insert: " + persister.GetInfoString(),
				                                 insertSQL.Text);
			}
		}
开发者ID:KaraokeStu,项目名称:nhibernate-core,代码行数:53,代码来源:AbstractSelectingDelegate.cs

示例2: Prepare

		protected internal override IDbCommand Prepare(SqlCommandInfo insertSQL, ISessionImplementor session)
		{
			IDbCommand command = session.Batcher.PrepareCommand(CommandType.Text, insertSQL.Text, insertSQL.ParameterTypes);
			//Add the output parameter
			IDbDataParameter idParameter = factory.ConnectionProvider.Driver.GenerateParameter(command, ReturnParameterName,
			                                                                                         paramType);
			driveGeneratedParamName = idParameter.ParameterName;
			idParameter.Direction = ParameterDirection.ReturnValue;

			command.Parameters.Add(idParameter);
			return command;
		}
开发者ID:hazzik,项目名称:nh-contrib-everything,代码行数:12,代码来源:OutputParamReturningDelegate.cs

示例3: Prepare

		protected internal override IDbCommand Prepare(SqlCommandInfo insertSQL, ISessionImplementor session)
		{
			IDbCommand command = session.Batcher.PrepareCommand(CommandType.Text, insertSQL.Text, insertSQL.ParameterTypes);
			//Add the output parameter
			IDbDataParameter idParameter = factory.ConnectionProvider.Driver.GenerateParameter(command, ReturnParameterName,
			                                                                                         paramType);
			driveGeneratedParamName = idParameter.ParameterName;

            if (factory.Dialect.InsertGeneratedIdentifierRetrievalMethod == InsertGeneratedIdentifierRetrievalMethod.OutputParameter)
                idParameter.Direction = ParameterDirection.Output;
            else if (factory.Dialect.InsertGeneratedIdentifierRetrievalMethod == InsertGeneratedIdentifierRetrievalMethod.ReturnValueParameter)
                idParameter.Direction = ParameterDirection.ReturnValue;
            else
                throw new System.NotImplementedException("Unsupported InsertGeneratedIdentifierRetrievalMethod: " + factory.Dialect.InsertGeneratedIdentifierRetrievalMethod);

			command.Parameters.Add(idParameter);
			return command;
		}
开发者ID:marchlud,项目名称:nhibernate-core,代码行数:18,代码来源:OutputParamReturningDelegate.cs

示例4: PerformInsert

 public object PerformInsert(SqlCommandInfo insertSQL, ISessionImplementor session, IBinder binder)
 {
     try
     {
         // prepare and execute the insert
         IDbCommand insert = Prepare(insertSQL, session);
         try
         {
             binder.BindValues(insert);
             return ExecuteAndExtract(insert, session);
         }
         finally
         {
             ReleaseStatement(insert, session);
         }
     }
     catch (DbException sqle)
     {
         throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, sqle,
                                          "could not insert: " + MessageHelper.InfoString(persister), insertSQL.Text);
     }
 }
开发者ID:zibler,项目名称:zibler,代码行数:22,代码来源:AbstractReturningDelegate.cs

示例5: Prepare

 protected internal abstract IDbCommand Prepare(SqlCommandInfo insertSQL, ISessionImplementor session);
开发者ID:zibler,项目名称:zibler,代码行数:1,代码来源:AbstractReturningDelegate.cs

示例6: GenerateSQLDeleteStrings

		protected SqlCommandInfo[] GenerateSQLDeleteStrings(object[] loadedState)
		{
			int span = TableSpan;
			SqlCommandInfo[] deleteStrings = new SqlCommandInfo[span];

			for (int j = span - 1; j >= 0; j--)
			{
				SqlDeleteBuilder delete = new SqlDeleteBuilder(Factory.Dialect, Factory)
					.SetTableName(GetTableName(j))
					.SetIdentityColumn(GetKeyColumns(j), IdentifierType);

				if (Factory.Settings.IsCommentsEnabled)
				{
					delete.SetComment("delete " + EntityName + " [" + j + "]");
				}

				bool[] versionability = PropertyVersionability;
				IType[] types = PropertyTypes;
				for (int i = 0; i < entityMetamodel.PropertySpan; i++)
				{
					bool include = versionability[i] &&
												 IsPropertyOfTable(i, j);

					if (include)
					{
						// this property belongs to the table and it is not specifically
						// excluded from optimistic locking by optimistic-lock="false"
						string[] _propertyColumnNames = GetPropertyColumnNames(i);
						bool[] propertyNullness = types[i].ToColumnNullness(loadedState[i], Factory);
						SqlType[] sqlt = types[i].SqlTypes(Factory);
						for (int k = 0; k < propertyNullness.Length; k++)
						{
							if (propertyNullness[k])
							{
								delete.AddWhereFragment(_propertyColumnNames[k], sqlt[k], " = ");
							}
							else
							{
								delete.AddWhereFragment(_propertyColumnNames[k] + " is null");
							}
						}
					}
				}
				deleteStrings[j] = delete.ToSqlCommandInfo();
			}

			return deleteStrings;
		}
开发者ID:rbirkby,项目名称:nhibernate-core,代码行数:48,代码来源:AbstractEntityPersister.cs

示例7: Delete

		/// <summary>
		/// Perform an SQL DELETE
		/// </summary>
		public void Delete(object id, object version, int j, object obj, SqlCommandInfo sql, ISessionImplementor session,
											 object[] loadedState)
		{
			if (IsInverseTable(j))
			{
				return;
			}

			// NH : Only use version if lock mode is Version
			bool useVersion = j == 0 && IsVersioned && Versioning.OptimisticLock.Version == entityMetamodel.OptimisticLockMode;

			//bool callable = IsDeleteCallable(j);
			IExpectation expectation = Expectations.AppropriateExpectation(deleteResultCheckStyles[j]);
			bool useBatch = j == 0 && expectation.CanBeBatched && IsBatchable;

			if (log.IsDebugEnabled)
			{
				log.Debug("Deleting entity: " + MessageHelper.InfoString(this, id, Factory));
				if (useVersion)
				{
					log.Debug("Version: " + version);
				}
			}

			if (IsTableCascadeDeleteEnabled(j))
			{
				if (log.IsDebugEnabled)
				{
					log.Debug("delete handled by foreign key constraint: " + GetTableName(j));
				}
				return; //EARLY EXIT!
			}

			try
			{
				int index = 0;
				IDbCommand statement;
				if (useBatch)
				{
					statement = session.Batcher.PrepareBatchCommand(sql.CommandType, sql.Text, sql.ParameterTypes);
				}
				else
				{
					statement = session.Batcher.PrepareCommand(sql.CommandType, sql.Text, sql.ParameterTypes);
				}

				try
				{
					//index += expectation.Prepare(statement, factory.ConnectionProvider.Driver);

					// Do the key. The key is immutable so we can use the _current_ object state - not necessarily
					// the state at the time the delete was issued
					IdentifierType.NullSafeSet(statement, id, index, session);
					index += IdentifierColumnSpan;

					// We should use the _current_ object state (ie. after any updates that occurred during flush)
					if (useVersion)
					{
						VersionType.NullSafeSet(statement, version, index, session);
					}
					else if (entityMetamodel.OptimisticLockMode > Versioning.OptimisticLock.Version && loadedState != null)
					{
						bool[] versionability = PropertyVersionability;
						IType[] types = PropertyTypes;
						for (int i = 0; i < entityMetamodel.PropertySpan; i++)
						{
							if (IsPropertyOfTable(i, j) && versionability[i])
							{
								// this property belongs to the table and it is not specifically
								// excluded from optimistic locking by optimistic-lock="false"
								bool[] settable = types[i].ToColumnNullness(loadedState[i], Factory);

								types[i].NullSafeSet(statement, loadedState[i], index, settable, session);
								index += ArrayHelper.CountTrue(settable);
							}
						}
					}

					if (useBatch)
					{
						session.Batcher.AddToBatch(expectation);
					}
					else
					{
						Check(session.Batcher.ExecuteNonQuery(statement), id, j, expectation, statement);
					}
				}
				catch (Exception e)
				{
					if (useBatch)
					{
						session.Batcher.AbortBatch(e);
					}
					throw;
				}
				finally
				{
//.........这里部分代码省略.........
开发者ID:rbirkby,项目名称:nhibernate-core,代码行数:101,代码来源:AbstractEntityPersister.cs

示例8: UpdateOrInsert

		/// <summary> Perform an SQL UPDATE or SQL INSERT</summary>
		protected internal virtual void UpdateOrInsert(object id, object[] fields, object[] oldFields, object rowId,
			bool[] includeProperty, int j, object oldVersion, object obj, SqlCommandInfo sql, ISessionImplementor session)
		{
			if (!IsInverseTable(j))
			{
				bool isRowToUpdate;
				if (IsNullableTable(j) && oldFields != null && IsAllNull(oldFields, j))
				{
					//don't bother trying to update, we know there is no row there yet
					isRowToUpdate = false;
				}
				else if (IsNullableTable(j) && IsAllNull(fields, j))
				{
					//if all fields are null, we might need to delete existing row
					isRowToUpdate = true;
					Delete(id, oldVersion, j, obj, SqlDeleteStrings[j], session, null);
				}
				else
				{
					//there is probably a row there, so try to update
					//if no rows were updated, we will find out
					isRowToUpdate = Update(id, fields, oldFields, rowId, includeProperty, j, oldVersion, obj, sql, session);
				}

				if (!isRowToUpdate && !IsAllNull(fields, j))
				{
					// assume that the row was not there since it previously had only null
					// values, so do an INSERT instead
					//TODO: does not respect dynamic-insert
					Insert(id, fields, PropertyInsertability, j, SqlInsertStrings[j], obj, session);
				}
			}
		}
开发者ID:rbirkby,项目名称:nhibernate-core,代码行数:34,代码来源:AbstractEntityPersister.cs

示例9: Insert

		/// <summary>
		/// Perform an SQL INSERT, and then retrieve a generated identifier.
		/// </summary>
		/// <remarks>
		/// This form is used for PostInsertIdentifierGenerator-style ids (IDENTITY, select, etc).
		/// </remarks>
		protected object Insert(object[] fields, bool[] notNull, SqlCommandInfo sql, object obj, ISessionImplementor session)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("Inserting entity: " + EntityName + " (native id)");
				if (IsVersioned)
				{
					log.Debug("Version: " + Versioning.GetVersion(fields, this));
				}
			}
			IBinder binder = new GeneratedIdentifierBinder(fields, notNull, session, obj, this);
			return identityDelegate.PerformInsert(sql, session, binder);
		}
开发者ID:rbirkby,项目名称:nhibernate-core,代码行数:19,代码来源:AbstractEntityPersister.cs

示例10: Insert

		/// <summary>
		/// Persist an object, using a natively generated identifier
		/// </summary>
		protected object Insert(object[] fields, bool[] notNull, SqlCommandInfo sql, object obj, ISessionImplementor session)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("Inserting entity: " + ClassName + " (native id)");
				if (IsVersioned)
				{
					log.Debug("Version: " + Versioning.GetVersion(fields, this));
				}
			}

			try
			{
				SqlString insertSelectSQL = null;

				if (sql.CommandType == CommandType.Text)
				{
					insertSelectSQL = Dialect.AddIdentitySelectToInsert(sql.Text, GetKeyColumns(0)[0], GetTableName(0));
				}

				if (insertSelectSQL != null)
				{
					// Use one statement to insert the row and get the generated id
					IDbCommand insertSelect = session.Batcher.PrepareCommand(CommandType.Text, insertSelectSQL, sql.ParameterTypes);
					IDataReader rs = null;
					try
					{
						// Well, it's always the first table to dehydrate, so pass 0 as the position
						Dehydrate(null, fields, notNull, propertyColumnInsertable, 0, insertSelect, session, 0);
						rs = session.Batcher.ExecuteReader(insertSelect);
						return GetGeneratedIdentity(obj, session, rs);
					}
					finally
					{
						session.Batcher.CloseCommand(insertSelect, rs);
					}
				}
				else
				{
					// Do the insert
					IDbCommand statement = session.Batcher.PrepareCommand(sql.CommandType, sql.Text, sql.ParameterTypes);
					try
					{
						// Well, it's always the first table to dehydrate, so pass 0 as the position
						Dehydrate(null, fields, notNull, propertyColumnInsertable, 0, statement, session, 0);
						session.Batcher.ExecuteNonQuery(statement);

						// Fetch the generated id in a separate query. This is done inside the first try/finally block
						// to keep the insert command open, so that the batcher does not close the connection.
						//
						// It's possible that some ADO.NET provider will not allow two open IDbCommands for the same connection,
						// in that case we'll have to rewrite the code to use some sort of lock on IBatcher.
						SqlString idselectSql = new SqlString(SqlIdentitySelect(IdentifierColumnNames[0], GetTableName(0)));
						IDbCommand idselect = session.Batcher.PrepareCommand(CommandType.Text, idselectSql, SqlTypeFactory.NoTypes);
						IDataReader rs = null;

						try
						{
							rs = session.Batcher.ExecuteReader(idselect);
							return GetGeneratedIdentity(obj, session, rs);
						}
						finally
						{
							session.Batcher.CloseCommand(idselect, rs);
						}
					}
					finally
					{
						session.Batcher.CloseCommand(statement, null);
					}
				}
			}
			catch (HibernateException)
			{
				// Do not call Convert on HibernateExceptions
				throw;
			}
			catch (Exception sqle)
			{
				throw Convert(sqle, "could not insert: " + MessageHelper.InfoString(this), sql.Text);
			}
		}
开发者ID:Novthirteen,项目名称:sconit_timesseiko,代码行数:85,代码来源:AbstractEntityPersister.cs

示例11: Update

		protected void Update(
			object id,
			object[] fields,
			object[] oldFields,
			bool[] includeProperty,
			int j,
			object oldVersion,
			object obj,
			SqlCommandInfo sql,
			ISessionImplementor session)
		{
			bool useVersion = j == 0 && IsVersioned;
			IExpectation expectation = Expectations.AppropriateExpectation(updateResultCheckStyles[j]);
			bool useBatch = j == 0 && expectation.CanBeBatched && IsBatchable;
				//note: updates to joined tables can't be batched...

			if (log.IsDebugEnabled)
			{
				log.Debug("Updating entity: " + MessageHelper.InfoString(this, id));
				if (useVersion)
				{
					log.Debug("Existing version: " + oldVersion + " -> New Version: " + fields[VersionProperty]);
				}
			}

			try
			{
				IDbCommand statement = useBatch
				                       	? session.Batcher.PrepareBatchCommand(sql.CommandType, sql.Text, sql.ParameterTypes)
				                       	: session.Batcher.PrepareCommand(sql.CommandType, sql.Text, sql.ParameterTypes);
				try
				{
					int index = 0;

					//index += expectation.Prepare(statement, factory.ConnectionProvider.Driver);
					index = Dehydrate(id, fields, includeProperty, propertyColumnUpdateable, j, statement, session, index);

					// Write any appropriate versioning conditional parameters
					if (useVersion && OptimisticLockMode == OptimisticLockMode.Version)
					{
						VersionType.NullSafeSet(statement, oldVersion, index, session);
					}
					else if (OptimisticLockMode.Version < OptimisticLockMode && null != oldFields)
					{
						bool[] versionability = PropertyVersionability;
						bool[] includeOldField = OptimisticLockMode == OptimisticLockMode.All
						                         	? PropertyUpdateability
						                         	: includeProperty;

						for (int i = 0; i < entityMetamodel.PropertySpan; i++)
						{
							bool include = includeOldField[i] &&
							               IsPropertyOfTable(i, j) &&
							               versionability[i];
							if (include)
							{
								if (!PropertyTypes[i].IsDatabaseNull(oldFields[i]))
								{
									PropertyTypes[i].NullSafeSet(statement, oldFields[i], index, session);
									index += GetPropertyColumnSpan(i);
								}
							}
						}
					}

					if (useBatch)
					{
						session.Batcher.AddToBatch(expectation);
					}
					else
					{
						Check(session.Batcher.ExecuteNonQuery(statement), id, j, expectation, statement);
					}
				}
				catch (Exception e)
				{
					if (useBatch)
					{
						session.Batcher.AbortBatch(e);
					}

					throw;
				}
				finally
				{
					if (!useBatch)
					{
						session.Batcher.CloseCommand(statement, null);
					}
				}
			}
			catch (HibernateException)
			{
				// Do not call Convert on HibernateExceptions
				throw;
			}
			catch (Exception sqle)
			{
				throw Convert(sqle, "could not update: " + MessageHelper.InfoString(this, id), sql.Text);
			}
//.........这里部分代码省略.........
开发者ID:Novthirteen,项目名称:sconit_timesseiko,代码行数:101,代码来源:AbstractEntityPersister.cs

示例12: Append

		public virtual void Append(SqlCommandInfo commandInfo)
		{
			resultSetsCount++;
			sqlString = sqlString.Append(commandInfo.Text).Append(";").Append(Environment.NewLine);
			types.AddRange(commandInfo.ParameterTypes);
		}
开发者ID:pontillo,项目名称:PowerNap,代码行数:6,代码来源:BasicResultSetsCommand.cs

示例13: Prepare

			protected internal override IDbCommand Prepare(SqlCommandInfo insertSQL, ISessionImplementor session)
			{
				return session.Batcher.PrepareCommand(CommandType.Text, insertSQL.Text, insertSQL.ParameterTypes);
			}
开发者ID:jlevitt,项目名称:nhibernate-core,代码行数:4,代码来源:IdentityGenerator.cs

示例14: AbstractCollectionPersister


//.........这里部分代码省略.........

				CheckColumnDuplication(distinctColumns, idColl.Identifier.ColumnIterator);
			}
			else
			{
				identifierType = null;
				identifierColumnName = null;
				identifierColumnAlias = null;
				identifierGenerator = null;
				identityDelegate = null;
			}

			#endregion

			#region GENERATE THE SQL

			// NH Different behavior : for the Insert SQL we are managing isPostInsertIdentifier (not supported in H3.2.5) 
			if (collection.CustomSQLInsert == null)
			{
				if (!IsIdentifierAssignedByInsert)
				{
					sqlInsertRowString = GenerateInsertRowString();
				}
				else
				{
					sqlInsertRowString = GenerateIdentityInsertRowString();
				}
				insertCallable = false;
				insertCheckStyle = ExecuteUpdateResultCheckStyle.Count;
			}
			else
			{
				SqlType[] parmsTypes = GenerateInsertRowString().ParameterTypes;
				sqlInsertRowString = new SqlCommandInfo(collection.CustomSQLInsert, parmsTypes);
				insertCallable = collection.IsCustomInsertCallable;
				insertCheckStyle = collection.CustomSQLInsertCheckStyle
				                   ?? ExecuteUpdateResultCheckStyle.DetermineDefault(collection.CustomSQLInsert, insertCallable);
			}

			sqlUpdateRowString = GenerateUpdateRowString();
			if (collection.CustomSQLUpdate == null)
			{
				updateCallable = false;
				updateCheckStyle = ExecuteUpdateResultCheckStyle.Count;
			}
			else
			{
				sqlUpdateRowString = new SqlCommandInfo(collection.CustomSQLUpdate, sqlUpdateRowString.ParameterTypes);
				updateCallable = collection.IsCustomUpdateCallable;
				updateCheckStyle = collection.CustomSQLUpdateCheckStyle
				                   ?? ExecuteUpdateResultCheckStyle.DetermineDefault(collection.CustomSQLUpdate, updateCallable);
			}

			sqlDeleteRowString = GenerateDeleteRowString();
			if (collection.CustomSQLDelete == null)
			{
				deleteCallable = false;
				deleteCheckStyle = ExecuteUpdateResultCheckStyle.None;
			}
			else
			{
				sqlDeleteRowString = new SqlCommandInfo(collection.CustomSQLDelete, sqlDeleteRowString.ParameterTypes);
				deleteCallable = collection.IsCustomDeleteCallable;
				deleteCheckStyle = ExecuteUpdateResultCheckStyle.None;
			}
开发者ID:khaliyo,项目名称:Spring.net-NHibernate.net-Asp.net-MVC-DWZ-,代码行数:66,代码来源:AbstractCollectionPersister.cs

示例15: Update

        public void Update(object id, object[] fields, int[] dirtyFields, bool hasDirtyCollection,
			object[] oldFields, object oldVersion, object obj, object rowId, ISessionImplementor session)
        {
            //note: dirtyFields==null means we had no snapshot, and we couldn't get one using select-before-update
            //	  oldFields==null just means we had no snapshot to begin with (we might have used select-before-update to get the dirtyFields)

            bool[] tableUpdateNeeded = GetTableUpdateNeeded(dirtyFields, hasDirtyCollection);
            int span = TableSpan;
            bool[] propsToUpdate;
            SqlCommandInfo[] updateStrings;

            if (entityMetamodel.IsDynamicUpdate && dirtyFields != null)
            {
                // For the case of dynamic-update="true", we need to generate the UPDATE SQL
                propsToUpdate = GetPropertiesToUpdate(dirtyFields, hasDirtyCollection);
                // don't need to check laziness (dirty checking algorithm handles that)
                updateStrings = new SqlCommandInfo[span];
                for (int j = 0; j < span; j++)
                {
                    updateStrings[j] = tableUpdateNeeded[j]
                                                            ? GenerateUpdateString(propsToUpdate, j, oldFields, j == 0 && rowId != null)
                                                            : null;
                }
            }
            else
            {
                // For the case of dynamic-update="false", or no snapshot, we use the static SQL
                updateStrings = GetUpdateStrings(rowId != null, HasUninitializedLazyProperties(obj, session.EntityMode));
                propsToUpdate = GetPropertyUpdateability(obj, session.EntityMode);
            }

            for (int j = 0; j < span; j++)
            {
                // Now update only the tables with dirty properties (and the table with the version number)
                if (tableUpdateNeeded[j])
                {
                    UpdateOrInsert(id, fields, oldFields, j == 0 ? rowId : null, propsToUpdate, j, oldVersion, obj, updateStrings[j], session);
                }
            }
        }
开发者ID:aistrate,项目名称:TypingPracticeTexts,代码行数:40,代码来源:AbstractEntityPersister.cs


注:本文中的NHibernate.SqlCommand.SqlCommandInfo类示例由纯净天空整理自Github/MSDocs等开源代码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。