Base class members not being exposed in WCF service reference - c#

In order to use a WCF SOAP service, I am using the "Add a service reference" tool in VS2015.
The problem is when adding the service reference this way, the members of the base class are not exposed in the derived classes.
Here is my interface for my types:
[ServiceKnownType(typeof(DbEntry))]
public interface ILogEntry
{
[DataMember]
Int64 Id { get; set; }
[DataMember]
String Error { get; set; }
[DataMember]
String CustomMessage { get; set; }
[DataMember]
Int32 ApplicationId { get; set; }
Int32 ServiceId { get; set; }
[DataMember]
Guid ApplicationGuid { get; set; }
[DataMember]
String ApplicationName { get; set; }
...etc.
Here it the base class which implements the interface:
[DataContract]
[KnownType(typeof(DbEntry))]
public abstract class LogEntry : ILogEntry
{
private Int64 _Id;
[DataMember]
public Int64 Id
{
get { return _Id; }
set { _Id = value; }
}
private String _Error;
[DataMember]
public String Error
{
get { return _Error; }
set { _Error = value; }
}
private String _CustomMessage;
[DataMember]
public String CustomMessage
{
get { return _CustomMessage; }
set { _CustomMessage = value; }
}
private Int32 _ApplicationId;
[DataMember]
public Int32 ApplicationId
{
get { return _ApplicationId; }
set { _ApplicationId = value; }
}
private Int32 _ServiceId;
public Int32 ServiceId
{
get { return _ServiceId; }
set { _ServiceId = value; }
}
private Guid _ApplicationGuid;
[DataMember]
public Guid ApplicationGuid
{
get { return _ApplicationGuid; }
set { _ApplicationGuid = value; }
}
...etc
Here is my derived class :
[DataContract]
public class DbEntry : LogEntry
{
private String _Message;
[DataMember]
public String Message
{
get { return _Message; }
set { _Message = value; }
}
}
In the client the only datamember that is being exposed in the DbEntry class is Message. When I use the CS file that is generated by accessing the WSDL directly then everything works great.
Can anyone tell me why the base class members aren't exposed in the DbEntry derived class when I use the "Add a service reference" tool?

I commend you for attempting to program your code against interfaces, but I find that doing this out of WCF is not possible. I still program against interfaces, but at the WCF level I include conversion code that takes the Interface instance and translates it into an equivalent WCF Model.
When you discover the service, it will only create objects based on what it can pass in or receive. Your problem also deals with inheritance, as WCF won't extrapolate that for you, either. You must reference both LogEntry and DbEntry as seperate objects that can be either passed in or returned by your Service functions. In other words, you should have a seperate Operation Contract for LogEntry AND DbEntry.
Thanks,
Jibba

Is it your WCF service? If so, place the models and interface for the service in a separate project, and reference that library from both your WCF service and your client application. I often name that something like MyServiceName.Interface.
(Even if it's not your WCF service but you're able to reference the models from your client app this still works.)
Then, when adding the service reference, click "Advanced" and ensure that "Reuse types in referenced assemblies" is checked.
Then, when you add the reference and the proxy class is created it will reuse your models as declared in your interface library. [DataContract] and [DataMember] attributes won't matter. Because you're using the exact classes that you defined (not creating new proxy classes) your inherited properties will be accessible.

Related

SOAP webservice method returning different types

Our webservice must return different types that may have one base type with some shared properties but with otherwise different properties.
Method may look like:
CreateObject(int typeID)
and depending on the typeID a different type of object would be returned. If this was no webservice, then an IOC container would do the trick. But in our scenario, the object must be created by the service and will actually be used be another service that will then pass the object to the business layer.
We are trying to avoid a lot of mapping and if possible the object types should only be defined in one place.
What would be a good design pattern to look for in this scenario?
Something like this does the trick (of course this can be nicyified with IOC etc.), the piece I was missing is the "KnownType" attribute:
[ServiceContract]
public interface IService1
{
[OperationContract]
AbstractResult GetAbstractData(int id);
}
[DataContract]
[KnownType(typeof(ConcreteResult1))]
[KnownType(typeof(ConcreteResult2))]
public abstract class AbstractResult
{
[DataMember]
public int ID { get; set; }
}
[DataContract]
public class ConcreteResult1 : AbstractResult
{
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class ConcreteResult2 : AbstractResult
{
[DataMember]
public int Number { get; set; }
}
Simple example of a service method:
public class Service1 : IService1
{
public AbstractResult GetAbstractData(int id)
{
if (id == 1)
return new ConcreteResult1() { ID = 1, Name = "red" };
else
return new ConcreteResult2() { ID = 2, Number = 123 };
}
}

exposing datacontract defined in a different namespace via WCF

I have developed a webservice, it accepts object as input
namespace Someservice
{
public bool CreateBehavior(ClassInput classInput)
{
// code here
}
}
now I have defined this ClassInput as a datacontract in business entities which is a different namespace
namespace Someservice.BusinessEntities
{
[DataContract]
public class ClassInput
{
[DataMember]
public int intvariable{ get; set; }
[DataMember]
public string stringVariable { get; set; }
}
}
To use this ClassInput in the webservice i have added project reference to Business entities project.
Now how can i expose this datacontract to consumer of this webservice?
For instance in client side, they will need this classInput, but its not exported via service!!
SomeserviceClient someserviceClient = new SomeserviceClient();
someserviceClient.CreateBehavior(classInput)
I read about datacontractsurrogate but i am not able to map that to this scenario

WCF RIA Services POCO Entity properties becoming null after SubmitChanges

I have a POCO object in my solution that has some Entity objects as properties. I have done this in many other classes, but not when the base class is a POCO as opposed to another Entity.
Here is the server side version of the class:
public partial class ItemLocationPricingDetail
{
public ItemLocationPricingDetail()
{
}
[Key]
[DataMember]
public int ItemLocationDetailId
{
get;
set; //get { return _itemLocationDetail.Id; }
}
[DataMember]
public int ItemMasterId
{
get
{
if (ItemLocationDetail == null) return 0;
return ItemLocationDetail.ItemMaster.ItemMasterId;
}
}
private EntityRef<ItemLocationDetail> _itemLocationDetail;
[DataMember]
//[Include()]
//[Association("ParentChild", "ItemLocationDetailId", "Id", IsForeignKey = false)]
public ItemLocationDetail ItemLocationDetail
{
get { return _itemLocationDetail.Entity; }
set { _itemLocationDetail.Entity = value; }
}
[DataMember]
public ItemMaster ItemMaster
{
get
{
if (ItemLocationDetail == null) return null;
return ItemLocationDetail.ItemMaster;
}
set { ItemLocationDetail.ItemMaster = value; }
}
private decimal _newPrice;
[DataMember]
public decimal NewPrice
{
get { return _newPrice; }
set { _newPrice = value; }
}
}
I do have a client side partial class, but that doesn't deal with any of these properties.
I also have a meta data class as follows:
[MetadataTypeAttribute(typeof(ItemLocationPricingDetail.ItemLocationPricingDetailMetadata))]
public partial class ItemLocationPricingDetail
{
internal sealed class ItemLocationPricingDetailMetadata
{
[Key]
public int ItemLocationDetailId { get; set; }
[Include]
[Association("ItemLocationDetailAssociation", "ItemLocationDetailId", "Id")]
public EntityRef<ItemLocationDetail> ItemLocationDetail { get; set; }
[Include]
[Association("ItemMasterAssociation", "ItemMasterId", "ItemMasterId")]
public EntityRef<ItemMaster> ItemMaster { get; set; }
}
}
The issue is that I make a simple modification to a property of the ItemLocationDetail property. I then call SubmitChanges on the DataContext which works perfectly fine. The issue is that after that has completed, something, tries to access some of the properties in the ItemLocationPricingDetail class, but for some reason the ItemLocationDetail is now null. This is why there are null checks on that property throughout the class, just so I can try and see where the issue is, but I can't find the problem.
I am guessing it occurs when the ItemLocationPricingDetail class is being updated after the SubmitChanges has been called, but I'm unsure of the working of RIA services at that level.
As you can can see there is some commented out code around the ItemLocationDetail property, while this compiles and no errors are given, the issue still persists. That is the only information vaguely related to this issue I have found on the internet.
Could anyone make any suggestions? If you need more information I will provide it.
Thanks.

How to use custom type with WCF data service and EF

I've created a WCF Data Service with a base class of my EF model.
I wanted to return a custom type (one that isn't in my EF model), but I get the error:
The server encountered an error processing the request. Please see the service help
page for constructing valid requests to the service.
My custom class looks like:
public class MyCustomClass
{
public string CustomProp { get; set; }
public List<int> Ids { get; set; }
}
How can I make this work?
You need to set up your return object as a data contract:
[DataContract]
public class MyCustomClass
{
[DataMember]
public string CustomProp { get; set; }
[DataMember]
public List<int> Ids { get; set; }
}
See also: How to accept JSON in a WCF DataService?
Linked is how to set up a receiving service, returning the values you just change the return types on your methods.
The only way I have found to do this with WCF Data Services is to pass a json string as a parameter and then deserialize it into the custom class.
Like so:
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public bool ConfigurationChanged(string jsonStr)
{
try
{
MyObject obj = new JavaScriptSerializer().Deserialize<MyObject>(jsonStr);
// ... do something with MyObject
}
catch (Exception)
{
throw;
}
}

wcf newbie question: arrays as properties

I have DataContract class which has property of type List<AnotherObject>. AnotherObject is also marked with DataContract. For some reason this property comes from wcf service as null, althought I fill it at the server. Is that by design?
Here you go. Class definitions:
[DataContract]
public class UserInfo
{
[DataMember]
public decimal UserID
{
get;
protected internal set;
}
[DataMember]
public string UserName
{
get;
protected internal set;
}
[DataMember]
public string Pswd
{
get;
protected internal set;
}
[DataMember]
public List<decimal> RoleID
{
get;
protected internal set;
}
List<UserRole> userRolesTable = new List<UserRole>();
[DataMember]
public List<UserRole> UserRoles
{
get
{
return userRolesTable;
}
protected internal set { }
}
}
[DataContract]
public class UserRole
{
[DataMember]
public decimal ROLEID { get; internal set; }
[DataMember]
public string ROLE_CODE { get; internal set; }
[DataMember]
public string ROLE_DESCRIPTION { get; internal set; }
[DataMember]
public decimal FORMID { get; internal set; }
[DataMember]
public string FORMCODE { get; internal set; }
[DataMember]
public string FORMNAME { get; internal set; }
}
UserRoles property comes as null.
Why are you letting the RoleId property be auto-implemented but not UserRoles? The code as-is won't work because you have an empty setter. You should probably just use an auto-property for it:
[DataMember]
public List<UserRole> UserRoles
{
get; set;
}
Or at least provide a meaningful setter. You setter does nothing, hence the de-serializer can't populate the value.
List<UserRole> userRolesTable = new List<UserRole>();
[DataMember]
public List<UserRole> UserRoles
{
get
{
return userRolesTable;
}
protected internal set { }
}
Your setter is empty. Put some
userRolesTable = value;
Another thing, your DataContract properties should have public setters.
Your Setter on the UserRoles property is set to internal. Because the WCF framework will be setting the property, it gives up assigning the value because it is listed as internal.
http://connect.microsoft.com/data/feedback/details/625985/wcf-client-entities-with-internal-setters-and-internalsvisibletoattribute-on-asmbly-fail
You can do what this link suggests, using the InternalsVisibleToAttribute attribute on that property, but I have never used it.
update
What I am trying to say is that I bet the Serialization works fine, the WCF framework is unable to insert the deserialized value into the host code because based upon the data contract, the internal Setter section of the property is inaccessible. use the InternalVisibleTo attribute to inform the WCF serialization framework access to the setter of the client version of your data contract object.
You need to Implement the setter...
protected internal set { userRolesTable = value; }
Basically, its a serialization problem. I had this problem in my code in the past, but it has been a while, so bear with me.
First, we need to find out if the object relations are null before the WCF call, so put a debug before and after.
If the object is being returned as null before the call, you have a few options:
You can explicitly use .Include("AnotherObject") on your DbContext to get the object. I used this by having my Read method take an array of strings which I used to include all the necessary objects. This is more ideal than automatically taking all objects because during serialization, if you take everything, you could fairly easily end up with your entire database being serialized, which introduces performance and security issues, among other things.
Another option is to use a dynamic proxy by adding the keyword virtual in front of your list. The DataContractSerializer, though, has a problem serializing dynamic proxies, so you will need to implement an attribute that uses the ProxyDataContractResolver instead of DataContractResolver. This attribute needs to be applied on all OperationContracts that can pass a dynamic proxy. This will automatically take ALL object references, which is probably bad coding practice, so I do recommend the above method.
public class ApplyDataContractResolverAttribute : Attribute, IOperationBehavior
{
public ApplyDataContractResolverAttribute() { }
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) { }
public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
{
DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();
}
public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
{
DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();
}
public void Validate(OperationDescription description) { }
}
Edit: Also I think you can have setters in Data Contracts not be public, because I do it and it works fine :). But I would try making your setter public first, then solving the problem, then reverting to a protected setter, just so that you are dealing with as few variables at a time as possible.

Categories

Resources