Protobuf net not serializing when using WCF - wcf

I am trying to use protobuf to serialize my WCF calls, but it seems the object is not getting serialized by the client. Some things to note:
I am using a shared DTO library.
I am using a ChannelFactory to
invoke the service (so the types are not losing their datamember
attributes).
I can serialize and deserialize the objects just using
normal protobuf.net code, so the types themselves seem to be ok
I am using version 2.0.0.480 of protobuf.net
I haven't posted the service code as the problem is with the outgoing message (message log posted below)
The client and service work fine if I don't use the protobuf endpoint behaviour.
My main method looks as follows
static void Main(string[] args)
{
Base.PrepareMetaDataForSerialization();
FactoryHelper.InitialiseFactoryHelper(new ServiceModule());
Member m = new Member();
m.FirstName = "Mike";
m.LastName = "Hanrahan";
m.UserId = Guid.NewGuid();
m.AccountStatus = MemberAccountStatus.Blocked;
m.EnteredBy = "qwertt";
ChannelFactory<IMembershipService> factory = new ChannelFactory<IMembershipService>("NetTcpBinding_MembershipService");
var client = factory.CreateChannel();
using (var ms = new MemoryStream())
{
Serializer.Serialize<Member>(ms, m);
Console.WriteLine(ms.Length.ToString());
ms.Position = 0;
var member2 = Serializer.Deserialize<Member>(ms);
Console.WriteLine(member2.EnteredBy);
Console.WriteLine(member2.FirstName);
}
var result = client.IsMemberAllowedToPurchase(m);
System.Console.Write(result.IsValid.ToString());
factory.Close();
var input = Console.ReadLine();
}
My client configuration looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
</configSections>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_Common" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288"
maxBufferSize="1000065536" maxConnections="10" maxReceivedMessageSize="1000000">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="Proto.Common.EndpointBehavior">
<protobuf />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="protobuf" type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=2.0.0.480, Culture=neutral, PublicKeyToken=257b51d87d2e4d67" />
</behaviorExtensions>
</extensions>
<client>
<endpoint address="net.tcp://mikes-pc:12002/MembershipService.svc"
behaviorConfiguration="Proto.Common.EndpointBehavior" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_Common" contract="PricesForMe.Core.Entities.ServiceInterfaces.IMembershipService"
name="NetTcpBinding_MembershipService">
<identity>
<userPrincipalName value="Mikes-PC\Mike" />
</identity>
</endpoint>
</client>
<diagnostics>
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true"
maxMessagesToLog="3000"
maxSizeOfMessageToLog="2000"/>
</diagnostics>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true">
<listeners>
<add name="traceListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="E:\Work\Logs\IMembershipServiceWcfTrace_Client.svclog" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messages"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="E:\Work\Logs\IMembershipServiceWcfTrace_Client_messages.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
After logging the client message, I get the following entry in the log
<MessageLogTraceRecord>
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://www.pricesforme.com/services/MembershipService/IsMemberAllowedToPurchase</a:Action>
<a:MessageID>urn:uuid:8b545576-c453-4be6-8d5c-9913e2cca4bf</a:MessageID>
<ActivityId CorrelationId="b4e9361f-1fbc-4b2d-b7ee-fb493847998a" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">6d712899-62fd-4547-9517-e9de452305c6</ActivityId>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink"></VsDebuggerCausalityData>
</s:Header>
<s:Body>
<IsMemberAllowedToPurchase xmlns="http://www.pricesforme.com/services/">
<proto></proto>
</IsMemberAllowedToPurchase>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
So as can been seen in the above log message the proto entry has no data in it. My member class looks as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using PricesForMe.Core.Entities.Common;
using PricesForMe.Core.Entities.Ordering;
namespace PricesForMe.Core.Entities.Members
{
/// <summary>
/// This entity represents a member or user of the site.
/// </summary>
[DataContract]
[Serializable]
public class Member: User
{
public Member()
:base()
{
EntityType = Entities.EntityType.Member;
}
[DataMember(Order = 20)]
public int Id { get; set; }
[DataMember(Order = 21)]
public string MemberName { get; set; }
[DataMember(Order = 22)]
public PaymentInfo DefaultPaymentMethod { get; set; }
[DataMember(Order = 23)]
public MemberAccountStatus AccountStatus { get; set; }
#region static
public static readonly string CacheCollectionKey = "MemberCollection";
private static readonly string CacheItemKeyPrefix = "Member:";
public static string GetCacheItemKey(int id)
{
return CacheItemKeyPrefix + id.ToString();
}
#endregion
}
}
The parent user class looks as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Diagnostics.Contracts;
namespace PricesForMe.Core.Entities.Common
{
/// <summary>
/// This class represents a user in the system. For example, a user could be a member or a merchant user.
/// </summary>
[DataContract]
[Serializable]
public class User: Base
{
public User()
:base()
{
EntityType = Entities.EntityType.User;
}
[DataMember(Order = 10)]
public Guid UserId { get; set; }
[DataMember(Order = 11, Name = "First Name")]
public string FirstName { get; set; }
[DataMember(Order = 12, Name = "Last Name")]
public string LastName { get; set; }
}
}
}
And the base class looks as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Diagnostics.Contracts;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using ProtoBuf.Meta;
namespace PricesForMe.Core.Entities
{
/// <summary>
/// This is the base class for all entities involved in the request/response pattern of our services
/// </summary>
/// <remarks>
/// The objects derived from this class are used to transfer data from our service classes to our UIs and back again and they should
/// not contain any logic.
/// </remarks>
[DataContract]
[Serializable]
public abstract class Base
{
public Base()
{
//Set some defaults for this
EnteredBy = System.Environment.UserName;
EnteredSource = System.Environment.MachineName;
}
/// <summary>
/// This is the record timestamp
/// </summary>
[DataMember(Order = 2)]
public DateTime RecordTimeStamp { get; set; }
/// <summary>
/// This is the name of the user who last edited the entity
/// </summary>
[DataMember(Order = 3)]
public string EnteredBy { get; set; }
/// <summary>
/// This is the source of the last edited entity
/// </summary>
[DataMember(Order = 4)]
public string EnteredSource { get; set; }
[DataMember(Order = 5)]
private PricesForMe.Core.Entities.Common.ValidationResult _validationResult = null;
/// <summary>
/// Data on the validity of the entity.
/// </summary>
public PricesForMe.Core.Entities.Common.ValidationResult ValidationData
{
get
{
_validationResult = Validate();
return _validationResult;
}
set
{
_validationResult = value;
}
}
/// <summary>
/// Flag denoting if the record is a new record or not.
/// </summary>
/// <remarks>
/// To flag an entity as an existing record call the "FlagAsExistingReport()" method.
/// </remarks>
public bool IsNewRecord
{
get
{
return _isNewRecord;
}
}
[DataMember(Order = 6)]
protected bool _isNewRecord = true;
/// <summary>
/// Flags the entity as a record that already exists in the database
/// </summary>
/// <remarks>
/// This is a method rather than a field to demonstrait that this should be called with caution (as opposed to inadvertantly setting a flag!)
/// <para>
/// Note that this method should only need to be called on object creation if the entity has a composite key. Otherwise the flag is
/// set when the id is being set. It should always be called on saving an entity.
/// </para>
/// </remarks>
public void FlagAsExistingRecord()
{
_isNewRecord = false;
}
public virtual PricesForMe.Core.Entities.Common.ValidationResult Validate()
{
if (_validationResult == null)
{
_validationResult = new PricesForMe.Core.Entities.Common.ValidationResult();
_validationResult.MemberValidations = new List<Common.ValidationResult>();
_validationResult.RulesViolated = new List<Common.ValidationRule>();
}
return _validationResult;
}
/// <summary>
/// This is the type of entity we are working with
/// </summary>
[DataMember(Order = 7)]
private EntityType _entityType = EntityType.Unknown;
public EntityType EntityType
{
get
{
return _entityType;
}
protected set
{
_entityType = value;
}
}
/// <summary>
/// Flag to say if the id generated for this class need to be int64 in size.
/// </summary>
[DataMember(Order = 9)]
public bool IdRequiresInt64 { get; protected set; }
/// <summary>
/// This method tells us if the database id has been assigned. Note that this does
/// not mean the entity has been saved, only if the id has been assigned (so the id could be greater than 0, but the
/// entity could still be a NewRecord
/// </summary>
/// <returns></returns>
[DataMember(Order = 8)]
public bool HasDbIdBeenAssigned { get; protected set; }
private Guid _validationId = Guid.NewGuid();
public Guid EntityValidationId
{
get
{
return _validationId;
}
}
/// <summary>
/// Converts an object into another type of object based on the mapper class provided.
/// </summary>
/// <remarks>
/// This method allows us to easily convert between objects without concerning ourselves with the mapping implementation. This
/// allows us to use various mapping frameworks (e.g. Automapper, ValueInjector) or create our own custom mapping.
/// </remarks>
/// <typeparam name="TDestination">The type we want to convert to</typeparam>
/// <typeparam name="KMapper">The mapping type</typeparam>
/// <returns>The new type</returns>
public TDestination ConvertTo<TDestination, TSource, KMapper>()
where KMapper : IEntityMapper<TDestination, TSource>
where TSource : class
{
return Base.ConvertToItem<TDestination, TSource, KMapper>(this as TSource);
}
/// <summary>
/// Returns all known child types
/// </summary>
public IEnumerable<Type> GetAllTypes()
{
Assembly current = Assembly.GetCallingAssembly();
List<Type> derivedTypes = new List<Type>();
var allTypes = current.GetTypes();
foreach (var t in allTypes)
{
if (t.IsAssignableFrom(typeof(Base)))
{
derivedTypes.Add(t);
}
}
return derivedTypes;
}
#region Static Methods
/// <summary>
/// Converts a list of one type to a list of another type
/// </summary>
/// <typeparam name="TDestination">The type we want to convert to</typeparam>
/// <typeparam name="TSource">The source type</typeparam>
/// <typeparam name="KMapper">The mapper class</typeparam>
/// <param name="source">The source list of items.</param>
/// <returns></returns>
public static List<TDestination> ConvertToList<TDestination, TSource, KMapper>(IEnumerable<TSource> source)
where KMapper : IEntityMapper<TDestination, TSource>
where TSource : class
{
List<TDestination> result = new List<TDestination>();
KMapper mapper = Activator.CreateInstance<KMapper>();
foreach (var item in source)
{
result.Add(mapper.Convert(item));
}
return result;
}
public static TDestination ConvertToItem<TDestination, TSource, KMapper>(TSource source)
where KMapper : IEntityMapper<TDestination, TSource>
where TSource : class
{
//Return default (i.e. null for ref objects) if the source is null.
if (source == null) { return default(TDestination); }
KMapper mapper = Activator.CreateInstance<KMapper>();
return mapper.Convert(source);
}
private static object _metaLock = new object();
private static bool _metaDataPrepared = false;
/// <summary>
/// Creates protobuf type models from the entities in this assembly
/// </summary>
public static void PrepareMetaDataForSerialization()
{
lock (_metaLock)
{
if (_metaDataPrepared) { return; }
Assembly current = Assembly.GetExecutingAssembly();
var allTypes = current.GetTypes();
foreach (var t in allTypes)
{
checkType(t);
}
}
}
private static void checkType(Type type)
{
Assembly current = Assembly.GetExecutingAssembly();
var allTypes = current.GetTypes();
int key = 1000;
foreach (var t in allTypes)
{
if (t.IsSubclassOf(type) && t.BaseType == type)
{
RuntimeTypeModel.Default[type].AddSubType(key, t);
key++;
}
}
}
#endregion
}
}
The PrepareMetaDataForSerialization method on base configures the RuntimeModel for protobuf.net, but I mentioned earlier, the serialization and deserialization works fine outside of the WCF, so I think the DTOs are ok. Any ideas around what could be causing the problem are greatly appreciated.

k; the element name looks correct (proto, matching XmlProtoSerializer.PROTO_ELEMENT), so protobuf-net definitely tried to do something. It also doesn't include #nil to represent null, so it knows there was data. Beyond that, it serializes the object to a MemoryStream and writes it as base-64 (identical to how byte[] etc is represented, which allows WCF to hoist the data silently and automatically if things like MTOM are enabled). So the question becomes "why would my type serialize to nothing?"
The use of DataContract/DataMember is fine, and matches my existing WCF integration tests.
I wonder how much of this is due to the inheritance (only one of the members shown relates to the concrete type, and I would hazard a guess that Blocked is 0, which has particular handling).
I cannot, however, emphasise enough how unsafe your current inheritance handling is; the numbers matter, and reflection makes no guarantees re order. I would strongly suggest you revisit this and make inheritance numbering more predicatable.
Very minor observation, but there is no need to store EntityType - it is entirey redundant and could be handled via polymorphism with no need for storage.
Also, there's an important bug that _metaDataPrepared is never set to true.
However! Ultimately I cannot reproduce this; I have used your code (or most of it) to generate an integration test, and - it passes; meaning: using WCF, NetTcpBinding, your classes (including your inheritance fixup code), and the protobuf packing, it just works. The data that goes over the wire is the data we expect.
Happy to try to help further, but I need to be able to repro it... and right now, I can't.
The first thing I'd do is add the missing _metaDataPrepared = true; to see if that helps.

I thought you need to use ProtoContract instead of DataContract per this SO? Also, make sure you set "Reuse types in referenced assemblies" when configuring service reference. And according to this SO they support data contracts but you have to have the order set [DataMember(Order = 0)] (at least that's what worked for me).

Related

WCF Serialization Long Namespace being repeated instead of prefix

We are using contract first approach for WCF services in our project, XSD(s) are converted to entities using WSCF Blue and default serialization is used. The default serializer serializes the packets in following way
<category xmlns="http://myportal.com/schema/category/v1.0">
<processingDate xmlns="http://myportal.com/schema/common/elements/v1.0">0001-01-01T00:00:00</processingDate>
<key **xmlns="http://myportal.com/schema/common/elements/v1.0"**>f9a8d542-72c8-4465-8d6b-aaeb94a72394</key>
<code>C511746379</code>
<name>category308277327</name>
<description>One Tow</description>
</category>
<region xmlns="http://myportal.com/schema/shared/region/v1.0">
<key **xmlns="http://myportal.com/schema/common/elements/v1.0"**>3</key>
<code>N35</code>
<name>North</name>
<panelCode>N98</panelCode>
</region>
<category xmlns="http://myportal.com/schema/category/v1.0">
<processingDate xmlns="http://myportal.com/schema/common/elements/v1.0">0001-01-01T00:00:00</processingDate>
<key **xmlns="http://myportal.com/schema/common/elements/v1.0"**>00121be8-968f-4dbf-9d5c-d7b81e127a36</key>
<name>Aplha</name>
<code>76542</code>
<createdDate **xmlns="http://myportal.com/schema/common/elements/v1.0"**>2014-03-26T16:36:52.794876</createdDate>
<stream>Online</stream>
</category>
The problem is highlighted in bold, why the default serializer puts the whole namespace there, why can't it declare it at the top and use prefix. The whole namespace inflates size of packet.
The category entity looks like following
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18058")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://myportal.com/schema/category/v1.0", TypeName="category")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://myportal.com/schema/category/v1.0", IsNullable=false, ElementName="category")]
public partial class CategoryType : BaseType
{
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Namespace = "http://myportal.com/schema/common/elements/v1.0", Order = 0, ElementName = "key")]
public string Key
{
get
{
return this.keyField;
}
set
{
this.keyField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order = 1, ElementName = "code")]
public string Code
{
get
{
return this.codeField;
}
set
{
this.codeField = value;
}
}
How do I enforce the XmlElementAttribute to use prefix instead of complete namespace?
Thanks,
Avi
Finally I was able to resolve this one, all of my DTO (XSD) were an extension of same parent class by the name BaseType. I had to add a public field with XmlNamespaceDeclarations decoration, this field is consulted right before serialization.
#region Public Fields
/// <summary>
/// This is considered at the time of serialization for adding namespace prefixes,
/// The namespaces are built in the default constructor, it queries a Constant that in chance fetches the namespace(s) from configuration file
/// </summary>
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Prefixes;
#endregion
#region Public Constructors
/// <summary>
/// Builds namespace prefixes for serialization
/// </summary>
public BaseType()
{
Prefixes = new XmlSerializerNamespaces();
int index = 1;
Constants.NAMESPACES
.ForEach(tempNamespace =>
Prefixes.Add(Constants.PREFIX_LETTER + index++, tempNamespace)
);
}
#endregion
I hope this one helps somebody.
Cheers,
Avi

Unable to deserialize CSLA request in soap message sent by WCF (C#)

I am learning WCF so my apologies if my terminology is off here and there.
I am trying to deserialize a soap message that was sent with WCF and captured by Fiddler and I keep getting this error message: "The data at the root level is invalid. Line 1, position 1."
The WCF service is setup with the following code:
// Set up WcfProxy DataPortal service url
Csla.DataPortalClient.WcfProxy.DefaultUrl = appRootUrl + "WcfSilverlightPortal.svc";
// Set up WcfProxy Binding
System.ServiceModel.BasicHttpBinding largeBinding = new System.ServiceModel.BasicHttpBinding();
largeBinding.MaxReceivedMessageSize = 20000000;
largeBinding.MaxBufferSize = 20000000;
largeBinding.OpenTimeout = new TimeSpan(0, 10, 0);
largeBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
largeBinding.CloseTimeout = new TimeSpan(0, 10, 0);
largeBinding.SendTimeout = new TimeSpan(0, 10, 0);
largeBinding.Name = "BasicHttpBinding_IWcfPortal";
Csla.DataPortalClient.WcfProxy.DefaultBinding = largeBinding;
The soap message captured in Fiddler is as follows:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<Fetch xmlns="http://ws.lhotka.net/WcfDataPortal">
<request xmlns:d4p1="http://schemas.datacontract.org/2004/07/Csla.Server.Hosts.Silverlight" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:ClientContext>QBhBcnJheU9mU2VyaWFsaXphdGlvbkluZm8IQWh0dHA6Ly9zY2hlbWFzLmRhdGFjb250cmFjdC5vcmcvMjAwNC8wNy9Dc2xhLlNlcmlhbGl6YXRpb24uTW9iaWxlCQFpKWh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAUALUmVmZXJlbmNlSWSDQAhUeXBlTmFtZZhlQ3NsYS5Db3JlLkNvbnRleHREaWN0aW9uYXJ5LCBDc2xhLCBWZXJzaW9uPTQuMy4xMy4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTkzYmU1ZmRjMDkzZTRjMzABQAZWYWx1ZXMJAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAQEB</d4p1:ClientContext>
<d4p1:ClientCulture>en-US</d4p1:ClientCulture>
<d4p1:ClientUICulture>en-US</d4p1:ClientUICulture>
<d4p1:CriteriaData i:nil="true" />
<d4p1:GlobalContext>QBhBcnJheU9mU2VyaWFsaXphdGlvbkluZm8IQWh0dHA6Ly9zY2hlbWFzLmRhdGFjb250cmFjdC5vcmcvMjAwNC8wNy9Dc2xhLlNlcmlhbGl6YXRpb24uTW9iaWxlCQFpKWh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAUALUmVmZXJlbmNlSWSDQAhUeXBlTmFtZZhlQ3NsYS5Db3JlLkNvbnRleHREaWN0aW9uYXJ5LCBDc2xhLCBWZXJzaW9uPTQuMy4xMy4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTkzYmU1ZmRjMDkzZTRjMzABQAZWYWx1ZXMJAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAQEB</d4p1:GlobalContext>
<d4p1:Principal>QBhBcnJheU9mU2VyaWFsaXphdGlvbkluZm8IQWh0dHA6Ly9zY2hlbWFzLmRhdGFjb250cmFjdC5vcmcvMjAwNC8wNy9Dc2xhLlNlcmlhbGl6YXRpb24uTW9iaWxlCQFpKWh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAUALUmVmZXJlbmNlSWSDQAhUeXBlTmFtZZhwQ3NsYS5TZWN1cml0eS5VbmF1dGhlbnRpY2F0ZWRQcmluY2lwYWwsIENzbGEsIFZlcnNpb249NC4zLjEzLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49OTNiZTVmZGMwOTNlNGMzMAFABlZhbHVlcwkBYTlodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tLzIwMDMvMTAvU2VyaWFsaXphdGlvbi9BcnJheXNeM0tleVZhbHVlT2ZzdHJpbmdTZXJpYWxpemF0aW9uSW5mby5GaWVsZERhdGFPem9adkxybV4DS2V5mRZDc2xhUHJpbmNpcGFsLklkZW50aXR5XgVWYWx1ZUAMRW51bVR5cGVOYW1lLgNuaWyGAUAHSXNEaXJ0eYVABVZhbHVlLgR0eXBlmA5iOmJhc2U2NEJpbmFyeQkBYiBodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYaCNCUAYQXJyYXlPZlNlcmlhbGl6YXRpb25JbmZvCEFodHRwOi8vc2NoZW1hcy5kYXRhY29udHJhY3Qub3JnLzIwMDQvMDcvQ3NsYS5TZXJpYWxpemF0aW9uLk1vYmlsZQkBaSlodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZUARU2VyaWFsaXphdGlvbkluZm9ACENoaWxkcmVuCQFhOWh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uL0FycmF5c14zS2V5VmFsdWVPZnN0cmluZ1NlcmlhbGl6YXRpb25JbmZvLkNoaWxkRGF0YU96b1p2THJtXgNLZXmZDV9maWVsZE1hbmFnZXJeBVZhbHVlQAdJc0RpcnR5hUALUmVmZXJlbmNlSWSJAgEBAUALUmVmZXJlbmNlSWSDQAhUeXBlTmFtZZhvQ3NsYS5TZWN1cml0eS5VbmF1dGhlbnRpY2F0ZWRJZGVudGl0eSwgQ3NsYSwgVmVyc2lvbj00LjMuMTMuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05M2JlNWZkYzA5M2U0YzMwAUAGVmFsdWVzCQFhOWh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uL0FycmF5cwEBQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzXjNLZXlWYWx1ZU9mc3RyaW5nU2VyaWFsaXphdGlvbkluZm8uQ2hpbGREYXRhT3pvWnZMcm1eA0tleZkFUm9sZXNeBVZhbHVlQAdJc0RpcnR5hUALUmVmZXJlbmNlSWSJAwEBAUALUmVmZXJlbmNlSWSJAkAIVHlwZU5hbWWYcUNzbGEuQ29yZS5GaWVsZE1hbmFnZXIuRmllbGREYXRhTWFuYWdlciwgQ3NsYSwgVmVyc2lvbj00LjMuMTMuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05M2JlNWZkYzA5M2U0YzMwAUAGVmFsdWVzCQFhOWh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uL0FycmF5c14zS2V5VmFsdWVPZnN0cmluZ1NlcmlhbGl6YXRpb25JbmZvLkZpZWxkRGF0YU96b1p2THJtXgNLZXmZE19idXNpbmVzc09iamVjdFR5cGVeBVZhbHVlQAxFbnVtVHlwZU5hbWUuA25pbIYBQAdJc0RpcnR5hUAFVmFsdWUuBHR5cGWYCGI6c3RyaW5nCQFiIGh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hmG9Dc2xhLlNlY3VyaXR5LlVuYXV0aGVudGljYXRlZElkZW50aXR5LCBDc2xhLCBWZXJzaW9uPTQuMy4xMy4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTkzYmU1ZmRjMDkzZTRjMzABAQFeM0tleVZhbHVlT2ZzdHJpbmdTZXJpYWxpemF0aW9uSW5mby5GaWVsZERhdGFPem9adkxybV4DS2V5mRJBdXRoZW50aWNhdGlvblR5cGVeBVZhbHVlQAxFbnVtVHlwZU5hbWUuA25pbIYBQAdJc0RpcnR5hUAFVmFsdWUuBHR5cGWYCGI6c3RyaW5nCQFiIGh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hAQEBXjNLZXlWYWx1ZU9mc3RyaW5nU2VyaWFsaXphdGlvbkluZm8uRmllbGREYXRhT3pvWnZMcm1eA0tleZkPSXNBdXRoZW50aWNhdGVkXgVWYWx1ZUAMRW51bVR5cGVOYW1lLgNuaWyGAUAHSXNEaXJ0eYVABVZhbHVlLgR0eXBlmAliOmJvb2xlYW4JAWIgaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWGFAQFeM0tleVZhbHVlT2ZzdHJpbmdTZXJpYWxpemF0aW9uSW5mby5GaWVsZERhdGFPem9adkxybV4DS2V5mQROYW1lXgVWYWx1ZUAMRW51bVR5cGVOYW1lLgNuaWyGAUAHSXNEaXJ0eYVABVZhbHVlLgR0eXBlmAhiOnN0cmluZwkBYiBodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYQEBAQEBQBFTZXJpYWxpemF0aW9uSW5mb0AIQ2hpbGRyZW4JAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzAUALUmVmZXJlbmNlSWSJA0AIVHlwZU5hbWWYvkNzbGEuQ29yZS5Nb2JpbGVMaXN0YDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTUuMC41LjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49N2NlYzg1ZDdiZWE3Nzk4ZV1dLCBDc2xhLCBWZXJzaW9uPTQuMy4xMy4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTkzYmU1ZmRjMDkzZTRjMzABQAZWYWx1ZXMJAWE5aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS8yMDAzLzEwL1NlcmlhbGl6YXRpb24vQXJyYXlzXjNLZXlWYWx1ZU9mc3RyaW5nU2VyaWFsaXphdGlvbkluZm8uRmllbGREYXRhT3pvWnZMcm1eA0tleZkFJGxpc3ReBVZhbHVlQAxFbnVtVHlwZU5hbWUuA25pbIYBQAdJc0RpcnR5hUAFVmFsdWUuBHR5cGWYDmI6YmFzZTY0QmluYXJ5CQFiIGh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hnoc8QXJyYXlPZnN0cmluZyB4bWxuczppPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uL0FycmF5cyIgLz4BAQEBAZ8BAQEBAQEB</d4p1:Principal>
<d4p1:TypeName>Competition.Domain.Entities.SchoolSummaryList, Competition.Domain.Csla, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</d4p1:TypeName>
</request>
</Fetch>
</s:Body>
</s:Envelope>
I am trying to deserialize this to an object of type Csla.Server.Hosts.Silverlight.CriteriaRequest
The code for this class looks like this:
//-----------------------------------------------------------------------
// <copyright file="CriteriaRequest.cs" company="Marimer LLC">
// Copyright (c) Marimer LLC. All rights reserved.
// Website: http://www.lhotka.net/cslanet/
// </copyright>
// <summary>Message sent to the Silverlight</summary>
//-----------------------------------------------------------------------
using System;
using System.Runtime.Serialization;
using System.Security.Principal;
using Csla.Core;
namespace Csla.Server.Hosts.Silverlight
{
/// <summary>
/// Message sent to the Silverlight
/// WCF data portal.
/// </summary>
[DataContract]
public class CriteriaRequest
{
/// <summary>
/// Assembly qualified name of the
/// business object type to create.
/// </summary>
[DataMember]
public string TypeName { get; set; }
/// <summary>
/// Serialized data for the criteria object.
/// </summary>
[DataMember]
public byte[] CriteriaData { get; set; }
/// <summary>
/// Serialized data for the principal object.
/// </summary>
[DataMember]
public byte[] Principal { get; set; }
/// <summary>
/// Serialized data for the global context object.
/// </summary>
[DataMember]
public byte[] GlobalContext { get; set; }
/// <summary>
/// Serialized data for the client context object.
/// </summary>
[DataMember]
public byte[] ClientContext { get; set; }
/// <summary>
/// Serialized client culture.
/// </summary>
/// <value>The client culture.</value>
[DataMember]
public string ClientCulture { get; set; }
/// <summary>
/// Serialized client UI culture.
/// </summary>
/// <value>The client UI culture.</value>
[DataMember]
public string ClientUICulture { get; set; }
}
}
I have tried grabbing each of the three xml elements 'Body', 'Fetch' and 'request' from the soap message and storing them as an XElement variable named 'element' and running the code below:
DataContractSerializer dcs = new DataContractSerializer(typeof(CriteriaRequest));
MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(element.Value));
ms.Seek(0, SeekOrigin.Begin);
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(ms, Encoding.UTF8, new XmlDictionaryReaderQuotas(), null);
CriteriaRequest fr = (CriteriaRequest)dcs.ReadObject(reader);
In every case it has failed to deserialize the object and I am out of ideas.
I think I have provided all the relavent information but if anything else would help please let me know and I will provide it.
Thanks.
You can't use the more simplistic serializers to directly serialize a CSLA .NET object graph. Serializers such as XmlSerializer, JsonSerializer, and the various DataContractSerializer variations aren't sophisticated enough to completely clone and object graph, and so they aren't supported by CSLA.
Only the BinaryFormatter, NetDataContractSerializer, and MobileFormatter are supported.
Odds are you are trying to expose your business objects directly as a service interface. That's a poor architectural choice, and one I'd never choose to support or encourage anyone to do. You might find the XML Services FAQ page useful.

WCF: Getting (400) Bad Request when sending small amount of data

I am getting "The remote server returned an unexpected response: (400) Bad Request" when I try to send more than 100 audit entries. I am using Fiddler to help debug and do see the request being sent to the server. The client and server both use the same interface.
[ServiceContract]
public interface ISyncDataContract
{
#region Audit Log
/// <summary>
/// Creates a collection of new audit entries items in the database.
/// </summary>
/// <param name="items">The audit entry items to be created.</param>
/// <returns><c>True</c> if created successfully; otherwise, <c>false</c>.</returns>
[OperationContract]
[WebInvoke(UriTemplate = "AuditEntries", Method = "PUT")]
bool CreateAuditEntryItems(AuditEntryItemCollection items);
/// <summary>
/// Gets all the audit entry items available.
/// </summary>
/// <returns>An <see cref="AuditEntryItemCollection"/> object containing all the
/// available audit entry items.</returns>
[OperationContract]
[WebGet(UriTemplate = "AuditEntries")]
Message GetAuditEntryItems();
#endregion
}
AuditEntryItem.cs
[DataContract]
public class AuditEntryItem
{
#region Constructor/Deconstructor
/// <summary>
/// Initializes a new instance of the <see cref="AuditEntryItem"/> class.
/// </summary>
public AuditEntryItem()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AuditEntryItem"/> class.
/// </summary>
/// <param name="auditEntry">The audit entry.</param>
public AuditEntryItem(AuditEntry auditEntry)
{
if (auditEntry == null)
{
throw new ArgumentNullException("auditEntry");
}
this.Audit_Type = auditEntry.Audit_type;
this.ComputerName = Environment.MachineName;
this.Message = auditEntry.Message;
this.Sequence_Number = auditEntry.Sequence_number;
this.Session_ID = auditEntry.Session_ID;
this.SyncDate = DateTime.Now;
this.Time_Stamp = auditEntry.Time_stamp;
this.User_ID = auditEntry.User_ID;
}
#endregion Constructor/Deconstructor
#region Properties
/// <summary>
/// Gets or sets the session ID.
/// </summary>
/// <value>
/// The session ID.
/// </value>
[DataMember]
[XmlElement(ElementName = #"Session_ID")]
public string Session_ID { get; set; }
/// <summary>
/// Gets or sets the user ID.
/// </summary>
/// <value>
/// The user ID.
/// </value>
[DataMember]
[XmlElement(ElementName = #"User_ID")]
public string User_ID { get; set; }
/// <summary>
/// Gets or sets the time stamp.
/// </summary>
/// <value>
/// The time stamp.
/// </value>
[DataMember]
[XmlElement(ElementName = #"Time_Stamp")]
public string Time_Stamp { get; set; }
/// <summary>
/// Gets or sets the sequence number.
/// </summary>
/// <value>
/// The sequence number.
/// </value>
[DataMember]
[XmlElement(ElementName = #"Sequence_number")]
public int Sequence_Number { get; set; }
/// <summary>
/// Gets or sets the message.
/// </summary>
/// <value>
/// The message.
/// </value>
[DataMember]
[XmlElement(ElementName = #"Message")]
public string Message { get; set; }
/// <summary>
/// Gets or sets the type of the audit.
/// </summary>
/// <value>
/// The type of the audit.
/// </value>
[DataMember]
[XmlElement(ElementName = #"Audit_type")]
public string Audit_Type { get; set; }
/// <summary>
/// Gets or sets the name of the computer.
/// </summary>
/// <value>
/// The name of the computer.
/// </value>
[DataMember]
[XmlElement(ElementName = #"ComputerName")]
public string ComputerName { get; set; }
/// <summary>
/// Gets or sets the sync date.
/// </summary>
/// <value>
/// The sync date.
/// </value>
[DataMember]
[XmlElement(ElementName = #"SyncDate")]
public DateTime? SyncDate { get; set; }
/// <summary>
/// Gets the time stamp value in a date time format.
/// </summary>
[XmlIgnore]
public DateTime DisplayTimeStamp
{
get { return this.TimeStampDateTime(); }
}
#endregion Properties
#region Overrides
public override bool Equals(object obj)
{
return obj is AuditEntryItem ? this.Equals((AuditEntryItem)obj) : false;
}
public bool Equals(AuditEntryItem other)
{
if (ReferenceEquals(this, other))
{
return true;
}
return string.Equals(this.Audit_Type, other.Audit_Type) &&
string.Equals(this.ComputerName, other.ComputerName) &&
string.Equals(this.Message, other.Message) &&
this.Sequence_Number == other.Sequence_Number &&
string.Equals(this.Session_ID, other.Session_ID) &&
this.SyncDate == other.SyncDate &&
string.Equals(this.Time_Stamp, other.Time_Stamp) &&
string.Equals(this.User_ID, other.User_ID);
}
public override int GetHashCode()
{
unchecked
{
var result = (this.Audit_Type != null ? this.Audit_Type.GetHashCode() : 0);
result = (result * 397) ^ (this.ComputerName != null ? this.ComputerName.GetHashCode() : 0);
result = (result * 397) ^ (this.Message != null ? this.Message.GetHashCode() : 0);
result = (result * 397) ^ this.Sequence_Number.GetHashCode();
result = (result * 397) ^ (this.Session_ID != null ? this.Session_ID.GetHashCode() : 0);
result = (result * 397) ^ (this.SyncDate != null ? this.SyncDate.GetHashCode() : 0);
result = (result * 397) ^ (this.Time_Stamp != null ? this.Time_Stamp.GetHashCode() : 0);
result = (result * 397) ^ (this.User_ID != null ? this.User_ID.GetHashCode() : 0);
return result;
}
}
#endregion Overrides
/// <summary>
/// Converts the Java time stamp value into a readable format.
/// </summary>
/// <returns>A readable date time format.</returns>
private DateTime TimeStampDateTime()
{
if (this.Time_Stamp.IsNullOrEmpty())
{
return new DateTime(1970, 01, 01);
}
long value;
if (!long.TryParse(this.Time_Stamp, out value))
{
return new DateTime(1970, 01, 01);
}
value = value / 1000;
return new DateTime(1970, 01, 01).AddSeconds(value);
}
}
AuditEntryItemCollection.cs
[DataContract]
[XmlRoot(ElementName = "AuditLog")]
public class AuditEntryItemCollection
{
#region Declarations
#endregion Declarations
#region Constructor/Deconstructor
/// <summary>
/// Initializes a new instance of the <see cref="AuditEntryItemCollection"/> class.
/// </summary>
public AuditEntryItemCollection()
{
this.AuditEntryItems = new List<AuditEntryItem>();
}
#endregion Constructor/Deconstructor
#region Properties
/// <summary>
/// Gets or sets the collection of <see cref="AuditEntryItem"/>
/// objects.
/// </summary>
/// <value>
/// The collection of <see cref="AuditEntryItem"/> objects.
/// </value>
[XmlElement(ElementName = #"AuditEntry")]
[DataMember]
public List<AuditEntryItem> AuditEntryItems { get; set; }
#endregion Properties
}
App.config
<?xml version="1.0" encoding="utf-8" ?>
<behaviors>
<endpointBehaviors>
<behavior name="restXmlBehavior">
<webHttp helpEnabled="true" defaultOutgoingResponseFormat="Xml" />
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
<behavior name="rssAtomBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="metadataBehavior" >
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="StreamedHttp"
maxReceivedMessageSize="2147483647"
transferMode="Streamed" >
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="6000000" />
</binding>
</webHttpBinding>
</bindings>
UPDATE:
The error I am now getting is "The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element." As far as I can tell, I have already done this. Is there some other setting I need to set?
You could make sure the httpRuntime is also configured for large requests:
(An example grabbed from a service I use to upload files:)
<httpRuntime executionTimeout="3600" maxRequestLength="50000000" maxQueryStringLength="2097151" requestValidationMode="2.0" />
And also perhaps look at the buffer pool sizes on the binding (again, these values are just examples):
<binding name="WHB" maxReceivedMessageSize="50000000" maxBufferPoolSize="50000000" crossDomainScriptAccessEnabled="true">
<readerQuotas maxArrayLength="50000000" maxStringContentLength="50000000" />

Can I associate a connection string with an endpoint in WCF?

We have a WCF REST service that connects to a database. In fact, we have several instances of the database, all with the same schema.
We would like to set up one endpoint for each database instance and associate a connection string with the endpoint. The service would read the connection string and connect to the appropriate SQL Server instance.
I'm sure this is possible; is it a good idea? How do I set it up? Is there documentation on MSDN?
Edit: I found this question, where the answer suggests adding connection information on the client in a header. I don't want to do that—for security reasons, and because I do want to have a distinct uri for each database.
This was a bit harder than I thought. WCF has so many extensibility points its hard to pick the right one. Please answer or comment if you think there's a better way, or anything wrong with this.
I've settled on using a custom class that implements IEndpointBehavior and IDispatchMessageInspector. I have a class derived from BehaviorExtensionElement that lets me associate the behavior with an endpoint in configuration. This blog post describes hot do do that.
My DatabaseConnectionContext class looks like this:
/// <summary>
/// An endpoint behavior that associates a database connection string name with the endpoint and adds it to the
/// properties of incoming messages.
/// </summary>
public class DatabaseConnectionContext : IEndpointBehavior, IDispatchMessageInspector
{
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseConnectionContext"/> class with the provided connection string name.
/// </summary>
/// <param name="connectionStringName">The name of the connection string to associate with the endpoint.</param>
public DatabaseConnectionContext(string connectionStringName)
{
this.ConnectionStringName = connectionStringName;
}
/// <summary>
/// Gets the name of the connection string to associate with the endpoint.
/// </summary>
public string ConnectionStringName { get; private set; }
/// <inheritdoc />
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
/// <inheritdoc />
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
}
/// <inheritdoc />
public void Validate(ServiceEndpoint endpoint)
{
}
/// <inheritdoc />
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
request.Properties["connectionStringName"] = this.ConnectionStringName;
return null;
}
/// <inheritdoc />
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
}
In my service class I have this method:
/// <summary>
/// Returns the connection string to use for this service call.
/// </summary>
/// <returns>A SQL Server database connection string.</returns>
private string GetConnectionString()
{
string connectionStringName = (string)OperationContext.Current.IncomingMessageProperties["connectionStringName"];
return ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
}
My BehaviorExtensionElement class looks like this:
/// <summary>
/// Associates a <see cref="DatabaseConnectionContext"/> with an endpoint in configuration.
/// </summary>
public class DatabaseConnectionContextBehaviorExtension : BehaviorExtensionElement
{
/// <summary>
/// The name of the <see cref="ConnectionStringName"/> property when it appears in a configuration file.
/// </summary>
private const string ConnectionStringNamePropertyName = "connectionStringName";
/// <summary>
/// Gets or sets the name of the configuration string to associate with the endpoint.
/// </summary>
[ConfigurationProperty(ConnectionStringNamePropertyName)]
public string ConnectionStringName
{
get
{
return (string)this[ConnectionStringNamePropertyName];
}
set
{
this[ConnectionStringNamePropertyName] = value;
}
}
/// <inheritdoc />
public override Type BehaviorType
{
get { return typeof(DatabaseConnectionContext); }
}
/// <inheritdoc />
protected override object CreateBehavior()
{
return new DatabaseConnectionContext(this.ConnectionStringName);
}
}
My web.config contains something like this:
<behaviors>
<endpointBehaviors>
<behavior name="DevRestEndpointConfiguration">
<webHttp helpEnabled="false" />
<connectionStringInterceptor connectionStringName="myDevConnectionStringName" />
</behavior>
<behavior name="ProductionRestEndpointConfiguration">
<webHttp helpEnabled="false" />
<connectionStringInterceptor connectionStringName="myProductionConnectionStringName" />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="connectionStringInterceptor" type="DatabaseConnectionContextBehaviorExtension, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
Each <endpoint /> element in the <services /> section has its behaviorConfiguration set to the name of an appropriate element from the <endpointBehaviors /> section.
why don't you add a new parameters specifying what is the database the call will connect?
for example:
you can add a db parameters that will get a number and from there you will connect
you can add such parameter on the authentication method
as the example for the first item:
public ProductItem GetProduct(int productId, int db = 1)
{
ProductItem product = new ProductItem();
string connectionString = getConnectionStringForDb(db);
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand("SELECT name, price FROM Products WHERE productId = #product;", connection);
command.Parameters.AddWithValue("#product", productId);
try
{
connection.Open();
SqlDataReader reader = command.ExecuteReader();
reader.Read();
product = new product({
Name = reader[0],
Price = reader[1]
});
reader.Close();
}
catch (Exception ex)
{
// Log exception
}
}
return product;
}
taken from MSDN
private string getConnectionStringForDb(int type)
{
System.Configuration.ConnectionStringSettings connString;
System.Configuration.Configuration rootWebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/MyWebSiteRoot");
if (rootWebConfig.ConnectionStrings.ConnectionStrings.Count > 0) {
connString = rootWebConfig.ConnectionStrings.ConnectionStrings["DBConnectionString_" + type];
if (connString == null) {
// LOG ERROR
}
}
return connString.ConnectionString;
}
and just add your connection strings in your web.config and name then like:
DBConnectionString_1, DBConnectionString_2, DBConnectionString_3
or anything that makes sense to you.
<connectionStrings>
<add
name="DBConnectionString_1"
connectionString="Data Source=serverName;Initial
Catalog=Northwind;Persist Security Info=True;User
ID=userName;Password=password"
providerName="System.Data.SqlClient"
/>
<add
name="DBConnectionString_2"
connectionString="Data Source=serverName;Initial
Catalog=Northwind;Persist Security Info=True;User
ID=userName;Password=password"
providerName="System.Data.SqlClient"
/>
<add
name="DBConnectionString_3"
connectionString="Data Source=serverName;Initial
Catalog=Northwind;Persist Security Info=True;User
ID=userName;Password=password"
providerName="System.Data.SqlClient"
/>
</connectionStrings>
With this in your web.config:
<configuration>
<appSettings>
<add key="Foo.svc" value="tagvalue1"/>
</appSettings>
...
You could retrieve the value at runtime this way:
private static string GetConfigValue()
{
ServiceEndpointCollection ec = OperationContext.Current
.Host.Description.Endpoints;
if (ec!=null)
{
var segments = ec[0].Address.ToString().Split('/');
var s = segments[segments.Length-1]; // "Foo.svc"
return ConfigurationManager.AppSettings[s]; // "tagvalue1"
}
return null;
}

WCF Message Contract namespace issue

I have following MessageContract
[MessageContract(IsWrapped = true, WrapperName = AuthorizationXmlElementNames.ElementNames.GetUserRightsResponse,
WrapperNamespace = AuthorizationXmlElementNames.Namespace)]
public class GetUserRightsResponseMessage
{
#region Properties
/// <summary>
/// Gets or sets GetUserRightsResponse.
/// </summary>
[MessageBodyMember(Namespace = AuthorizationXmlElementNames.Namespace)]
public GetUserRightsResponse GetUserRightsResponse { get; set; }
/// <summary>
/// Gets or sets ResponseHeader.
/// </summary>
[MessageHeader(
Name = XmlCoreElementNames.ElementNames.ResponseHeader,
Namespace = XmlCoreElementNames.Namespace,
ProtectionLevel = ProtectionLevel.None)]
public ResponseHeader ResponseHeader { get; set; }
#endregion
}
GetUserRightsResponse class looks like this
[XmlRoot(ElementName = AuthorizationXmlElementNames.ElementNames.GetUserRightsResponse,
Namespace = AuthorizationXmlElementNames.Namespace, IsNullable = false)]
[Serializable]
//[MessageContract(WrapperName = AuthorizationXmlElementNames.ElementNames.GetUserRightsResponse, WrapperNamespace = AuthorizationXmlElementNames.Namespace)]
public class GetUserRightsResponse
{
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="GetUserRightsResponse"/> class.
/// Initialize a new instance of the <see cref="GetUserRightsResponse"/> class.
/// </summary>
public GetUserRightsResponse()
{
this.UserServiceAccesses = new UserServiceAccesses();
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the user rights for the current user.
/// </summary>
//[MessageBodyMember(Namespace = AuthorizationXmlElementNames.Namespace)]
public UserServiceAccesses UserServiceAccesses { get; set; }
#endregion
}
XmlCoreElementNames.Namespace is the constant string for namespace and it's value is urn:MyNamespace:Authorization
I return an instance of the GetUserRightsResponseMessage from my operation contract. But I always get wrong namespace for the GetUserRightsResponse instance contained in the returned object. The returned XML part looks like the following.
<s:Body u:Id="_0">
<GetUserRightsResponse xmlns="urn:MyNamespace:Authorization">
<GetUserRightsResponse i:nil="true"
xmlns:a="http://schemas.datacontract.org/2004/07/MyMessageContract.MessageContracts"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
</GetUserRightsResponse>
</s:Body>
Why do i always get the namespace xmlns:a="http://schemas.datacontract.org/2004/07 for the property in the returned object? What does xmlns:a mean?
Both GetUserRightsResponse elements are in namespace "urn:MyNamespace:Authorization", because the default namespace declaration on the outer one is inherited by the inner one.
xmlns:a="http://schemas.datacontract.org/2004/07/MyMessageContract.MessageContracts" is a namespace declaration which defines a namespace prefix ("a") which isn't actually used in your message. So it has no effect at all on the meaning of the XML message and could be omitted. Its presence is a quirk of the Microsoft implementation, perhaps triggered by the naming collision between your message wrapper element name and the data contract name (but I'm just guessing about this).
If your clients are fully XML compliant, this shouldn't be a problem for you at all. However, there are some non-compliant SOAP client toolsets around which are fussy about the treatment of namespace declarations. If you are very unlucky you might find you have one which is confused by an unused namespace prefix declaration.
You are not getting wrong namespace, as far as I can see.
All elements are set to "urn:MyNamespace:Authorization" which I imagine is the AuthorizationXmlElementNames.Namespace.
xmlns:a="http://schemas.datacontract.org/2004/07/MyMessageContract.MessageContracts" points to Microsoft's schema for message contracts. Now this does not seem to have been used in the snippet you published, so I am surprised it is there at all but this is an innocent namespace since you do not have any element or attribute in that namespace.