Please note: this article has been superceded by the documentation for the ChannelAdam WCF Library.

Background

In my previous article, How To Call WCF Services Properly, I researched and highlighted the typical problems with using WCF clients (such as memory and connection leaks), and concluded that the most correct solution for consuming WCF services would:

  • Perform the Close/Abort pattern without a race condition;
  • Handle the situation when the service operation throws exceptions;
  • Handle the situations when both the Close and Abort methods throw exceptions; and
  • Handle asynchronous exceptions such as the ThreadAbortException.

And below was my sample code for correctly using a WCF client, performing exception handling and correctly performing the Close/Abort pattern.

SampleServiceClient client = null;

try
{
client = new SampleServiceClient(); var response = client.SampleOperation(1234); // Do some business logic
}
catch (FaultException<MyCustomException>)
{
// Do some business logic for this SOAP Fault Exception
}
catch (FaultException)
{
// Do some business logic for this SOAP Fault Exception
}
catch (CommunicationException)
{
// Catch this expected exception so it is not propagated further.
// Perhaps write this exception out to log file for gathering statistics...
}
catch (TimeoutException)
{
// Catch this expected exception so it is not propagated further.
// Perhaps write this exception out to log file for gathering statistics...
}
catch (Exception)
{
// An unexpected exception that we don't know how to handle.
// Perhaps write this exception out to log file for support purposes...
throw;
}
finally
{
// This will:
// - be executed if any exception was thrown above in the 'try' (including ThreadAbortException); and
// - ensure that CloseOrAbortServiceChannel() itself will not be interrupted by a ThreadAbortException
// (since it is executing from within a 'finally' block)
CloseOrAbortServiceChannel(client); // Unreference the client
client = null;
} private void CloseOrAbortServiceChannel(ICommunicationObject communicationObject)
{
bool isClosed = false; if (communicationObject == null || communicationObject.State == CommunicationState.Closed)
{
return;
} try
{
if (communicationObject.State != CommunicationState.Faulted)
{
communicationObject.Close();
isClosed = true;
}
}
catch (CommunicationException)
{
// Catch this expected exception so it is not propagated further.
// Perhaps write this exception out to log file for gathering statistics...
}
catch (TimeoutException)
{
// Catch this expected exception so it is not propagated further.
// Perhaps write this exception out to log file for gathering statistics...
}
catch (Exception)
{
// An unexpected exception that we don't know how to handle.
// Perhaps write this exception out to log file for support purposes...
throw;
}
finally
{
// If State was Faulted or any exception occurred while doing the Close(), then do an Abort()
if (!isClosed)
{
AbortServiceChannel(communicationObject);
}
}
} private static void AbortServiceChannel(ICommunicationObject communicationObject)
{
try
{
communicationObject.Abort();
}
catch (Exception)
{
// An unexpected exception that we don't know how to handle.
// If we are in this situation:
// - we should NOT retry the Abort() because it has already failed and there is nothing to suggest it could be successful next time
// - the abort may have partially succeeded
// - the actual service call may have been successful
//
// The only thing we can do is hope that the channel's resources have been released.
// Do not rethrow this exception because the actual service operation call might have succeeded
// and an exception closing the channel should not stop the client doing whatever it does next.
//
// Perhaps write this exception out to log file for gathering statistics and support purposes...
}
}

Unfortunately, due to the way Microsoft designed WCF, if you want to use a WCF service client cleanly without connection or memory leaks, then above is the type of extensive code that must be written for every service operation call.

I then promised to share an even better way to achieve this - by using some programming tricks to provide a nice, clean API for working with WCF clients, that significantly reduces the amount of that code that developers have to write.

What Is The Easy, Clean and Robust Solution?

Well, here it is! Drum roll please...

After more than a month of developing late into the evening and early morning, allow me to introduce the ChannelAdam WCF Library - an open source (Apache License 2.0) library that allows you to forget about the complex subtleties of WCF clients, channels and communication states, and lets you just use your service client (very similar to how you probably are doing it now - but without the leaks!).

When you use the ChannelAdam WCF Library's ServiceConsumer, you know that you can keep calling service operations through the ServiceConsumer and it will 'just work' and not leak connections or memory - no matter what happens!

The ChannelAdam WCF Library's ServiceConsumer hides the inner complexity of service channels. For example, if a service channel / communication object / service client moves into a Fault state, the ServiceConsumer will correctly perform the Close/Abort pattern and dispose of the channel. The next time a request is made through the ServiceConsumer, a new channel is created under the covers to seamlessly process the request.

Where Do I Get The Library?

You can install the ChannelAdam.Wcf NuGet package from within Visual Studio, or find it on the NuGet.org gallery at
https://www.nuget.org/packages/ChannelAdam.Wcf.

If you are keen to see some of the programming tricks and how it
works, or possibly contribute features to it, the source code for the
ChannelAdam WCF Library is on CodePlex at https://channeladamwcf.codeplex.com.

Show Me The Basics! How Do I Use It?

Let's assume for the duration of this article that there is a web service named FakeService and in Visual Studio we have generated a service reference and interface to FakeService, and there is now an IFakeService interface and a FakeServiceClient class to use.
That is all normal and basic Visual Studio functionality.

Let's also assume that the service has the following operation signature:

int AddIntegers(int first, int second);

Create a ServiceConsumer with the ServiceConsumerFactory

The first thing you need to understand is that you consume your service operations through the usage of the ChannelAdam.ServiceModel.ServiceConsumer class.

The second thing you need to understand is that the best way to create the ServiceConsumer is by using the Create method on the ChannelAdam.ServiceModel.ServiceConsumerFactory.

In order to use the ServiceConsumerFactory.Create method, simply pass in as a parameter a factory method for creating the service client.

For example:

IServiceConsumer<IFakeService> service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()));

The ServiceConsumerFactory.Create method returns a IServiceConsumer<TServiceInterface> type. In all further examples, I will use the var keyword instead - such as:

var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()));

Alternatively, you can pass in as a string parameter the endpoint configuration name from the service client section of your application's .config file. For example:

var service = ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService"));

Four Ways to Clean Up the ServiceConsumer

There are four ways in which the ServiceConsumer can be cleaned up:

  1. Explicitly with the Close method;
  2. Explicitly with the Dispose method;
  3. Implicitly via the Using statement; or
  4. Automatically by the Garbage Collector.

The Close Method

The ServiceConsumer and underlying channel can be closed immediately by calling the Close method on the ServiceConsumer instance.

service.Close();

If you want to, you still can immediately call another service operation via that same ServiceConsumer instance, as under the covers, a new service channel will be created seamlessly and automatically.

The Dispose Method

The ServiceConsumer and underlying channel can be disposed of immediately by calling the Dispose method on the ServiceConsumer instance.

service.Dispose();

As per standard IDisposable concepts, you should not attempt to reuse the ServiceConsumer after disposing it.

The Using Statement

Instead of calling the Close or Dispose methods directly, you can use the Using statement on the ServiceConsumer because it is a well behaved implementation of IDisposable (unlike Microsoft's service clients!).

using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient())))
{
...
}

Garbage Collection

If you happen to accidently forget to wrap your ServiceConsumer instance in a Using statement or call the Close method, rest assured that the ServiceConsumer will still be cleaned up! When the ServiceConsumer instance goes out of scope, the .NET Garbage Collector will [eventually] kick in and call its destructor/finalise method, thus correctly cleaning up the service channel and preventing any leaks.

Since there is an indeterminate amount of time before the Garbage Collector kicks in, I would recommend the Using statement approach, as it minimises the number of open channels and other resources currently in use, over the lifetime of your application.

How To Call a Service Operation

There are two ways in which to call a service operation via the ServiceConsumer:

  1. Via the Operations property; or
  2. Via the Consume method.

The Operations Property

The Operations property exposes the interface of your service. It can be used just like the normal service client that you already would be familiar with.

Below is an example of how to call the AddIntegers operation via the Operations property.

using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient())))
{
try
{
int actualValue = service.Operations.AddIntegers(1, 1);
Assert.AreEqual(2, actualValue);
}
...
}

And below is an example of how to do some exception handling.

using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient())))
{
try
{
int actualValue = service.Operations.AddIntegers(1, 1);
Assert.AreEqual(2, actualValue);
}
catch (FaultException<MyCustomException> fe)
{
Console.WriteLine("Service operation threw a custom fault: " + fe.ToString());
}
catch (FaultException fe)
{
Console.WriteLine("Service operation threw a fault: " + fe.ToString());
}
catch (Exception ex)
{
Console.WriteLine("An unexpected error occurred while calling the service operation: " + ex.ToString());
}
}

You may notice that there is now no need to catch a CommunicationException or TimeOutException and try to perform the Close/Abort pattern, as the underlying channel is dealt with automatically for you by the ServiceConsumer.

The Consume Method

As an alternative to the Operations property, you can call a service operation via the Consume method on the ServiceConsumer instance.

The Consume method returns an instance of IOperationResult<TReturnTypeOfYourOperation> which wraps the result of the operation. This interface has the following helpful properties:

  • TReturnTypeOfYourOperation Value - which gives you access to the return value from the operation;
  • bool HasException - whether there was an exception thrown by the service operation;
  • bool HasNoException - whether there was not an exception thrown by the service operation;
  • bool HasFaultException - whether an exception of type FaultException was thrown by the service operation;
  • bool HasFaultException<OfThisType> - whether an exception of FaultException<OfThisType> was thrown by the service operation; and
  • Exception Exception - the exception that was thrown by the service operation.

Below is an example of how to call the AddIntegers operation via the Consume method.

using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient())))
{
IOperationResult<int> result = service.Consume(operation => operation.AddIntegers(1, 1)); if (result.HasNoException)
{
Assert.AreEqual(2, result.Value);
}
...
}

And below is an example of how to do some exception handling with IOperationResult.

using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient())))
{
var result = service.Consume(operation => operation.AddIntegers(1, 1)); if (result.HasNoException)
{
int actualValue = result.Value;
Assert.AreEqual(2, actualValue);
}
else
{
if (result.HasFaultExceptionOfType<MyCustomException>())
{
Console.WriteLine("Service operation threw a custom fault: " + result.Exception.ToString());
}
else if (result.HasFaultException)
{
Console.WriteLine("Service operation threw a fault: " + result.Exception.ToString());
}
else
{
Console.WriteLine("An unexpected error occurred while calling the service operation: " + result.Exception.ToString());
}
}
}

More Advanced Usage - With IoC Containers

It is easy to use the ServiceConsumerFactory from Inversion of Control (IoC) containers. Below are examples with three popular IoC containers.

Let's assume that there is the following class and we want to perform constructor injection with it.

public class ConstructorInjectedController
{
public IServiceConsumer<IFakeService> FakeService { get; set; } /// <summary>
/// Initializes a new instance of the <see cref="ConstructorInjectedController"/> class.
/// </summary>
/// <param name="fakeService">The fake service.</param>
public ConstructorInjectedController(IServiceConsumer<IFakeService> fakeService)
{
this.FakeService = fakeService;
}
}

AutoFac

Here is how to use the ServiceConsumerFactory with AutoFac.

var builder = new Autofac.ContainerBuilder();
builder.Register(c => ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService")).InstancePerDependency();
builder.RegisterType<ConstructorInjectedController>(); var autofacContainer = builder.Build(); var controller = autofacContainer.Resolve<ConstructorInjectedController>();

SimpleInjector

Here is how to use the ServiceConsumerFactory with SimpleInjector.

var simpleInjectorContainer = new SimpleInjector.Container();
simpleInjectorContainer.Register(() => ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService"), SimpleInjector.Lifestyle.Transient);
simpleInjectorContainer.Verify(); var controller = simpleInjectorContainer.GetInstance<ConstructorInjectedController>();

Unity

And, here is how to use the ServiceConsumerFactory with Unity.

var unityContainer = new UnityContainer();

unityContainer
.RegisterType<IServiceConsumer<IFakeService>>(
new TransientLifetimeManager(),
new InjectionFactory(c => ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService"))) //
// OR
//
//.RegisterType<IServiceConsumer<IFakeService>>(
// new InjectionFactory(c => ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))) //
// OR more 'correctly'
//
//.RegisterType<IFakeService, FakeServiceClient>(new TransientLifetimeManager())
//.RegisterType<IServiceConsumer<IFakeService>>(
// new InjectionFactory(c =>
// ServiceConsumerFactory.Create<IFakeService>(() => (ICommunicationObject)c.Resolve<IFakeService>())))
; var controller = unityContainer.Resolve<ConstructorInjectedController>();

But Wait, There's More - Channel Close Trigger Strategies

When an exception occurs while calling the service operation, the ServiceConsumer follows a specific strategy to determine whether the exception and/or current channel state is severe enough to necessitate and trigger the closing/aborting of the service channel.

These "Service Channel Close Trigger Strategies" are an implementation of the interface IServiceChannelCloseTriggerStrategy. There is one method in this interface:

bool ShouldCloseChannel(ICommunicationObject channel, Exception exception);

If the method ShouldCloseChannel returns true, then the ServiceConsumer will perform the Close/Abort pattern.

Out of the box, the DefaultServiceChannelCloseTriggerStrategy is used. This strategy is very simple, effective and safe. It triggers a close to occur if the exception is anything but a FaultException, regardless of the state of the service channel. It is highly probable that the channel is no longer usable in the case where there was any other type of exception.

If you find that this strategy does not suit your need, you can create your own class that implements that interface and specify your class as one of the overloads of the Create method on the ServiceConsumerFactory. Alternatively, you can directly set the property ChannelCloseTriggerStrategy on the ServiceConsumer instance itself.

But Wait, There's More - Exception Behaviour Strategies

As you know, the ServiceConsumer automatically handles the clean up of service channels. If an exception occurs at any point throughout the usage of the channel, the ServiceConsumer will catch it and close the channel if necessary. In some cases, for example, if an exception occurs while attempting to close the channel, the ServiceConsumer does not want to propagate the exception back to the calling code, as it is not the caller's concern. In order to be a good citizen and prevent the ServiceConsumer from swallowing exceptions, the concept of the "Exception Behaviour Strategy" was born.

Whenever an exception occurs throughout the lifetime of the ServiceConsumer, a corresponding method on the configured Exception Behaviour Strategy class is invoked, thus allowing the exception to be logged and statistics recorded for an organisation's support purposes.

Each Exception Behaviour Strategy class implements the interface IServiceConsumerExceptionBehaviourStrategy. This interface contains the following methods:

  • PerformFaultExceptionBehaviour - the behaviour to perform when a fault exception occurs while the service operation is called;
  • PerformCommunicationExceptionBehaviour - the behaviour to perform when a communication exception occurs while the service operation is called;
  • PerformTimeoutExceptionBehaviour - the behaviour to perform when a timeout exception occurs while the service operation is called;
  • PerformUnexpectedExceptionBehaviour - the behaviour to perform when an unexpected exception occurs while the service operation is called;
  • PerformCloseCommunicationExceptionBehaviour - the behaviour to perform when a communication exception occurs during a close;
  • PerformCloseTimeoutExceptionBehaviour - the behaviour to perform when a timeout exception occurs during a close;
  • PerformCloseUnexpectedExceptionBehaviour - the behaviour to perform when an unexpected exception occurs during a close;
  • PerformAbortExceptionBehaviour - the behaviour to perform when an exception occurs during an abort; and
  • PerformDestructorExceptionBehaviour - the behaviour to perform when an exception occurs during a destructor/Finalize method.

The ChannelAdam.Wcf Library comes with the following Exception Behaviour Strategies:

  • NullServiceConsumerExceptionBehaviourStrategy - which does not write out any exception details anywhere;
  • StandardOutServiceConsumerExceptionBehaviourStrategy - which writes out all exceptions to the Standard Error stream; and
  • StandardErrorServiceConsumerExceptionBehaviourStrategy - which writes out all exceptions to the Standard Out stream.

By default, the ServiceConsumerFactory configures each ServiceConsumer with the StandardErrorServiceConsumerExceptionBehaviourStrategy.

There are three ways to change the Exception Behaviour Strategy that is assigned when you create a ServiceConsumer:

  1. Specify a different Exception Behaviour Strategy on one of the overloads of the Create method on the ServiceConsumerFactory;
  2. Directly set the property ChannelCloseTriggerStrategy on the ServiceConsumer instance itself; or
  3. Change the default - in some bootstrap code, set the static property ServiceConsumerFactory.DefaultExceptionBehaviourStrategy to your desired strategy. From then on, that strategy will be used by default.

As I expect will be the case, many organisations will want to use a logging library and write out the exceptions to a log file somewhere. Easy - just implement the interface and set it as the default in some bootstrap code.

But Wait, There's More - Retry Capability for Handling Transient Faults

When you call your web services, do you currently handle transient errors that may occur (such as the network briefly dropping out), and have some automatic retry logic?

Well, the ChannelAdam WCF Library has some built-in support for the Microsoft Practices Transient Fault Handling Library - but only when you use the Consume method and not when you use the Operations property.

Naive Retry Logic with the Operations Property

Below is an example naive implementation of retry logic (without the help of any library) while using the Operations property.

int retryCount = 1;
Exception lastException = null; using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
{
while (retryCount > 0)
{
Console.WriteLine("#### Retry count: " + retryCount); try
{
int actual = service.Operations.AddIntegers(1, 1); Console.WriteLine("Actual: " + actual);
Assert.AreEqual(2, actual); return;
}
catch (FaultException fe)
{
lastException = fe;
Console.WriteLine("Service operation threw a fault: " + fe.ToString());
}
catch (Exception ex)
{
lastException = ex;
Console.WriteLine("Technical error occurred while calling the service operation: " + ex.ToString());
} retryCount--;
}
} Assert.Fail("Service operation was not successfully called");

Unfortunately that code is lengthy and error prone.

Retry Logic with Operations Property and the Microsoft Library

The code above can be improved upon by using the Microsoft Practices Transient Fault Handling Library, as below.

You will notice in this example, I am using the ChannelAdam.ServiceModel.SoapFaultWebServiceTransientErrorDetectionStrategy to determine if there was a transient error or not. If the given exception was a FaultException, this strategy says that was not a transient error and therefore do not retry. If on the other hand any other type of exception occurs, then this strategy will tell the Microsoft library to retry according to the retry policy.

Exception lastException;

using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
{
try
{
int actual = 0; var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2));
var retryPolicy = new RetryPolicy<SoapFaultWebServiceTransientErrorDetectionStrategy>(retryStrategy); retryPolicy.ExecuteAction(() =>
{
actual = service.Operations.AddIntegers(1, 1);
}); Console.WriteLine("Actual: " + actual);
Assert.AreEqual(2, actual); return;
}
catch (FaultException fe)
{
lastException = fe;
Console.WriteLine("Service operation threw a fault: " + fe.ToString());
}
catch (Exception ex)
{
lastException = ex;
Console.WriteLine("Technical error occurred while calling the service operation: " + ex.ToString());
} Assert.Fail("Service operation was not successfully called");

Retry Logic with the Consume Method

For me, both code examples above that use the Operations property is clunky - which is why I prefer using the Consume method!

When using the Consume method, there are three ways to set the retry policy:

  1. Setting the static property DefaultRetryPolicy on the ServiceConsumerFactory - which will apply to all created instances;
  2. As an overload to the Create method on the ServiceConsumerFactory - which will apply just to that ServiceConsumer instance to be created; or
  3. As one of the overloads on the Consume method itself.

For example, the overload on the Consume method:

using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
{
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2));
var retryPolicy = new RetryPolicy<SoapFaultWebServiceTransientErrorDetectionStrategy>(retryStrategy); var result = service.Consume(operation => operation.AddIntegers(1, 1), retryPolicy); if (result.HasNoException)
{
Console.WriteLine("Actual: " + result.Value);
Assert.AreEqual(2, result.Value);
}
else
{
if (result.HasFaultException)
{
Console.WriteLine("Service operation threw a fault: " + result.Exception.ToString());
}
else if (result.HasException)
{
Console.WriteLine("An unexpected error occurred while calling the service operation: " + result.Exception.ToString());
} Assert.Fail("Service operation was not successfully called");
}
}

Perhaps you too may agree that using the retry strategy with the Consume method is simpler and cleaner, in comparison with the Operations property.

What Else Does the ServiceConsumer Do?

If you want to know at a deeper level how the ServiceConsumer and its friends behave, perhaps you would like to read the Behaviour Specifications feature file.

Conclusion

The ChannelAdam WCF Library is designed to make your life as a developer easier when consuming WCF services, by abstracting away the complexities of service channel state transitions and the odd behaviour of the Microsoft WCF libraries that do not comply with even their own design guidelines.

In doing this, the ChannelAdam ServiceConsumer once and for all prevents memory and connection leaks, gives developers the ability to plug in their own strategies for handling different situations (such as logging exceptions), and provides a built-in mechanism to retry service operation calls when transient faults occur.

I hope you like it, and I welcome your feedback!

How To Easily Call WCF Services Properly z的更多相关文章

  1. WCF4.0 –- RESTful WCF Services (1) (入门)

    WCF 很好的支持了 REST 的开发, 而 RESTful 的服务通常是架构层面上的考虑. 因为它天生就具有很好的跨平台跨语言的集成能力,几乎所有的语言和网络平台都支持 HTTP 请求,无需去实现复 ...

  2. Learing WCF Chapter1 WCF Services

    WCF ServicesWCF services are the new distributed boundary in an enterprise application—with an empha ...

  3. IIS8.0 部署WCF Services

    今天在Win 8的IIS上部署WCF Services,访问SVC文件时出现找不到处理程序的错误,以前遇到这个问题时都是尝试通过注册asp.net的方式处理一下,但是在Win8下这招不灵了,出现如下提 ...

  4. [Programming WCF Services]Chapter 1. WCF Essentials - Metadata Exchange

    1.HTTP-GET WCF 方式 通过Http的方式提供metadata 1.1.配置文件方式 <system.serviceModel> <services> <se ...

  5. [Programming WCF Services]Chapter 1. WCF Essentials - EndPoint

    1.配置文件方式设置EndPoint(ABC) 1.1.基本配置 <system.serviceModel> <services> <service name=" ...

  6. WCF4.0 –- RESTful WCF Services

    转自:http://blog.csdn.net/fangxinggood/article/details/6235662 WCF 很好的支持了 REST 的开发, 而 RESTful 的服务通常是架构 ...

  7. 使用WCF测试客户端 z

    http://blog.csdn.net/u013036274/article/details/50570989 [是什么] WCF测试客户端(WCF Test Client)是一个用来测试WCF服务 ...

  8. WCF bindings comparison z

    Binding Protocol/Transport Message Encoding Security Default Session Transaction Duplex BasicHttpBin ...

  9. Service Station - An Introduction To RESTful Services With WCF

    Learning about REST An Abstract Example Why Should You Care about REST? WCF and REST WebGetAttribute ...

随机推荐

  1. 解决Android AVD的方向键DPAD不能用的问题

    Android AVD在生成出来一个新的模拟器之后默认都是不能够使用DPAD的.原因是它禁用了. 解决方式如下 : 找到C:\Documents and Settings\Administrator\ ...

  2. 在eclipse中的tomcat内存设置

    设置步骤如下: 1.点击eclipse上的debug图标旁边的下拉箭头 2.然后选择Run Configurations, 3.系统弹出设置tomcat配置页面,在Argument中末尾添加参数中的V ...

  3. ORACLE 包[转]

    包的作用: 包可以将任何出现在块声明的语句 ( 过程 , 函数 , 游标 , 游标 , 类型 , 变量 ) 放于包中 , 相当于一个容器 .使用包的好处不仅仅是程序的分组管理,还可以使用包中的程序重载 ...

  4. [oracle] ORA-08002:序列XXXXXXX.CURRVAL尚未在此进程中定义

    出现 ORA-08002: 序列XXXXXXX.CURRVAL 尚未在此进程中定义. 导致原因:因为是首次查询序列的当前值,内存中之前并没有缓存序列的任何值,所以需要先查询 一下序列的下一个值(此时, ...

  5. Python基础(三)——集合、有序 无序列表、函数、文件操作

    1.Set集合 class set(object): """ set() -> new empty set object set(iterable) -> n ...

  6. OpenCV图像处理篇之边缘检测算子

    OpenCV图像处理篇之边缘检测算子 转载: http://xiahouzuoxin.github.io/notes/ 3种边缘检测算子 一阶导数的梯度算子 高斯拉普拉斯算子 Canny算子 Open ...

  7. phpstorm laravel单元测试 配置

    laravel中集成了单元测试工具phpunit可以在项目的根目录下进行使用,命令是:phpunti ./tests/单元测试文件名称.在phpstorm中使用phpunit需要做一些配置,指定com ...

  8. 剑指offer系列21--二叉搜索树的后续遍历序列

    * 21[题目]输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果. * 如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. * [注]二叉搜索树特点:左子树比根结 ...

  9. 【转】双机高可用、负载均衡、MySQL(读写分离、主从自动切换)架构设计

    架构简介 前几天网友来信说帮忙实现这样一个架构:只有两台机器,需要实现其中一台死机之后另一台能接管这台机器的服务,并且在两台机器正常服务时,两台机器都能用上.于是设计了如下的架构.此架构主要是由kee ...

  10. MySQL使用Union创建视图报错

    mysql> select * from test_main; +----+-------+ | id | value | +----+-------+ |  1 | ONE   | |  2  ...