In WCF I have a nullable int property on person object. What i want to do is that when the client explicitly passes NULL then i should update the database table to null, if they do NOT send this optional parameter then ignore.
Example code below. What i have foind is that there seems to be no easy way to do this.
[ServiceContract]
public interface IPersonRepository
{
[OperationContract]
string UpdatePerson(Person person);
}
public class PersonRepository : IPersonRepository
{
public string UpdatePerson(Person person)
{
return person.ToString();
}
}
[DataContract]
public class Person
{
[DataMember(IsRequired = false)]
public int? Age{get;set;}
}
Related
I have a simple entity, and one of its property is required:
[DataContract]
public class Person
{
[DataMember]
public string FirstName { get; set; }
[DataMember (IsRequired=true)]
public string LastName { get; set; }
}
This is the interface of the service:
[ServiceContract]
public interface IService1
{
[OperationContract]
Person DoubleLastName(Person person);
}
and this is the service:
public class Service1 : IService1
{
public Person DoubleLastName (Person person)
{
return new Person { FirstName = person.LastName, LastName =
person.LastName};
}
}
And here is the problem: When the client sends an object to this service,
without the required property, everything works.
Shouldn't I get an exception?
using (Service1Client myProxy = new Service1Client())
{
Person person1 = new Person { }; //Here I don't notify the required value.
Person person = myProxy.DoubleLastName(person1);
}
This is because DataMemberAttribute.IsRequired applies to .NET Framework 3.0:
For more information about it, you can refer to this link.
I tried to use .NET Framework3.0 for testing and got an exception:
I am using interface as input parameter in OperationContract. But when i generate proxy class at client side. I am not able to access the members of interface or class implemeting the ITransaction interface. I am only geeting is object
Service Interface
[ServiceContract]
public interface IServiceInterface
{
[OperationContract]
string SyncDatabase(ITransaction TransactionObject);
}
Service class
class SyncService:IServiceInterface
{
public string SyncDatabase(ITransaction TransactionObject)
{
return "Hello There!!";
}
}
Interface
public interface ITransaction
{
ExpenseData ExpData { get; set; }
void Add(ITransaction transactionObject);
}
Data Contract
[DataContract]
public class Transaction:ITransaction
{
[DataMember]
public ExpenseData ExpData
{
get;
set;
}
public void Add(ITransaction transactionObject)
{
}
}
In above case should i also copy the iTransaction class and interface on client
You actually need to make your ServiceContract aware of the implementation of the interface you pass as a parameter, so WCF will include it in the WSDL.
This should work:
[ServiceContract]
[ServiceKnownType(typeof(Transaction))]
public interface IServiceInterface
{
[OperationContract]
string SyncDatabase(ITransaction TransactionObject);
}
Use [KnownType(typeof(testClass))].
Refer these links:
msdn.microsoft.com/en-us/library/ms730167.aspx
www.codeproject.com/Tips/108807/Implementing-KnownType-Attribute
Try making your interface the [DataContract] and use the [KnownType] attribute to tell WCF what the known implementations of that interface are.
[DataContract]
[KnownType(typeof(Transaction))]
public interface ITransaction
{
[DataMember]
ExpenseData ExpData { get; set; }
void Add(ITransaction transactionObject);
}
Our WCF service has just one method:
[ServiceContract(Name = "Service", Namespace = "http://myservice/")]
[ServiceKnownType("GetServiceKnownTypes", typeof(Service))]
public interface IService {
Response Execute(Request request);
}
public class Service : IService {
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider) {
return KnownTypesResolver.GetKnownTypes();
}
public Response Execute(Request request) {
return new MyResponse { Result = MyEnumHere.FirstValue };
}
}
Both the Request and Response class includes a ParameterCollection member.
[Serializable]
[CollectionDataContract(Name = "ParameterCollection", Namespace = "http://myservice/")]
[KnownType("GetKnownTypes")]
public class ParameterCollection : Dictionary<string, object> {
private static IEnumerable<Type> GetKnownTypes()
{
return KnownTypesResolver.GetKnownTypes();
}
}
Subclasses of Request and Response store their values into the ParameterCollection value bag.
I am using the KnownTypesResolver class to provide type information across all Service objects.
public static class KnownTypesResolver {
public static IEnumerable<Type> GetKnownTypes()
{
var asm = typeof(IService).Assembly;
return asm
.GetAllDerivedTypesOf<Response>() // an extension method
.Concat(new Type[] {
typeof(MyEnumHere),
typeof(MyEnumHere?),
typeof(MyClassHere),
typeof(MyClassListHere),
});
}
}
If I'm not mistaken, everything should have proper type information for proxy class generation tools to produce well-defined classes client-side.
However, whenever one of the Response subclasses (i.e. MyResponse) contains an enum value such as MyEnumHere, WCF starts complaining that the deserializer has no knowledge of the MyEnumHere value. It should have. I provided a KnownTypeAttribute for this very reason.
The client-side proxy class does have a MyEnumHere enum in the Reference.cs file; the problem is that the ParameterCollection class has no KnownTypeAttributes generated for it.
I resorted to hand-editing and including the following lines in the generated Reference.cs file:
//>
[KnownTypeAttribute(typeof(MyEnumHere))]
[KnownTypeAttribute(typeof(MyEnumHere?))]
[KnownTypeAttribute(typeof(MyClassHere))]
[KnownTypeAttribute(typeof(MyClassListHere))]
//<
public class ParameterCollection : Dictionary<string, object> { /* ... */ }
Hand-editing generated files is horrible. But this makes the clients work. What am I doing wrong? How can I define my Service objects so that the VS-proxy classes that are generated are correct from the get-go?
Thanks for your time.
WCF does not work well with Dictionary because it is not interoperable. You may use Array, List or custom collection to make sure that your data is properly serialized.
Code below uses List<ParamCollectionElement> instead of Dictionary. I also removed some redundant attributes.
[DataContract]
public class Request
{
[DataMember]
public ParameterCollection ParameterCollection { get; set; }
}
[DataContract]
public class Response
{
[DataMember]
public ParameterCollection ParameterCollection { get; set; }
}
[DataContract]
public class MyResponse : Response
{
[DataMember]
public MyEnumHere Result { get; set; }
}
public class ParamCollectionElement
{
public string Key { get; set; }
public object Value { get; set; }
}
[CollectionDataContract(Name = "ParameterCollection")]
public class ParameterCollection : List<ParamCollectionElement>
{
}
public static class KnownTypesResolver
{
public static IEnumerable<Type> GetKnownTypes()
{
return
new Type[] {
typeof(MyEnumHere),
typeof(MyEnumHere?),
typeof(Request),
typeof(Response),
typeof(MyResponse)
};
}
}
[DataContract]
public enum MyEnumHere
{
[EnumMember]
FirstValue,
[EnumMember]
SecondValue
}
[ServiceKnownType("GetServiceKnownTypes", typeof(Service))]
[ServiceContract(Name = "Service")]
public interface IService
{
[OperationContract]
Response Execute(Request request);
}
public class Service : IService
{
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
{
return KnownTypesResolver.GetKnownTypes();
}
public Response Execute(Request request)
{
var result = new MyResponse
{
Result = MyEnumHere.FirstValue,
ParameterCollection = new ParameterCollection()
};
result.ParameterCollection.Add(new ParamCollectionElement {Key = "one", Value = MyEnumHere.FirstValue});
result.ParameterCollection.Add(new ParamCollectionElement { Key = "two", Value = new Response() });
return result;
}
}
Make sure you have [DataContract] on your enum and [EnumMember] on each of the enum members:
[DataContract]
public enum MyEnumHere
{
[EnumMember]
SomeValue,
[EnumMember]
OtherValue,
[EnumMember]
OneMoreValue
}
That should cause the proxy-enum to be built out (with its member values) in your client without having to manually change the Reference.cs file.
Im trying to create a WCF which consumes a List
public class myClass {
public int ID { get;set;}
}
In my service i write
[DataContract]
public class myClass
{
public int ID { get; set; }
}
And my client nows the entity
ServiceReference2.myClass[] sendData = new ServiceReference2.myClass[2];
but when i would add a new myClass to the array like
ServiceReference2.myClass add1= new ServiceReference2.myClass();
the entity only exposes the field 'ExtensionData' and not the field ID
What am i doing wrong
You forgot to decorate the ID property with the DataMember attribute:
[DataContract]
public class myClass
{
[DataMember]
public int ID { get; set; }
}
I have classes in my service defined as
[DataContract]
[KnownType(typeof(MyConcrete1)) ]
[KnownType(typeof(MyConcrete2)) ]
public abstract class MyAbstract
{
[DataMember]
public int AbsInt { get; set; }
}
[DataContract]
public class MyConcrete1 : MyAbstract
{
[DataMember]
public int Concrete1Int { get; set; }
}
[DataContract]
public class MyConcrete2 : MyAbstract
{
[DataMember]
public int Concrete2Int { get; set; }
}
and in my Service, I use it as
[ServiceContract]
public interface IService1
{
[OperationContract]
MyAbstract TestAbstract(MyAbstract value);
}
As you can see, the method TestAbstract takes and returns the abstract parameter MyAbstract, however in the client proxy generated by SvcUtil, the type "MyAbstract" is not abstract! It generated a concrete class.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name = "MyAbstract", Namespace = "http://schemas.datacontract.org/2004/07/WcfService")]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(WcfService.MyConcrete1))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(WcfService.MyConcrete2))]
public partial class MyAbstract : object, System.Runtime.Serialization.IExtensibleDataObject
{
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
private int AbsIntField;
public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public int AbsInt
{
get
{
return this.AbsIntField;
}
set
{
this.AbsIntField = value;
}
}
}
How can I force svcUtil to generate MyAbstract as an abstract class? I'm stuck, please help...
You cannot - svcutil cannot know that MyAbstract class is abstract. The metadata that's being exchanged between server and client just doesn't support such a concept. The SOA world doesn't always support everything the OO world has in store.
The service metadata only knows about things like services, method calls, and data contracts - anything else is not part of the service metadata.
You will need to add some extra logic and tweaking to the client code, once it's been creating, if that's a real requirement on your side.
Marc