How to share data between Service Project (WCF) and another project - c#

(Edited) -> I have a solution with different projects. It has a strange structure because I'm not an professional software engineer. The solution has three parts: one HMI project (WPF), bussiness project (class library type) and service project (WCF).
In the bussiness project, I do some process that has result which I save in a static class. This results are exposed in the screen by WPF project mentioned previously and now I need to transfer this data by one service placed in the WCF project.
The object is similar to:
[DataContract]
public class ObjectToTransfer
{
[DataMember]
public string ParameterOne = string.Empty;
[DataMember]
public string ParameterTwo = string.Empty;
}
In the other hand, the method that picks up the informtion from static class belonging to bussiness project is like this:
[ServiceBehavior]
public class Service: IService
{
public ObjectToTransfer SendObject()
{
return new ObjectToTransfer{
ParameterOne = BusinessProject.StaticClass.ResultOne,
ParameterTwo = BusinessProject.StaticClass.ResultTwo
};
}
Well, when I consume the service the result is a object with the parameters one and two empty. In additon, when I debug the solution with both projects run, the static class hasn't info in her atributes... It's like whether both projects run independently, in spite of both are under the same solutions..., It's like whether there was no relationship between them. Where is the mistake?
Thank you in advance!

The main thing you will need to do is change the DataContract class to a class with proper getters and setters.
[DataContract]
public class ObjectToTransfer
{
[DataMember]
public string ParameterOne { get; set; }
[DataMember]
public string ParameterTwo { get; set; }
}
The reason this is important is because of the way WCF works when it try to serialize the data from the server to the client. Essentially, the class on the server side schematically matches a generated class on the client. (They share the same class schema.) If there is no proper structure to the class, the data cannot get set. The members are basically read-only.
What you have in your class right now are public member variables, and they are set to always return string.Empty. WCF can't work with that, it is too strict. The server and the client need to be able to read and write to all DataMember properties, so you need the { get; set; }.
Don't forget to refresh your service reference after making this change.
If you need some good resources to learn WCF, check out these videos WCF Tutorial. They are a little dated, but all of the concepts still apply.

This might be late and I am new to WCF service, so I am sharing based on what I have done in my case to share data among different projects.
Background of my project:
I have a WCF service(host), a client and a server project. (A total of 3 different projects.) I will perform the necessary calculation in WCF service. Afterwards, I am suppose to share the result to the client and server. Therefore, there is this variable, variableA, that I have share among the 3 projects.
What I did:
In WCF service, I declare variableA and mark it static:
private **static** string variableA;
Only after I marked variableA as static, then I can see the same variableA across different projects.
I am not sure if this helps but this is what I did in my case.

Related

Access multiple projects from IIncrementalGenerator

I need to create code by the IIncrementalGenerator in at least two projects which are referring to the same library that references the SourceCodeGenerator project.
My solution, for further clarification:
MySolution
|->DesktopApp
| |->ref:Library
|
|->Library
| |->ref:SourceGenerator
|
|->WebApi
| |->ref:Library
|
|->SourceGenerator
In the library, I define Interfaces for all objects I use across my application, many properties I define in the interface are Ids which I mark by an attribute where I head the IType of the underlying object.
The attribute:
[System.AttributeUsage(System.AttributeTargets.Property)]
public class NavigationPropertyAttribute : System.Attribute
{
public System.Type NavigationPropertyType { get; }
public NavigationPropertyAttribute(System.Type type) => NavigationPropertyType = type;
}
This is how such an Interface looks like:
public interface IFoo
{
int Id { get; set; }
[NavigationProperty(typeof(IBar))]
int BarId { get; set; }
[NavigationProperty(typeof(IFoo))]
int ParentId { get; set; }
[NavigationProperty(typeof(IFoo))]
int ChildId { get; set; }
}
In both projects, one is a desktop application, the other is a Web API using EF Core, I have partial classes implementing these interfaces.
namespace IGTryout.Main;
public partial class Foo : IFoo
{
public int Id { get; set; }
public int BarId { get; set; }
public int ParentId { get; set; }
public int ChildId { get; set; }
}
what I need now, and where I'm struggling with, is using the IIncrementalGenerator to create a partial class with properties based on the Id, Name and the Type from the NavigationPropertyAttribute.
public partial class Foo
{
private Bar bar;
public Bar Bar => bar ??= GetValue<Bar>();
private Foo parent;
public Foo Parent => parent ??= GetValue<Foo>();
private Foo child;
public Foo Child => child ??= GetValue<Foo>();
}
(GetValue<T>() is an extension which gets the [CallerMemberName], resolves the matching Id via reflection and returns the object from cache by resolving it by the type and id)
In the Web API project, I'm also implementing the interfaces, yet, creating the NavigationProperties the common way
Foo { get; set; }
to fullfill EF Core's needs, also I'm creating the InversePropertyCollection in the related object for having the foreign keys set as needed by EF Core.
All of this is done, so I can use the interfaces as TransportObject with as little overhead as possible when sending them from the Web API to my desktop application and reverse.
Now to the problem I have:
to trigger all changes done to the interfaces, I reference my SourceCodeGenerator project from the library project, but by doing so, I can not find any opportunity to create the code inside the Web API assembly or the desktop app assembly.
I did solve this problem before by using the common SourceGenerator and referencing the SourceCodeGenerator project by both, the desktop app and the Web API, and accessing the interfaces inside my library project by calling the ReferencedAssemblySymbols of my compilations SourceModule and iterating over to the relevant project, since it is referenced by both, but this rather hacky solution is not possible, since would loose all the possibilities, which led me to switch to the IIncrementalGenerator in the first place.
Below is the answer to why this will not work with a Class Library project. however while tying up this answer I learned of the existence of Shared Projects which based on a cursory look at how they work, might enable the behavior of incremental generators on shared source code, see thoughts below.
The Problem
There are at 2 distinct reasons why what is asked in the question won't work with a Class Library project. One is to use source generator in a way they are not designed to work, and two, even if they worked as you wanted, the generated code would not behave as expected.
Source Generators hook to a compilation
By design, Source Generators hook to a compilation. The purpose of projects is to provide a distinct compilation unit. The use of solutions to group projects and order projects is a convivence, but does not cause the individual projects to merge into one compilation. Once a class library project is visible to the downstream projects, it is not longer a bunch of source code, it is just a dll.
Partial Classes in Different Projects
The example is making a partial class with the partials in three different projects, so even if you got the source generator to make the files in the correct project you would not end up with what you wanted, which is a WebApp version of Foo and and Desktop version of Foo. Instead you would end up with 3 versions of Foo the Library version with the Id Properties, the WebApp version with the auto properties for the navigations, and the Desktop version with the GetValue call. with both the desktop and webapp version missing the Id Properties. see this question/answer
Shared Projects
Can Shared Projects help here? Maybe. The intent of shared projects is to shared source code, not to be a compilation unit, so a change to the source should trigger the incremental generator in any project referencing that shared source, since that source is now part of that project's compilation.
You will want to be aware of the differences between a class library, and a shared project, and how that can impact your program, as now that code is being compiled multiple times in different contexts, so you can actually end up with completely different functionality from the same source. (i.e. features like global usings could cause namespace resolution to cause a Type to match by name to one in a completely different assembly, e.g. you have a property Document {get;set;} which in one project resolves to say AutoCAD.Document, but in another resolves to Microsoft.CodeAnalysis.Document.)

WCF is Returing Array of Objects of MyClass instead of Generic List

I have tried a lot of solutions from the internet. But I am not able to resolve this problem.
I have tried solutions from these links below:
WCF Proxy Returning Array instead of List EVEN THOUGH Collection Type == Generic.List
WCF Returning Array instead of List EVEN THOUGH Collection Type == Generic.List
Why does WCF return myObject[] instead of List<T> like I was expecting?
But I am not able to resolve it.
I have 2 copies of the same wcf service source code "WCFServiceApplication". One is hosted on LAN on IIS7 and Other one is am running locally in my solution.
The one which is hosted on IIS7 is working fine and returning list of objects. But the one on my local solution. I am trying to reference locally in solution to consume "WCFServiceApplication" functions in one of my local project named "MyProjectLibrary.csproj".
But here i have the problem. It is not giving me the Generic List of my custom class objects.
Any help is appreciated.
Thanks
I am Returning the List of below class:
[DataContract(IsReference = true)]
[KnownType(typeof(Joc))]
[KnownType(typeof(Stakeholder))]
[XmlInclude(typeof(JocStakeholder))]
public class JocStakeholder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public virtual Joc joc { get; set; }
[DataMember]
public virtual Stakeholder stakeholder { get; set; }
}
Joc and Stakeholder are 2 other classes. Actually, these are code first entities.
And My interface has this function:
[ServiceContract]
public interface IService
{
[OperationContract]
public IList<JocStakeholder> GetJocStakeHolders();
}
Change the Client's Service Reference Settings as follows
Right Click the Service Reference and Choose Configure Service Reference
Under the Data Type property settings, change the Collection Type value from Array to
System.Collections.Generic.List
This would give you the Generic List as a response
Specifies the list collection type for a WCF client. The default type is Array.
You can change it before adding service reference click Advance in Add Service Reference Dialog Box. Under Data Type beside Collection Type select in drownlist System.Collections.Generic.List.
I tried mostly all the method which i saw on the Stackoverflow.com and Internet. But i couldn't find one that could have done the job for me.
SO, finally, I found this article WCF the Right Wat by Miguel Castro and I implemented by services with the same architecture as the Miguel Sir is doing. The architecture resolved the problem.
But some programmers might not agree with the Architecture which is being used in that article because Model Classes project is being distributed to the clients as a DLL. Whereas in WCF usually, the Classes are exposed through the Service.

DisplayAttribute name property not working in Silverlight

I am binding DataGrid.ItemsSource property to the List<PersonDetails> object. I am getting datas through Silverlight-enabled WCF Service. So the PersonDetails class is implemented in Web Project. Each DataGrid's header text is changing as i want if the class is located in Silverlight project. But then I can not use this class in the web service. The only solution is to add same class in to the both of the projects. But, is there any other way?
The class looks like that:
[DataContract]
public class PersonGeneralDetails
{
// Properties
[DataMember]
[DisplayAttribute(Name = "Sira")]
public int RowNumber { get; set; }
[DataMember]
[DisplayAttribute(Name = "Seriyasi")]
public string SerialNumber { get; set; }
}
It seems attributes aren't generated in web project. I know that I can change header text using DataGrid events. But i want to make it work using attributes.
The problem is the WCF DataContract is an inter-operable mechanism that can be used across languages and platforms.
If you take a look to serialized data generated by the DataContractSerializer (or its code in System.Runtime.Serialization.dll, specifically InternalWriteObjectXyz() methods) you'll see that it merely serializes values into a simple XML message. Nothing related to .NET Framework will be there so all kind of attributes, both custom and compiler generated, will be stripped out and won't even received by the client.
It works creating a copy of your data and sending them from server to client, clients will then create a new class with the same signature. Note: a NEW CLASS with the same signature, NOT JUST A NEW OBJECT of the original class.
Of course there are some workaround for this. You may write your own serializer (see this post on SO for an example) or your own ISerializationSurrogate.
If you can deploy/share your assemblies to your clients you have a nice workaround: just deploy them and DataContractSerializer will build the right object on your clients (exactly the same one you had on the server, with all its attributes). Just remember that:
If custom attributes comes from run-time values (for example because of localization) then they'll be resolved on the client, not on the server (because attributes will be created on the client, their values won't be included in the XML message).
In the client application you need to add a reference to the assembly that contains your types.
When you add your service reference you have to instruct VS to use them (or it'll create proxies), in the Service Reference Settings dialog select Reuse types in referenced assemblies (you can limit this to only assemblies you want to share).

How do I stop a setter being exposed over web services?

I want to have an class like this,
public class Apple
{
public string Size { get; set;}
public string Colour { get; set;}
public string Shape { get; set;}
public int appleId { get; set;}
}
I want to expose that over web services using some web methods like this,
public void AddApple(Apple apple)
{
}
public Apple GetApple(int appleId)
{
}
So when I add a service reference in visual studio to that webservice I get client proxy objects generated for me allowing me to create an Apple on the client side and send that through to the webservice.
Is there a way for me to make one of those properties read only on the client side? I need the setters there so that I can set the values on the server side, but I want to control which data they can update on the client side.
Any ideas?
What I could do is pass in some of the data in the constructor, and only expose getters on the ones I want to be read only, but I want to use an object mapper on the server side. That means ideally I would want to leave the setters there.
In general, you cannot assume control over proxies generated at client side. So correct way would be to ignore values sent by client (or raise exception if he changes those values). The service documentation has to be explicitly mention such things.
Edit:
Yet another work-around would be to divide your data class into two classes - one non-editable by client (say Apple1) and another editable - say Apple2. So now service update method can only accept Apple2 so that client can know looking at generated proxy code what he can change. On server side, you can have Apple1 inherited from Apple2 to have complete data but I believe that proxy generated at client will/can anyway have two different unrelated classes. Perhaps better way in such case would be to have composite full AppleFull containing Apple1 and Apple2.
Please refer to the following question and its answers, I just skimmed through your question but I believe that it is a simillar problem as I faced -
WCF serialization and Value object pattern in Domain Driven Design
Another way to achieve that would be to share the assembly containing the Apple class (but no server side component) between server and client. Make the setters internal and mark the server side assemblies as friends using the InternalsVisibleTo attribute.
This will allow the server to use the setters but not the client.

.NET Web Service hydrate custom class

I am consuming an external C# Web Service method which returns a simple calculation result object like this:
[Serializable]
public class CalculationResult
{
public string Name { get; set; }
public string Unit { get; set; }
public decimal? Value { get; set; }
}
When I add a Web Reference to this service in my ASP .NET project Visual Studio is kind enough to generate a matching class so I can easily consume and work with it.
I am using Castle Windsor and I may want to plug in other method of getting a calculation result object, so I want a common class CalculationResult (or ICalculationResult) in my solution which all my objects can work with, this will always match the object returned from the external Web Service 1:1.
Is there anyway I can tell my Web Service client to hydrate a particular class instead of its generated one? I would rather not do it manually:
foreach(var fromService in calcuationResultsFromService)
{
ICalculationResult calculationResult = new CalculationResult()
{
Name = fromService.Name
};
yield return calculationResult;
}
Edit: I am happy to use a Service Reference type instead of the older Web Reference.
You can use http://automapper.codeplex.com. Typically it is used to simplify domain objects to DTO.
First of all, your web service - why do you actually GENERATE the classes? use common shared types.
http://www.codeproject.com/KB/WCF/WCFCollectionTypeSharing.aspx
This is obviously not an option when consuming a public web service, but as you control both ends... that has many advantages.
Among them complete code control ;)

Categories

Resources