Unable to initialize member through Constructor in WCF - c#

I have a CibilResponse Class that has properties that are of class type (TUEF class).
I am trying to assign values using : CibilEnquiryEnq.Tuef.Version = "12"; but it throws null reference error. I have already solved this error but through creating an object like : CibilEnquiryEnq.Tuef = new CibilWcfService.TUEF(); and not through constructor.
ICIBIL.cs
[ServiceContract]
public interface ICIBIL
{
[OperationContract]
string InsertCibil(CibilResponse cibilResponse);
[OperationContract]
string TestInsert(string testObj);
[OperationContract]
string GenerateEnquiry(CibilEnquiry testObj);
}
[DataContract]
public class CibilEnquiry
{
[DataMember]
public TUEF Tuef { get; set; }
public CibilEnquiry()
{
this.Tuef = new TUEF();
}
}
[DataContract]
public class TUEF
{
[DataMember]
public string SegmentTag { get; set; }
[DataMember]
public string Version { get; set; }
[DataMember]
public string MemberReferenceNumber { get; set; }
}
Application:(not working)
CibilWcfService.CIBIL obj = new CibilWcfService.CIBIL();
CibilWcfService.CibilEnquiry CibilEnquiryEnq = new CibilWcfService.CibilEnquiry();
CibilEnquiryEnq.Tuef.Version = "1111"; // null reference error here and Tuef is null
Application:(working)
CibilWcfService.CIBIL obj = new CibilWcfService.CIBIL();
CibilWcfService.CibilEnquiry CibilEnquiryEnq = new CibilWcfService.CibilEnquiry();
CibilEnquiryEnq.Tuef = new CibilWcfService.TUEF();
CibilEnquiryEnq.Tuef.Version = "1111";//works fine
I don't understand why I have to add CibilEnquiryEnq.Tuef = new CibilWcfService.TUEF(); to make this work. I am already initializing tuef in constructor in my wcf.
I created a sample in a console application (excluded wcf) and it worked fine without having Tuef = new TUEF();, initializing in constructor was enough.

The proxy objects generated by adding a service reference are not the same objects as you are defining in the service contract, they just happen to be created within the same namespace etc under the consuming clients service reference. Basically they are just DTOs that you use to consume the service.
If you want to have strong dependency between the objects then you can not use the service reference and you need to extract the contract to a separate assembly that you can reference.
1) CibilWcfService.Contract - contains the ICIBIL interface + datacontract objects. You need to reference System.ServiceModel, System.ServiceModel.Web and System.Runtime.Serialization for DataContract related attributes.
2) CibilWcfService - This hosts the WCF service and refers the CibilWcfService.Contract assembly.
namespace CibilWcfService
{
using CibilWcfService.Contract;
public class CibilService : ICIBIL
{
// ... Interface implementation
}
}
3) CibilClient - This is your consuming client application, it also refers the CibilWcfService.Contract assembly. You create the channel to the service like this, then the new CibilEnquiry() is using the same constructor as defined in your contract. You need to reference System.ServiceModel for ChannelFactory.
using CibilWcfService.Contract;
var cf = new ChannelFactory<ICIBIL>();
var channel = cf.CreateChannel(new EndpointAddress("http://127.0.01/CibilServiceUri"));
if (channel != null)
{
var CibilEnquiryEnq = new CibilEnquiry();
CibilEnquiryEnq.Tuef.Version = "1111";
channel.GenerateEnquiry(CibilEnquiryEnq);
}

Related

WCF Data Contracts using class from external assembly

using System.IO;
using System.Runtime.Serialization;
using System.Xml;
using MR.Storage.CommonClasses;
namespace Storage.Contract
{
[DataContract]
public class SaveMyData
{
[DataMember]
public MR.Storage.CommonClasses.MyData MyData{ get; set; }
}
Above is my data contract class for my wcf service. MyData is a poco class in an external assembly that is decorated with [DataContract] and [DataMember] attributes. When I add a service reference to it in a solution I get "Metadata contains a reference that cannot be resolved".
I also tried adding it in wcf test client and it throws error ...\Test Client Projects\14.0\729f94f0-f564-4439-90f9-1c1553821666\Client.cs(42,26) : error CS0234: The type or namespace name 'MyData' does not exist in the namespace 'MR.Storage.CommonClasses' (are you missing an assembly reference?) I opened this file and the only using statement is using System.Runtime.Serialization; Am what I am doing not possible? I saw some other suggestions about using a surrogate, but MyData has a ton of fields so would really like to find a solution that doesnt involve mapping each property
Did you edit the WCF service reference properties? You are able to allude to external assemblies that way. To be honest, I've found that approach to be a pain in the butt (version hell issues), but YMMV.
If you do not necessarily use DataContractSerializer, you could consider using XmlSerializer to serialize your data , it needn't use DataContract and DataMember attribute.
namespace ServiceInterface.Models
{
public class MyData
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class MyDataFather
{
public MyData MyData { get; set; }
}
}
My servicecontract and service. Please don't forget to add XmlSerializerFormat attribute to your service contract, it tells wcf to use XmlSerializer
[ServiceContract]
[XmlSerializerFormat]
public interface ICotractWithoutAttribute
{
[OperationContract]
MyDataFather GetData(MyDataFather myDataFather);
}
public class CotractWithoutAttribute : ICotractWithoutAttribute
{
public MyDataFather GetData(MyDataFather myDataFather)
{
return myDataFather;
}
}
The result.
My client.
using (ChannelFactory<ICotractWithoutAttribute> factory = new ChannelFactory<ICotractWithoutAttribute>("without"))
{
ICotractWithoutAttribute cotractWithoutAttribute = factory.CreateChannel();
ServiceInterface.Models.MyDataFather myDataFather = cotractWithoutAttribute.GetData(new ServiceInterface.Models.MyDataFather { MyData = new ServiceInterface.Models.MyData { Name = "MICK", Age = 18, Id = 1 } });
}
For more information about XmlSerializer, you could refer to https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-the-xmlserializer-class
If you must use DataContractSerializer , I think you should define a class with similar structure with the external class , add DataContarct and DataMember to the class and copy the data of your external class to your own class in your service.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/51b9d122-7345-44c7-8cd8-eb0ccdbffabf/can-i-use-external-assembly-class-in-a-wcf-without-using-poco?forum=wcf

C# Cannot add or modify List in WCF

I am using example code from a tutorial on Creating a WCF Service and it only half works. Adding to the List works when it's hard-coded and retrieving the List works. However, using a routine to Add or modify the List does not work. Here is the code:
BookData.cs
using System.Runtime.Serialization;
namespace BookServicesV2
{
[DataContract]
public class BookData
{
[DataMember]
public int BookID { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public string Author { get; set; }
[DataMember]
public decimal Price { get; set; }
}
}
BookService.cs:
using System.Collections.Generic;
namespace BookServicesV2
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
public class BookService : IBookService
{
private List<BookData> _books;
private BookService()
{
_books = new List<BookData>();
var book = new BookData { BookID = 1, Author = "Shakespeare", Title = "To Be or Not To Be", Price = 1.49M };
_books.Add(book);
book = new BookData { BookID = 2, Author = "Mark Twain", Title = "Not Dead Yet", Price = 3.50M };
_books.Add(book);
}
#region IBookService Members
public void AddBook(BookData book)
{
if (book != null) _books.Add(book);
}
public void UpdatePrices(decimal increaseAmmount)
{
foreach (var book in _books)
{
book.Price += increaseAmmount;
}
}
public List<BookData> GetBooks()
{
return _books;
}
#endregion
}
}
When I launch the WCF Test Client, GetBooks retrieves the two hard-coded books. When I use AddBook or UpdatePrices and then GetBooks, nothing has changed. I'm basically learning c# and WCF on the fly, so I am completely stumped. Thanks!
Wcf by default create a new instance at each call. Every time you call the service on client, your list will allways have the two hardcoded books.
Check if you creating a new BookService For each Wcf request. You can change the InstanceContextMode to Single in your config or you can do it programatically so that just one instance is called for all requests. You can google out how to set this behaviour
Try changing the service behavior of your BookService.cs.
[ServiceBehavior( ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.Single )]
public class BookService : IBookService{...}
This will change it to a singleton object. In other words, only 1 instance of your service is created.
You can also use InstanceContextMode = InstanceContextMode.PerSession as well. That means an object is created and maintained for each connection. If you close the connection and then re-connect you would have a new instance but you would be dealing with the same instance for each subsequent call you made to the service (assuming you don't close/reconnect).

host a WCF services which returns a list of instances of a datacontract

I'm new to visual studio. I create a datacontract about book information. I create a WCF web services parsing a txt file and create a list of instance of that book information.
When I was trying to call this service to get book information and displace on a web form. I found I don't know how to access Datamember of those instance. Can anybody help me?
public interface IService1
{
[OperationContract]
List<Book> GetAllBooks();
[OperationContract]
String SearchBookById();
}
[DataContract]
public class Book
{
[DataMember]
public int Num { get; set; }
[DataMember]
public string ID { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public string author { get; set; }
[DataMember]
public int year { get; set; }
[DataMember]
public float price { get; set; }
[DataMember]
public int stock { get; set; }
}
public List<Book> GetAllBooks()
{
var bookList = new List<Book>();
int n = 1;
StreamReader reader = new StreamReader(#"C:\infs 7204\prac3\books(1).txt");
{
string fileLine;
while((fileLine = reader.ReadLine())!= null)
{
string[] bookInfo = fileLine.Split(',');
int year = Int32.Parse(bookInfo[3]);
float price = float.Parse(bookInfo[4].Trim('$'));
int stock = Int32.Parse(bookInfo[5]);
bookList.Add(new Book { Num = n, ID = bookInfo[0], name = bookInfo[1], author=bookInfo[2],
year=year,price=price, stock=stock});
n++;
}
}
return bookList;
}
I need to display those book classes in a table on my webpage. Here is how I want to access the datacontract list. But I got an warrning says "Cannot implicitly convert type'prac3.ServiceReference1.Book[] to 'prac3.Book[]'"
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Service1Client client = new Service1Client();
Book[] list= client.GetAllBooks();
}
}
Also, Is there any segesstion about which conponent in toolbox should I use to display the table?
Try to include the full namespace of Book and see if it works.
Have you added a project reference to the Book class and generated a service reference also? If that's thje case, better remove the project reference and let svcutil generate a common namespace with ädd service reference".
List<prac3.ServiceReference1.Book> books = client.GetAllBooks();
foreach (prac3.ServiceReference1.Book book in books)
{
Console.WriteLine(book.author);
}
Your warning Cannot implicitly convert type'prac3.ServiceReference1.Book[] to 'prac3.Book[] indicates that very likely you have duplicate definition of type Book.
There are two possibilities:
More likely, as Servé Laurijssen has already pointed out, your client project might have a project reference to your service project. Hence you have access to all of the public types defined within it (including Book.) To be sure if your client project has reference to service project: open your client project in Visual Studio, in Solution Explorer expand the References node under your client project, and find if your service project's name appear there.
Or, less likely, you have redefined class Book in your client project.
In both cases, you get two definitions of Book: one prac3.Book (defined in service project) and another prac3.ServiceReference1.Book (generated by service reference in client project, usually in the file Service References\Service Namespace Name\Reference.cs under your client project directory.) In your case, you are trying to set a prac3.ServiceReference1.Book array to a prac3.Book one, hence causing a type mismatch.
For an immediate remedy change the following line in your client code
Book[] list= client.GetAllBooks();
to the following
prac3.ServiceReference1.Book[] list= client.GetAllBooks();
And consider removing the reference to the service project or the duplicate definition. It is very unusual a client has reference to a service assembly that it refers as well.

WCF Guid DataMember not Serialized properly

I have a WCF service that passes back and forth the following DataContracts:
[DataContract]
public class RequestWrapper
{
[DataMember]
public FooDataContract FooDataContract;
}
[DataContract]
public class ResponseWrapper
{
[DataMember]
public FooDataContract FooDataContract;
}
[DataContract]
public class FooDataContract
{
public FooDataContract(string data, Guid id)
{
Data = data;
ID = id;
}
[DataMember]
public string Data { get; set; }
[DataMember]
public Guid ID { get; set; }
}
It's called via a proxy class like this:
void CallService(string data)
{
var id = Guid.NewGuid();
var response = proxy.CallService(new RequestWrapper
{
new FooDataContract(data, id);
});
}
This is then passed (over the service) to the database via a repository using EF:
public void RepoMethod(FooDataContract foo)
{
var guid = foo.ID; // - Breakpoint here shows all zeros!
efContext.DoSomething(foo.Data, foo.ID);
}
Here's the service call:
public ResponseWrapper CallService(RequestWrapper request)
{
var foo = request.FooDataContract;
repository.RepoMethod(foo);
var response = new ResponseWrapper{ FooDataContract = foo };
return response;
}
Here's the proxy:
public class Proxy : IMyService
{
static readonly ChannelFactory<IMyService> channelFactory =
new ChannelFactory<IMyService>("IMyService");
ResponseWrapper CallService(RequestWrapper request)
{
return channelFactory.UseService(s => s.CallService(request));
}
}
internal static class UseServiceFunction
{
internal static R UseService<T, R>
(this ChannelFactory<T> channelFactory, Func<T, R> useService)
{
var service = channelFactory.CreateChannel();
try
{
R response = useService(service);
return response;
}
finally
{
var channel = service as ICommunicationObject;
try
{
if (channel.State != CommunicationState.Faulted) channel.Close();
}
catch { channel.Abort(); }
}
}
}
I've put a watch on the Guid in the VS debugger. When the service is called from a client web application, the generated Guid is a valid Guid of seemingly random hex characters. Great, that's working.
But when the data is serialized, goes over the wire, and comes out the other side (in my repository), the Guid is all zeros!
I've double, triple checked that the Guid is indeed marked with the [DataMember] attribute. I'm wondering if the extra layer of DataContract (how a FooDataContract is wrapped with the RequestWrapper data contract) is causing a serialization issue?
I think your problem here is that the constructor you've made in your DataContract class doesn't get passed to the proxy on the client side. WSDL won't know anything about this. Think of your data contracts as just a place to stick data with no other functionality. To confirm, you can look in the reference.cs class that got generated in the client when you added the service reference.
I'd suggest re-writing the code so that you explicitly set each of the values in your data contract rather than relying on the constructor.
You can also write a hand coded proxy that has whatever behavior you want and then share that file with the client. That would work, but then you'll be more tightly coupling your client to your service.
Turns out, my translation layer wasn't updated to convert between the DTOs! Whooooops!

Can I Create a Web Service that has Properties?

I'm trying to test code around a web service that is not available yet. I'm trying to dummy up my own version. According to the specs it will be called like this.
var service = new Service();
service.SD = new ServiceData();
service.SD.ID = "ABC123";
service.SD.Auth = "00000";
string result = service.DoMyThing();
This is the closest I've gotten.
var service = new Service();
service.set_SD(new ServiceData());
service.get_SD().ID = "ABC123";
service.get_SD().Auth = "00000";
service.DoMyThing();
The problem is with the SD property. How do I write the service so that Visual Studio 2008 generates the web reference correctly?
Here is my current dummy web service code.
public class Service : System.Web.Services.WebService
{
// This doesn't show up in the generated proxy at all
public static ServiceData SDTest;
// For extra credit explain why this needs to be static for it to work
private static ServiceData _sd;
public ServiceData SD
{
[WebMethod(EnableSession = true)]
get { return _sd; }
[WebMethod(EnableSession = true)]
set { _sd = value; }
}
[WebMethod]
public string DoMyThing()
{
// Presumably the real service accesses SD in here
return "";
}
}
public class ServiceData
{
public string ID { get; set; }
public string Auth { get; set; }
}
Your design is flawed. Web services are not meant to have properties. They should only expose methods, the reason being that the HTTP protocol is stateless (and web services assume this too), so exposing a property doesn't make sense unless you want it to apply to all callers of the instance (and still, even in that situation, it doesn't make sense to expose it).
Rather, what you want to do is have the DoMyThing method take the instance of ServiceData (if required) and operate on that, returning the appropriate result set.
If you really have a need to expose properties of the service, you would have a GetProperties method (or something like that) which takes no parameters and returns the appropriate data structure with the service information.
I'm with casperOne on this. I think using fakie properties are more annoying than useful.
Still, if you're married to this just eliminate the getter for the property. You don't need it. Do this instead:
var service = new Service();
ServiceData sd = new ServiceData();
sd.ID = "ABC123";
sd.Auth = "00000";
service.SD = sd;
string result = service.DoMyThing();
If Visual Studio still names the setter property incorrectly you can use one of the soap attributes to rename it.
EDIT: You'll also need to define SD as a SOAP Header.
You can't do this, so don't try to "fake it". The best you can do is:
var service = new Service();
ServiceData sd = new ServiceData();
sd.ID = "ABC123";
sd.Auth = "00000";
string result = service.DoMyThing(sd);
For those that may be interested.
This more accurately reflects the spec than my sanitized version above (I didn't know "TypeNameValue" was a key clue, sorry!).
var service = new Service();
service.ServiceDetailsValue = new ServiceDetails();
service.ServiceDetailsValue.ID = "ABC123";
service.ServiceDetailsValue.Auth = "00000";
string result = service.DoMyThing();
And this is the dummy web service code that works.
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(Name="TestService", ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
public ServiceDetails SDTest;
[WebMethod]
[SoapDocumentMethod(Binding = "TestService")]
[SoapHeader("SDTest", Required = true)]
public string DoMyThing()
{
return "";
}
}
public class ServiceDetails : System.Web.Services.Protocols.SoapHeader
{
public string ID { get; set; }
public string Auth { get; set; }
}

Categories