Entity Framework 4 allows us to create complex types. Typically, such types are used to get the result of a stored procedure. However, when we try to send such a complex type using a WCF RIA Domain Service, the following error is encountered.
What to do? Adding attributes to EF designer generated classes is a bad idea; Also, we cannot use partial classes to add attributes to an already defined property. However, luckily, WCF RIA Services uses metadata classes in conjunction with the entity model classes and hence we can always create a meta data class for our complex type and decorate one of the fields with a [Key] attribute. E.g., If our stored procedure is returning StoredProcedure_Result, then we may just need to add the following in our DomainService.metadata.cs file:
The following article summarizes all the possible problems that may arise when deploying a WCF RIA Service application to a shared host. ASPHostCentral.com, as the premier ASP.NET and Windows Hosting provider, proudly presents this article to anyone and we believe it will help many ASP.NET communities; especially to those who are using WCF RIA Service. In case you are looking for WCF RIA Service Hosting, you can always consider ASPHostCentral.com and you can start from our lowest Standard Plan @$4.99/month to host your WCF-service site.
Why : RIA framework dynamically creates WCF service (Domain services) and add endpoints to the service. It first checks if endpoint does not exist then create it, and it checks for 3 endpoints (http, SOAP and binary). After creating end points it adds authentication schema to end points. It picks IIS authentication schemas and tries to apply on end points and failed to apply.
If we could create desired end points in web.config RIA framework will not create or do anything with endpoints and it works successfully.
You just need to follow the simple steps as mentioned on the followings:
1. Add following code to you web.config to solve issue “This collection already contains an address with scheme http..”
<serviceHostingEnvironment aspNetCompatibilityEnabled="true">
<baseAddressPrefixFilters>
<add prefix="http://www.yoursite.com"/>
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
Note: Your service can be only accessed by URL mentioned in above settings. As configured above you can’t access your service via http://yoursite.com. You could also use factory code to host WCF (see below) to resolve this error however alone with that you need to create svc files for each domain service.
2.Add AspNetCompatibilityRequirementsMode attribute to your RIA Domain services classes
Eg .Attributes added to AuthenticationService class under services folder
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class AuthenticationService : AuthenticationBase<User> { }
RIA framework dynamically apply these attributes after creating end points. Since we are now bypassing endpoint creation, we need to manually apply these attributes.
3. For each RIA domain service add following to you configuration file.
E.g. Is shown for AuthenticationService and UserRegistrationService
Where SparkExams is my custom namespace.
<services>
<service name="SparkExams.Web.AuthenticationService"
behaviorConfiguration="RIAServiceBehavior">
<endpoint address="" binding="wsHttpBinding"
contract="SparkExams.Web.AuthenticationService" />
<endpoint address="/soap"
binding="basicHttpBinding"
contract="SparkExams.Web.AuthenticationService" />
<endpoint address="/binary"
binding="customBinding"
bindingConfiguration="BinaryHttpBinding"
contract="SparkExams.Web.AuthenticationService" />
</service>
<service name="SparkExams.Web.UserRegistrationService"
behaviorConfiguration="RIAServiceBehavior">
<endpoint address=""
binding="wsHttpBinding"
contract="SparkExams.Web.UserRegistrationService" />
<endpoint address="/soap"
binding="basicHttpBinding"
contract="SparkExams.Web.UserRegistrationService" />
<endpoint address="/binary"
binding="customBinding" bindingConfiguration="BinaryHttpBinding"
contract="SparkExams.Web.UserRegistrationService" />
</service>
Please note that RIA adds 3 endpoints and if any of these endpoints are missing from web.config it will throw "IIS specified authentication schemes 'Basic, Anonymous'..." error.
Add following behaviours and bindings to your web.config
<behaviors>
<serviceBehaviors>
<behavior name="RIAServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="BinaryHttpBinding">
<binaryMessageEncoding />
<httpTransport />
</binding>
</customBinding>
</bindings>
Test you WCF end points using WCF client test tool (Test client for Windows Communication Foundation services). WcfTestClient.exe : Go to VS 2008 Console and type WcfTestClient.exe.
Note that there is no need to host you service,or change IIS settings by ISP.
-----------------------------------------------------------------------------
Read further if you want to know how this configuration has been discovered...
1. Have Used Red Gate's .NET Reflector to examine RIA assemblies.
2. Plugin my custom DomainServiceHost factory to host service.
3. Set debug points on overridable methods "ApplyConfigutation()" in CustomHost
Please find code for class used to injected service host factory at the end of this post.
4. Check where the code was failing and what RIA has configured before failing.Found that it have configured 3 endpoints for each service. Noticed the minimal configuration and rectified other errors one by one.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Ria.Services;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ComponentModel;
using System.Web.DomainServices;
using System.Net;
namespace System.Web.Ria {
public class DomainServiceHostFactoryEx : DomainServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
Uri baseAddress=null;
try
{
baseAddress = baseAddresses[0];
}
catch (Exception e)
{
baseAddress = new Uri("http://localhost:52878");
}
CustomHost customServiceHost =
new CustomHost(serviceType, baseAddress);
return customServiceHost;
}
}
class CustomHost : DomainServiceHost
{
DomainServiceDescription _domainServiceDescription;
ServiceDescription _sdecreption;
ContractDescription _contract;
public CustomHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
this._domainServiceDescription = DomainServiceDescription.GetDescription(serviceType);
}
protected override ServiceDescription CreateDescription(out IDictionary<string, ContractDescription> implementedContracts)
{
ServiceDescription d = base.CreateDescription(out implementedContracts);
//_contract = implementedContracts[0];
_sdecreption = d;
return d;
}
protected override void ApplyConfiguration()
{
//base.LoadConfigurationSection(new System.ServiceModel.Configuration.ServiceElement(_sdecreption.ConfigurationName));
// try
//{
string error = "";
try
{
base.ApplyConfiguration();
}
catch (Exception applyconfigerror) { error += "Error1:" + applyconfigerror.Message + "\r\n"; }
try
{
//this.AddEndpoints();
}
catch (Exception applyconfigerror) { error += "Error2:" + applyconfigerror.Message + "\r\n"; }
try
{
// this.AddDefaultBehaviors();
}
catch (Exception applyconfigerror) { error += "Error3:" + applyconfigerror.Message + "\r\n"; }
//if (error.Length > 0) HttpContext.Current.AddError(new Exception( error));
//this.AddEndpoints();
//this.AddDefaultBehaviors();
using (IEnumerator<ServiceEndpoint> enumerator = base.Description.Endpoints.GetEnumerator())
{
while (enumerator.MoveNext())
{
ServiceEndpoint current = enumerator.Current;
//current.Binding.Scheme
}
}
//}
//catch (Exception ex)
// {
//HttpContext.Current.AddError(ex);
// }
}
}
}