I have a project A which has a Service reference to a WCF Service. I want to invoke the service in project B without a Service reference.
From what I understand, the Service reference is just a way to generate the proxy and config and is not used at run-time.
I copied the proxy class and the node from project A to project B.
Can I just create an instance of the proxy class in project B and expect it to directly use the config. items and connect to the service without anything else?
(I cannot try this right now)
Short answer yes.
As long as you have the interface, a way to connect to the service, you can create a channel and talk to service without having the reference.
The reference simply makes it easier for you to develop against.
Look into creating channels from your service contracts with ChannelFactory.
Here is a working copy. For me it works fine and returns List
private List<MyClass> GetAllSiteDetailsJSON(string language)
{
Uri address =
new Uri(#"http://weburlpath/MyService/MyService.svc/GetAllList/"
+ language);
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
request.Method = "GET";
request.ContentType = "application/x-www-form-urlencoded";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
string jSon = reader.ReadToEnd();
reader.Close();
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
List<MyClass> result = jsSerializer.Deserialize<List<MyClass>>(jSon);
return result;
}
}
And Here is the class
public class MyClass
{
public string ID { get; set; }
public string Name { get; set; }
public List<Location> Locations { get; set; }
}
public class Location
{
public string Region { get; set; }
public string Country { get; set; }
}
Related
I have a WCF Service like following:
public class Service1 : IService1
{
public string GetData(Person person)
{
if (person != null)
{
return "OK";
}
return "Not OK!";
}
Here is my Person class:
[DataContract]
public class Person
{
[DataMember]
public int Age { get; set; }
[DataMember]
public string Name { get; set; }
}
And I'm calling service like that:
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
IChannelFactory<IRequestChannel> factory = binding.BuildChannelFactory<IRequestChannel>(new BindingParameterCollection());
factory.Open();
EndpointAddress address = new EndpointAddress(url);
IRequestChannel irc = factory.CreateChannel(address);
using (irc as IDisposable)
{
irc.Open();
string soapMessage = "<GetData><person><Age>24</Age><Name>John</Name></person></GetData>";
XmlReader reader = XmlReader.Create(new StringReader(soapMessage));
Message m = Message.CreateMessage(MessageVersion.Soap11,"http://tempuri.org/IService1/GetData", reader);
Message ret = irc.Request(m);
reader.Close();
return ret.ToString();
}
When I try to send complex type like Person as a parameter to GetData method, person object is coming null. But I have no problem when I send known type like integer, string etc. as a parameter.
How can I manage to send complex type as a parameter to the service method?
I ran into a similar situation, and we ended up changing the interface of the service to be the equivalent of:
public string GetData(string person)
And we did our own object serialization before calling the web service. Immediately within the web service method we would deserialize it, and proceed as normal.
I have WCF service that uses raw messages (Message class).
1) Service side:
[DataContract]
public class Person
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
[ServiceContract]
public interface ITestService
{
[OperationContract(Action = TestService.RequestAction3)]
void AddNewPerson(Message newPerson);
public void AddNewPerson(Message newPerson)
{
Person personToAdd = newPerson.GetBody<Person>();
Employees.Persons.Add(personToAdd);
}
2) Client side:
TestServiceClient client = new TestServiceClient();
String RequestAction3 = "http://localhost:4249/Message_RequestAction3";
TestService.Person person = new TestService.Person
{
Id = 6,
FirstName = "Aleksey",
LastName = "Alekseyev"
};
Message request3 = Message.CreateMessage(MessageVersion.Default, RequestAction3, person);
string soapRequest = request3.ToString();
client.AddNewPerson(request3);
What's the problem here? I have Person class (data contract) on service side that is placed in TestService namespace: TestService.Person. Everything is fine on service side. But after I added service reference to client side by using "Add Service Reference..." option in VS2008, there's no such a type (TestService.Person) on client side. What I did to resolve this issue? I've simply copied the file with original data contract (TestService.Person) on client side, created object of Person type and passed it to the service method.
My question is - did I do it in correct way or there is another way to do this?
Thank you in advance.
Goran
Because Person class is not exposed in none of your service contracts their information is not shared via service metadata. That's why you get an error on the client side. If you copy the classes to your client with the same namespace that will do.
However a better solution is to place Person class in another assembly and reference this assembly from your client.
I'm brand new to OData and WCF data services so this might be an easy problem. I'm using VS Web Developer Express 2010 where I have a very simple WCF Data Service hosted in a console app. It's returning an IQuerable collection of a simple 'Study' class from a repository (located in a separated dll project), which in turn retrieves 'Study' classes from a db project in another dll (so 3 projects in the solution).
I also have an 'Experiment' class in the db project and there can be multiple Experiments in a Study. When I exclude the Experiment class from the Study everything works and I get data coming back. The problem happens when I add a List collection to the Study class, then I get a runtime error when I try to run the service. In Firebug the error is '500 Internal Server Error', and the message in the browser is 'Request Error. The server encountered an error processing the request. See server logs for more details.'
I have IIS 7 and I also just installed IIS 7.5 but again it's brand new to me, so I can't figure out where the service is hosted or where to view the server / web logs. There are only IIS 7 logs visible in 'C:\inetpub\logs\LogFiles\W3SVC1'. The VS web server (Cassini) doesn't start when I run the app, so this suggests it's being hosted in IIS 7.5 (?).
So
- how do I return child classes / complex objects?
- how do I know where my service is hosted and where can I find the server logs?
Here's the host app:
using MyStudyRepository;
using MyStudyDB;
namespace MyStudyService
{
public class Program
{
public static void Main(string[] args)
{
string serviceAddress = "http://localhost:998";
Uri[] uriArray = { new Uri(serviceAddress) };
Type serviceType = typeof(StudyDataService);
using (var host = new DataServiceHost(serviceType,uriArray))
{
host.Open();
Console.WriteLine("Press any key to stop service");
Console.ReadKey();
}
}
}
public class StudyDataService : DataService<StudyRepository>
{
public static void InitializeService(IDataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
}
}
}
Here's the repository:
using MyStudyDB;
namespace MyStudyRepository
{
public class StudyRepository
{
List<Study> _List = new List<Study>();
//Add constructor to populate myStudies list on creation of class
public StudyRepository()
{
for (int i = 1; i < 5; i++)
{
Study myStudy = new Study() { ID = i, StudyOwnerId = i, StudyName = "Study" + i.ToString() /*, Experiments = null */ };
_List.Add(myStudy);
}
}
public IQueryable<Study> Studies
{
get
{
return _List.AsQueryable<Study>();
}
}
}
}
And here's the DB:
namespace MyStudyDB
{
public class Study
{
public int ID { get; set;}
public int StudyOwnerId { get; set; }
public string StudyName { get; set; }
//public List<Experiment> Experiments { get; set; }
}
public class Experiment
{
public int ID { get; set; }
public string Name { get; set; }
public int StudyId { get; set; }
}
}
To debug the WCF Data Service please refer to this blog post: http://blogs.msdn.com/b/phaniraj/archive/2008/06/18/debugging-ado-net-data-services.aspx
As to why the collection of Experiment doesn't work, there are two reasons:
The Experiment class is not recognized as an entity type because there's no entity set for it. (Entity set is the IQueryable property on your repository class, which you don't have). As a result the Experiment class is only recognized as a complex type.
The currently released version of WCF Data Services doesn't support MultiValues, MultiValue is effectively a collection of primitive or complex types.
So you have two way to "fix" this. Either make sure that Experiment is in fact an entity, by adding IQueryable property on your repository class.
Or use the latest CTP (http://blogs.msdn.com/b/astoriateam/archive/2011/06/30/announcing-wcf-data-services-june-2011-ctp-for-net4-amp-sl4.aspx) which does support MultiValues.
Thanks! And I guess it is missing the DataServiceKey attribute on the class as follows:
[DataServiceKey("ID")]
public class Study
{
.....
}
I want to return an anonymous type over WCF. Is this possible?
You cannot use anonymous types, but maybe you are talking about WCF and untyped messages?
There is an option in WCF to just define a parameter of type Message (and possibly a return value of the same type). This is just the raw message that goes to WCF (and comes back from it).
I can't find much good information out there - there's some documentation on MSDN, but the best I've seen so far is Kurt Claeys' blog post WCF : Untyped messages on WCF operations.
I would not recommend using this approach - it's a lot more grunt work to handle the message contents directly yourself and that's what WCF is trying to spare us from - but if you absolutely, positively have to tweak every bit of your message - this seems like the way to go.
Marc
You can't return an anonymous type from any method, can you? So why would you be able to return it from WCF?
Looks like you cannot do so with the XML Serializer because of some complaint about a parameterless constructor but it works for the json serializer if you are serving to an ajax client as indicated by Dave Ward.
OK, I understand. But then if I define
a type - MyObj - for this purpose and
mark its members IsRequired=false, how
can I create+send across an instance
of MyObj with only some of its
members? Is this possible??
Take a look at [DataMember(EmitDefaultValue=false)]
No, it is not. You'll have to define your types ahead of time.
You definitely can return anonymous types. This works, for example:
public object GetLatestPost()
{
XDocument feedXML = XDocument.Load("http://feeds.encosia.com/Encosia");
var posts = from feed in feedXML.Descendants("item")
select new
{
Title = feed.Element("title").Value,
Link = feed.Element("link").Value,
Description = feed.Element("description").Value
};
return posts.First();
}
If you call that method as an ASMX ScriptService's WebMethod, you'll get this JSON from it:
{"d":
{"Title":"Using an iPhone with the Visual Studio development server",
"Link":"http://feeds.encosia.com/~r/Encosia/~3/vQoxmC6lOYk/",
"Description":" Developing iPhone-optimized portions of an ASP.NET ..."}}
You can use a return type of IEnumerable to return a collection of anonymous types also.
You can use the ExpandoObject. When you define a property in a DTO as ExpandoObject the client is generated as Dictionary:
Contract DTO
public class TaskDTO
{
public string Type { get; set; }
public ExpandoObject Args { get; set; }
public string Id { get; set; }
public TaskDTO SuccessTask { get; set; }
public TaskDTO FailTask { get; set; }
public bool IsFinal { get; set; }
}
Client
using (var client = new JobServiceClient())
{
var task = new TaskDTO
{
Id = Guid.NewGuid().ToString(),
Type = "SendEmailTask",
IsFinal = true
};
dynamic args = new ExpandoObject();
args.To = "who#mail.com";
args.Title = "test job service";
args.Content = "test job service";
task.Args = ((IDictionary<string, object>)args).ToDictionary(i => i.Key, i => i.Value);
client.Execute(task);
}
Service
dynamic args = dto.Args;
In Silverlight 2, I am attempting to add a new service which will return an object containing two lists from the WCF Service to the Silverlight app on the client. The svc and interface file already contain two contracts which work and are being used. After adding the new service, I click on the "Update Service Reference" option in the Silverlight app and receive the error:
There was an error downloading "http://localhost:3005/CMS.svc" ...
Metadata contains a reference that cannot be resolved "http://localhost:3005/CMS.svc" ...The client and service bindings may be mismatched...
Even though the web service project rebuilds without error, I think there must be something wrong with the way I have defined the service in the web service project, because when I remove the new service, the remaining two services are updated OK, and if I add a new service which I know is OK, the service reference will update OK. So I don t think it is a problem of endpoints, or the port number, etc.
The new service is supposed to return an object which contains two lists.
Here is the code:
In the interface file:
namespace CMSSilverlight.Web
{
// NOTE: If you change the interface name "ICMS" here, you must also update the reference to "ICMS" in Web.config.
[ServiceContract]
public interface ICMS
{
[OperationContract]
POCollection GetPOCollection(String s);
}
[DataContract]
public class POCollection
{
[DataMember]
public IList<Employee> em;
[DataMember]
public IList<School> sc;
}
public class Employee
{
public string EmpID { get; set; }
public string EmpName { get; set; }
public Employee(string empID, string empName)
{
this.EmpID = empID;
this.EmpName = empName;
}
}
public class School
{
public string SchID { get; set; }
public string SchName { get; set; }
public School(string schID, string schName)
{
this.SchID = schID;
this.SchName = schName;
}
}
}
In the service file:
namespace CMSSilverlight.Web
{
{
public POCollection GetPOCollection(String sParam)
{
IList<Employee> empList = new List<Employee>();
for (int i = 0; i < 5; i++)
{
empList.Add(new Employee(i.ToString(), i.ToString() + " Emp Name"));
}
IList<School> schList = new List<School>();
for (int i = 0; i < 5; i++)
{
schList.Add(new School(i.ToString(), i.ToString() + " Sch Name"));
}
POCollection po = new POCollection()
{
em = empList,
sc = schList
};
return po;
}
}
}
James,
Many thanks - I should have though of that. At any rate below was the error. It was just a matter of adding the [DataContractAttribute] attribute to the Employee and School classes, and everything worked fine. This is a frustrating learning process, but it is nice when a solution is revealed.
An ExceptionDetail, likely created by `IncludeExceptionDetailInFaults=true`, whose value is:
System.InvalidOperationException: An exception was thrown in a call to a WSDL export extension: System.ServiceModel.Description.DataContractSerializerOperationBehavior
contract: http://tempuri.org/:ICMS ----> System.Runtime.Serialization.InvalidDataContractException: Type 'CMSSilverlight.Web.Employee' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. See the Microsoft .NET Framework documentation for other supported types.