Those "fine" RFCs mandate from every RFC-client that they beware of not using more than 2 connections per host...
Microsoft implemented this in WebClient. I know that it can be turned off with
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.net>
<connectionManagement>
<add address="*" maxconnection="100" />
</connectionManagement>
</system.net>
</configuration>
(found on http://social.msdn.microsoft.com/forums/en-US/netfxnetcom/thread/1f863f20-09f9-49a5-8eee-17a89b591007 )
But how can I do it programmatically?
Accordin to
http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.defaultconnectionlimit.aspx
"Changing the DefaultConnectionLimit property has no effect on existing
ServicePoint objects; it affects only ServicePoint objects that are
initialized after the change. If the value of this property has not been
set either directly or through configuration, the value defaults to the
constant DefaultPersistentConnectionLimit."
I'd like best to configure the limit when I instanciate the WebClient, but just removing this sad limitation programmatically at the start of my programm would be fine, too.
The server I access is not a regular webserver in the internet, but under my control and in the local lan. I want to do API-calls, but I don't use webservices or remoting
for those interested:
System.Net.ServicePointManager.DefaultConnectionLimit = x (where x is your desired number of connections)
no need for extra references
just make sure this is called BEFORE the service point is created as mentioned above in the post.
With some tips from here and elsewhere I managed to fix this in my application by overriding the WebClient class I was using:
class AwesomeWebClient : WebClient {
protected override WebRequest GetWebRequest(Uri address) {
HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(address);
req.ServicePoint.ConnectionLimit = 10;
return (WebRequest)req;
}
}
This solution allows you to change the connection limit at any time:
private static void ConfigureServicePoint(Uri uri)
{
var servicePoint = ServicePointManager.FindServicePoint(uri);
// Increase the number of TCP connections from the default (2)
servicePoint.ConnectionLimit = 40;
}
The 1st time anyone calls this FindServicePoint, a ServicePoint instance is created and a WeakReference is created to hold on to it inside the ServicePointManager. Subsequent requests to the manager for the same Uri return the same instance. If the connection isn't used after, the GC cleans it up.
If you find the ServicePoint object being used by your WebClient, you can change its connection limit. HttpWebRequest objects have an accessor to retrieve the one they were constructed to use, so you could do it that way. If you're lucky, all your requests might end up sharing the same ServicePoint so you'd only have to do it once.
I don't know of any global way to change the limit. If you altered the DefaultConnectionLimit early enough in execution, you'd probably be fine.
Alternately, you could just live with the connection limit, since most server software is going to throttle you anyway. :)
We have a situation regarding the above piece of configuration in App.Config
In order for this to be valid in a CONSOLE Application,
we added the System.Configuration reference dll.
Without the reference, the above was useless.
Related
Caching behavior of the last Dynamics SDK is driving me crazy.
First, if you want to use CrmServiceClient to access different environments you have to use the parameter RequireNewInstance=True; in the connection string. If not, every instance of CrmServiceClient will use the same connection, even if you create and dispose instances to different environments.
Now, even if you use the RequireNewInstance=True; in the connection string I found that cache still occurs in some scenarios.
var client1 = new CrmServiceClient("RequireNewInstance=True;
Url=https://myCompany.crm.dynamics.com;
Username=myUser#myCompany.onmicrosoft.com; Password=myPassowrd;
AuthType=Office365");
//Now, client 2 points to a url that doesn’t exists:
var client2 = new CrmServiceClient("RequireNewInstance=True;
Url=https://xxx.crm.dynamics.com; Username=myUser#myCompany.onmicrosoft.com;
Password=myPassowrd; AuthType=Office365");
The client2 keeps using the first connection string, so you cannot determine if the new connection string is correct.
Any ideas how to test Dynamics Crm connections strings correctly in my asp.net application?
Late reply, but the behavior you're seeing is because when you're specifying an erroneous URL the discovery service is used to ascertain which instance to connect to.
To prevent this specify SkipDiscovery=True in your connection string:
var connectionString2 = #"AuthType=Office365;Url=https://FAKE.crm.dynamics.com;Username=USERNAME;Password=PASSWORD;RequireNewInstance=True;SkipDiscovery=True;";
Edit: SkipDiscovery is true by default starting with 9.0.7, kudos to #mwardm
I think I found the problem. It seems to only happens on Dynamics 365 online trials, that was the reason we were getting inconsistent results depending on the environment.
Apparently, the url doesn't need to be completely valid to establish a connection to a CRM online trial environment, as long as the credentials are valid and the url structure is kept.
Let's consider the following example:
var client1 = new CrmServiceClient("RequireNewInstance=True;
Url=https://fake.crm.dynamics.com;
Username=myUser#myCompany.onmicrosoft.com; Password=myPassowrd;
AuthType=Office365");
In this case I can substitute the "fake" part of the url with whatever I want, but still execute requests correctly using the CrmServiceClient service.
If I try to do this with another environment (e.g. 2015, on premise, not-trial crm online, etc.), the IsReady property of the CrmServiceClient would return false and I would get an error in the LastCrmError property.
Very weird behavior, and difficult to pinpoint.
Now that I think I understand the inconsistent behavior I know that finally it will not affect our clients, so I will mark this response as the answer even if I still do not know why we have different behavior between a trial and a normal environment..
I agree choosing to reuse the existing connection if you don't include RequireNewInstance=true seems counter-intuitive, but I can't reproduce what you're seeing. If I try the following from LinqPad crmSvcClient2 will print out errors and then throw a null ref on the Execute call (8.2.0.2 version of the SDK). With this version of the SDK you'll want to always check LastCrmError after connecting to see if the connection failed.
var connectionString = #"AuthType=Office365;Url=https://REAL.crm.dynamics.com;Username=USERNAME;Password=PASSWORD;RequireNewInstance=True;";
var connectionString2 = #"AuthType=Office365;Url=https://FAKE.crm.dynamics.com;Username=USERNAME;Password=PASSWORD;RequireNewInstance=True;";
using (var crmSvcClient = new CrmServiceClient(connectionString))
{
"crmSvcClient".Dump();
crmSvcClient.LastCrmError.Dump();
((WhoAmIResponse)crmSvcClient.Execute(new WhoAmIRequest())).OrganizationId.Dump();
crmSvcClient.ConnectedOrgFriendlyName.Dump();
}
using (var crmSvcClient2 = new CrmServiceClient(connectionString2))
{
"crmSvcClient2".Dump();
crmSvcClient2.LastCrmError.Dump();
((WhoAmIResponse)crmSvcClient2.Execute(new WhoAmIRequest())).OrganizationId.Dump();
crmSvcClient2.ConnectedOrgFriendlyName.Dump();
}
I'm building an EF6 web app in Azure and I'm using Azure Cache.
I'm testing calls to my WCF service and I'm getting wildly erratic response times - between 300ms and 15sec!
I configured my code according to the this example and it runs fine locally
I've debugged remotely and I can see that the cache key is being found and the data is getting called from cache, so I'm struggling to understand why there is sych a huge variation in response times. Most of the time it's 5+sec which is obviously waaay too long.
The example I've been testing is as follows:
WCF service GET request to:
http://feniksdev-staging.azurewebsites.net/EkckoNewsService.svc/getFriends
// Cache client configured by settings in application configuration file.
public DataCacheFactory cacheFactory = new DataCacheFactory();
public DataCache _cache;
public DataCache cache
{
get
{
if (_cache == null)
_cache = cacheFactory.GetDefaultCache();
return _cache;
}
set { }
}
...
...
[OperationContract]
[System.ServiceModel.Web.WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "/getFriends")]
public string getFriends()
{
string cachekey = "getFriends/{" + user.Id + "}";
object result = cache.Get(cachekey);
if (result == null)
{
using (EkckoContext entities = new EkckoContext())
{
var frnds = entities.UserConnections.Where(uc => uc.UserId == user.Id).Select(uc => new { Name = uc.Friend.Username }).ToList();
JsonSerializerSettings jsonSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
string json = JsonConvert.SerializeObject(frnds, jsonSettings);
cache.Add(cachekey, json);
return json;
}
}
else
{
return (string)result;
}
}
UserConnection is a simple table in my db and currently has no data, so the call returns an empty JSON array. user is a Session object and currently defaults to 1 for user.Id
When remote-debugging this, the object is found in cache and the cached object is returned. So all good, except the response time still varies by a factor of 20 (300ms - 6sec).
When remote debugging one of the other web service methods, I got the following error when attempting to access the cached object using the corresponding key (object result = cache.Get(cachekey);):
{"ErrorCode:SubStatus:There is a temporary failure. Please retry later. (One or more specified cache servers are unavailable, which could be caused by busy network or servers. For on-premises cache clusters, also verify the following conditions. Ensure that security permission has been granted for this client account, and check that the AppFabric Caching Service is allowed through the firewall on all cache hosts. Also the MaxBufferSize on the server must be greater than or equal to the serialized object size sent from the client.). Additional Information : The client was trying to communicate with the server: net.tcp://ekckodev.cache.windows.net:22238."}
I then set the maxBufferSize in my config as follows:
<configSections>
<section name="dataCacheClients" type="Microsoft.ApplicationServer.Caching.DataCacheClientsSection, Microsoft.ApplicationServer.Caching.Core" allowLocation="true" allowDefinition="Everywhere" />
<section name="cacheDiagnostics" type="Microsoft.ApplicationServer.Caching.AzureCommon.DiagnosticsConfigurationSection, Microsoft.ApplicationServer.Caching.AzureCommon" allowLocation="true" allowDefinition="Everywhere" />
</configSections>
...
...
<system.web>
...
...
<caching>
<outputCache defaultProvider="AFCacheOutputCacheProvider">
<providers>
<add name="AFCacheOutputCacheProvider" type="Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider, Microsoft.Web.DistributedCache" cacheName="default" dataCacheClientName="default" applicationName="AFCacheOutputCache" />
</providers>
</outputCache>
</caching>
</system.web>
....
....
...
<dataCacheClients>
<dataCacheClient name="default">
<autoDiscover isEnabled="true" identifier="ekckodev.cache.windows.net" />
<localCache isEnabled="true" sync="TimeoutBased" objectCount="100000" ttlValue="300" />
<securityProperties mode="Message" sslEnabled="false">
<messageSecurity authorizationInfo="xxxxxxxxxxxxxxxxxxxxxxx" />
</securityProperties>
<transportProperties connectionBufferSize="131072" maxBufferPoolSize="268435456"
maxBufferSize="8388608" maxOutputDelay="2" channelInitializationTimeout="60000"
receiveTimeout="600000"/>
</dataCacheClient>
</dataCacheClients>
But I still get such erratic response times - particularly when hitting the same service call repeatedly.
After adding the maxbuffersize config, the cache calls are still hit-and-miss. some fetch the object; other times I get the same exception, however the port is different
"... The client was trying to communicate with the server: net.tcp://ekckodev.cache.windows.net:22233."}"
Could this be a firewall issue? If so, how do I open the appropriate ports?
I also just got the following exception when instantiating the DataCache object:
_cache = cacheFactory.GetDefaultCache();
ErrorCode:SubStatus:There is a temporary failure. Please retry later.
(One or more specified cache servers are unavailable, which could be caused by busy network or servers.
For on-premises cache clusters, also verify the following conditions. Ensure that security permission has
been granted for this client account, and check that the AppFabric Caching Service is allowed through the
firewall on all cache hosts. Also the MaxBufferSize on the server must be greater than or equal to the
serialized object size sent from the client.)
Any thoughts on why I'm getting such results? it's certainly no quicker WITH the cache than WIHTOUT it, so it appears there is some sort of latency in the cache which doesn't seem right...
Thanks in advance for any help!
UPDATE:
After doing some more searching, it seems I'm not the only one with this issue:
poor performance with azure cache
I find it hard to believe that this is the performance I should expect
UPDATE 2
I have commented out all cache-related code from my service and ran the same tests again. The response times are appreciably lower WITHOUT the cache! the "getFriends" callaverages about 250ms wihtout the cache, but peaks at over 5sec WITH the cache.
My other method that fetches about 4kb of data, was peaking at 20+ seconds with cache and now averages about 2sec WITHOUT the cache.
Again: I find it hard to believe that this is the performance I should expect
UPDATE 3
I have now scrapped Azure Cache in favour of MemoryCache. Nice example here
My service calls are now consistently taking approx 300ms in the browser.
I've opened a ticket with Microsoft Azure support regarding Azure Cache, so I'll update this post when they get in touch and I've asked them why their cache is so rubbish. Just when my faith in Microsoft was climbing :/
Looks like you've arrived at the correct conclusion, which is don't use Azure Managed Cache. About 6 months ago, Microsoft started recommending all new development be done against their Redis-based cache offering in Azure.
We recommend all new developments use Azure Redis Cache.
Strangely, they don't show the option to create a Redis cache in the 'old' Azure management site (manage.windowsazure.com), but they do have it in the "preview" Azure management portal.
My application is a C# Windows service that consumes a WCF service. When the first "Expectation failed (417)" error occurs, I change both ServicePointManager.Expect100Continue and ServicePoint.Expect100Continue to false:
try
{
//ServicePointManager.Expect100Continue = false; // If uncomment all work
var svc = new ServiceClient();
svc.GetData(); // first error
}
catch (ProtocolException pex)
{
if (pex.Message.Contains("(417)"))
{
ServicePointManager.Expect100Continue = false;
var sp = ServicePointManager.FindServicePoint(new Uri(#"http://addr.to.service/service.svc"));
sp.Expect100Continue = false;
var svc = new ServiceClient();
svc.GetData(); // second same error
}
}
However, the second call to the service also fails. But if I set Expect100Continue to false before any connection, communication with the service works correctly.
Is this way correctly to handle Expect100Continue errors? I need the application adapts automatically without user action. What am I forgetting to do this work?
Most of the settings on ServicePointManager are treated as the default values applied on all NEW ServicePoints that are created after that point in the application's life. In the case where you change the setting after seeing the error, you are not actually changing anything on existing ServicePoint instances, including the instance associated with the connection used by WCF in this case.
In your Sample code you are calling ServicePointManager.FindServicePoint to try to find the correct ServicePoint. However, FindServicePoint has several overloads and it is easy to use that API incorrectly. For instance, FindServicePoint will try to take into account things http/https, the host you are connecting to, your proxy configuration, etc. If you are not providing the correct parameters to FindServicePoint, you can easily end up getting the wrong ServicePoint returned to you and your settings will not be applied to the ServicePoint you intended to change.
I would recommend that you use the FindServicePoint overload that takes an IWebProxy object to ensure that you get the right ServicePoint. In most cases, you should be able to pass in WebRequest.DefaultWebProxy as the IWebProxy object.
From the MSDN documentation of ServicePointManager.Expect100Continue,
Changing the value of this property does not affect existing ServicePoint objects. Only new ServicePoint objects created after the change are affected. Therefore changing the value on the existing WCF client will have no effect. You need to create a new WCF client, then call GetData()
Update: I have tried HttpWebRequest and it is also exhibiting the same behaviour.
I'm trying to use WebClient DownloadStringAsync to retrieve some (very small) data in an Outlook add-in (VSTO/.Net 4.0). It's taking about 10-15 seconds before it even makes the request.
Having utilized the powers of google, I was pointed towards the fact that it was trying to pick up the proxy settings, and that I should set these to null. I tried that both in code:
WebClient serviceRequest = new WebClient();
serviceRequest.Proxy = null;
and by adding an App.config file and putting:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.net>
<defaultProxy enabled="false">
<proxy/>
<bypasslist/>
<module/>
</defaultProxy>
</system.net>
</configuration>
I added the file through the 'New Item' interface (I'm not sure if its being picked up and utilised).
Neither of these solutions has worked. Is there any things that I could try changing.
The code in question is as follows:
class MyClient
{
string url = "http://192.168.1.99:4567/contact.json?token={0}&email={1}";
WebClient serviceRequest = new WebClient();
public void getContact(string email, DownloadStringCompletedEventHandler methodName)
{
Uri target = new Uri(String.Format(url, "1234", email));
serviceRequest.Proxy = null;
if(serviceRequest.IsBusy)
{
serviceRequest.CancelAsync(); // Changed our mind and switched email
}
serviceRequest.DownloadStringCompleted += methodName;
serviceRequest.DownloadStringAsync(target);
}
}
Discovered what the problem was.
I was working on a Windows 2003 Server Virtual Machine (what I had available). As soon as I installed Windows 7 (and environment) on another VM and tried it the problem vanished.
The server machine does not have IE Enhanced Security turned on.
I'm following a tutorial on this link http://www.codeproject.com/KB/aspnet/ASPNETService.aspx
Now I'm stuck at these codes
private const string DummyPageUrl =
"http://localhost/TestCacheTimeout/WebForm1.aspx";
private void HitPage()
{
WebClient client = new WebClient();
client.DownloadData(DummyPageUrl);
}
My local application address has a port number after "localhost", so how can I get the full path (can it be done in Application_Start method)? I want it to be very generic so that it can work in any cases.
Thanks a lot!
UPDATE
I tried this in the Application_Start and it runs fine, but return error right away when published to IIS7
String path = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) + VirtualPathUtility.ToAbsolute("~/");
If it is calling back to the same server, perhaps use the Request object:
var url = new Uri(Request.Url, "/TestCacheTimeout/WebForm1.aspx").AbsoluteUri;
Otherwise, store the other server's details in a config file or the database, and just give it the right value.
But a better question would be: why would you talk via http to yourself? Why not just call a class method? Personally I'd be using an external scheduled job to do this.
You need an answer that works when you roll out to a different environment that maybe has a virtual application folder.
// r is Request.Url
var url = new Uri(r, System.Web.VirtualPathUtility.ToAbsolute("~/Folder/folder/page.aspx")).AbsoluteUri;
This will work in all cases and no nasty surprises when you deploy.
I suspect you're using the ASP.NET development server that's built-in to Visual Studio, which has a tendency to change port numbers by default. If that's the case, then you might try simply configuring the development server to always use the same port, as described here. Then just add the port number to your URL, like so:
private const string DummyPageUrl =
"http://localhost:42001/TestCacheTimeout/WebForm1.aspx";