Unable to read FaultException from an asp.net core connected service - wcf

I'm trying to catch the FaultException on a WCF client.
the generated reference contract is:
[System.ServiceModel.OperationContractAttribute(Action="urn:wcfname#method", ReplyAction="*")]
[System.ServiceModel.FaultContractAttribute(typeof(Error[]), Action= "urn:wcfname#method", Name="errors")]
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(Response))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(Request))]
service.response method(service.request request);
and the generated error class is:
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.2")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="wcf")]
public partial class Error
{
private string errorCodeField;
private string errorStringField;
private string subErrorCodeField;
private string offendingFieldField;
private System.DateTime timeStampField;
private bool timeStampFieldSpecified;
private string detailStringField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(DataType="integer", Order=0)]
public string errorCode
{
get
{
return this.errorCodeField;
}
set
{
this.errorCodeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=1)]
public string errorString
{
get
{
return this.errorStringField;
}
set
{
this.errorStringField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(DataType="integer", Order=2)]
public string subErrorCode
{
get
{
return this.subErrorCodeField;
}
set
{
this.subErrorCodeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=3)]
public string offendingField
{
get
{
return this.offendingFieldField;
}
set
{
this.offendingFieldField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=4)]
public System.DateTime timeStamp
{
get
{
return this.timeStampField;
}
set
{
this.timeStampField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool timeStampSpecified
{
get
{
return this.timeStampFieldSpecified;
}
set
{
this.timeStampFieldSpecified = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=5)]
public string detailString
{
get
{
return this.detailStringField;
}
set
{
this.detailStringField = value;
}
}
}
the code to call the sercie is below:
try
{
//call the service method
}
catch (FaultException<Error[]> ex)
{
var errorElement = XElement.Parse(ex.CreateMessageFault().GetReaderAtDetailContents().ReadOuterXml());
var xmlDetail = (string)errorElement;
}
catch (FaultException ex)
{
var errorElement = XElement.Parse(ex.CreateMessageFault().GetReaderAtDetailContents().ReadOuterXml());
var xmlDetail = (string)errorElement;
}
When theres a FaultExeption<Error[]> ex, it enters there, but the error is empty string.
If I comment that part, it enters on the FaultException ex, but again the error is empty string.
I'm using .netCore 3.1
Any advice is appreciated.

Throwing FaultException indicates that there is no problem with the channel. The exception was thrown by the service. WCF's FaultException message is too general to pinpoint a specific problem, so you can customize the exception message like this:
public class HomeService : IHomeService
{
public Student Get(string id)
{
try
{
//Here, of course, an exception will be thrown
var result = Convert.ToInt32(id) / Convert.ToInt32("0");
return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" };
}
catch (Exception ex)
{
var reason = new FaultReason("Exception thrown information");
var code = new FaultCode("500");
var faultException = new FaultException(reason, code, "It is GET that throws the exception");
throw faultException;
}
}
}

Related

Consume WSDL proxy class using c#

I have generated a proxy class but I don't know how to consume it.
I also instantiated the class and accessed the class but I don't know how to get the response
My Access code
Controller class to access the proxy :
ProjectByElementsQuerySelectionByElements query = new
ProjectByElementsQuerySelectionByElements();
ProjectByElementsQueryMessage_sync sync = new ProjectByElementsQueryMessage_sync{
ProjectSelectionByElements = query
};
Proxy Soap class :
This is the automatically generated from wsdl file and is a proxy class that I want to consume.
public partial class ProjectStatusByQueryResponse {
private ProjectLifeCycleStatusCode projectLifeCycleStatusCodeField;
private UPTODATEOUTOFDATE_UpToDatenessStatusCode schedulingUpToDatenessStatusCodeField;
private StartingStatusCode startingStatusCodeField;
private ProjectTaskLifeCycleStatusCode taskLifeCycleStatusCodeField;
private BlockingStatusCode taskBlockingStatusCodeField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectLifeCycleStatusCode ProjectLifeCycleStatusCode {
get {
return this.projectLifeCycleStatusCodeField;
}
set {
this.projectLifeCycleStatusCodeField = value;
}
}
public partial class ProjectByElementsQuerySelectionByElements {
private ProjectSelectionByResponsibleUnitID[] selectionByResponsibleUnitIDField;
private ProjectSelectionByProjectId[] selectionByProjectIDField;
private ProjectSelectionByProjectName[] selectionByProjectNameField;
private ProjectSelectionByProjectType[] selectionByProjectTypeField;
private ProjectSelectionByCustomerID[] selectionByCustomerIDField;
private ProjectSelectionByRequestingUnitID[] selectionByRequestingUnitIDField;
private bool selectionByBillableIndicatorField;
private bool selectionByBillableIndicatorFieldSpecified;
private ProjectSelectionByInterCompanyIndicator[] selectionByIntercompanyIndicatorField;
private ProjectSelectionByResponsibleEmployeeID[] selectionByResponsibleEmployeeIDField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByResponsibleUnitID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByResponsibleUnitID[] SelectionByResponsibleUnitID {
get {
return this.selectionByResponsibleUnitIDField;
}
set {
this.selectionByResponsibleUnitIDField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByProjectID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByProjectId[] SelectionByProjectID {
get {
return this.selectionByProjectIDField;
}
set {
this.selectionByProjectIDField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByProjectName", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByProjectName[] SelectionByProjectName {
get {
return this.selectionByProjectNameField;
}
set {
this.selectionByProjectNameField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByProjectType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByProjectType[] SelectionByProjectType {
get {
return this.selectionByProjectTypeField;
}
set {
this.selectionByProjectTypeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByCustomerID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByCustomerID[] SelectionByCustomerID {
get {
return this.selectionByCustomerIDField;
}
set {
this.selectionByCustomerIDField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByRequestingUnitID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByRequestingUnitID[] SelectionByRequestingUnitID {
get {
return this.selectionByRequestingUnitIDField;
}
set {
this.selectionByRequestingUnitIDField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public bool SelectionByBillableIndicator {
get {
return this.selectionByBillableIndicatorField;
}
set {
this.selectionByBillableIndicatorField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool SelectionByBillableIndicatorSpecified {
get {
return this.selectionByBillableIndicatorFieldSpecified;
}
set {
this.selectionByBillableIndicatorFieldSpecified = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByIntercompanyIndicator", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByInterCompanyIndicator[] SelectionByIntercompanyIndicator {
get {
return this.selectionByIntercompanyIndicatorField;
}
set {
this.selectionByIntercompanyIndicatorField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByResponsibleEmployeeID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByResponsibleEmployeeID[] SelectionByResponsibleEmployeeID {
get {
return this.selectionByResponsibleEmployeeIDField;
}
set {
this.selectionByResponsibleEmployeeIDField = value;
}
}
}
The proxy class file is added to the project to instantiate the proxy class to call the wcf service. If you don’t understand how to call the wcf service in this way, you can refer to ServiceModel Metadata Utility Tool.
In addition, you can also call the wcf service by adding a service reference. This is the simplest way. Right-click the project and select addservicereference in add, and then add the service reference.

I Cant Return List Type from Wcf

[DataContract]
public class UserDetails
{
string _userid;
string tckimlik;
string ad;
string tarih;
string aciklama;
[DataMember]
public string userid
{
get { return _userid; }
set { _userid = value; }
}
[DataMember]
public string Tckimlik
{
get { return tckimlik; }
set { tckimlik = value; }
}
[DataMember]
public string Ad
{
get { return ad; }
set { ad = value; }
}
[DataMember]
public string Tarih
{
get { return tarih; }
set { tarih = value; }
}
[DataMember]
public string Aciklama
{
get { return aciklama; }
set { aciklama = value; }
}
}
public interface IService1
{
[OperationContract]
List<UserDetails> GetAllPersons();
}
My Class DataContract and OperationContract given above.
My Method GetAllPersons type of List beloew.
public List<UserDetails> GetAllPersons()
{
List<UserDetails> userL = new List<UserDetails>();
try
{
NpgsqlConnection con = new NpgsqlConnection("");
con.Open();
NpgsqlCommand cmd = new NpgsqlCommand("SELECT * FROM RegistrationTable",con);
NpgsqlDataAdapter sda = new NpgsqlDataAdapter(cmd);
NpgsqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
UserDetails us = new UserDetails()
{
userid = reader[0].ToString(),
Tckimlik = reader[1].ToString(),
Ad = reader[2].ToString(),
Tarih = reader[3].ToString(),
Aciklama = reader[4].ToString()
};
userL.Add(us);
}
con.Close();
return userL;
}
catch (Exception)
{
throw;
}
}
The Code above is in WCF Service. I want to call GetAllPersons method in Xamarin. But i have trouble with it. My Xamarin code is below.
void getVisitor()
{
List<ServiceReference1.UserDetails> getir = new List<ServiceReference1.UserDetails>();
getir = service.GetAllPersonsAsync();
}
When i wanted to call method in service, i get an error given below.
Severity Code Description Project File Line Suppression State
Error CS0029 Cannot implicitly convert type 'System.Threading.Tasks.Task<ServiceReference1.GetAllPersonsResponse>' to 'System.Collections.Generic.List<ServiceReference1.UserDetails>' XamarinCRUD C:\Users\Eyyub\source\repos\XamarinCRUD\XamarinCRUD\XamarinCRUD\MainPage.xaml.cs 59 Active
Who can hint me, appreciate it!
Best for all!
I also checked logs and no show. Some says that WCF doesnt (de)serialize List return type. Is it true? if yes, what is the solution?. When i used same service through FormApp, works fine. But Xamarin doesnt..

How can I create filter for UnauthorizedAccessException

I want to create filter for UnauthorizedAccessException I ddon't know why it is not work in my main filter
at first I try this in my ApiExceptionFilter : ExceptionFilterAttribute but doesn't worked here
private void HandleUnauthorizedAccessException(ExceptionContext context)
{
var exception = context.Exception as UnauthorizedAccessException;
var details = new ProblemDetails
{
Status = StatusCodes.Status401Unauthorized,
Title = "nauthorized exception",
Detail = exception.Message
};
context.Result = new ObjectResult(details)
{
StatusCode = StatusCodes.Status401Unauthorized
};
context.ExceptionHandled = true;
}
every other my exceptions work here instead of this
after that I try get that here but not successful again :
public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
{
public int Order { get; set; } = int.MaxValue - 10;
public void OnActionExecuting(ActionExecutingContext context) { }
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception is HttpResponseException exception)
{
context.Result = new ObjectResult(exception.Value)
{
StatusCode = exception.Status,
};
context.ExceptionHandled = true;
}
}
}
I dont know why only this exception not worked after request send to api :(

How to make queries to a WCF service with EF model / data first

I have a client project and a MVC3 EF model first project that exposes a WCF service to the client. I am new to EF and some things are not so clear.
Lets assume the Northwind service with Product entity, if i want to make a simple query like return the products with a specific price, where do i write the code for the query ?
On the server side there is only EDM and the service itself and it is pretty straighforward, hardly any code there.
On the client i have Product model class and NorthwindContext and also a ProdcutsPageViewModel class (it's a MVVM project)
So my question is, How and where do i make queries to the DB ? do i make it from the client side by attaching LINQ code to the URI ? do i make it from the server side by adding new methods to the service ? I would appreciate if you could explain it to a newbie,
Thanks !
Here is some of the code :
On the server, the NorthwindODataService service class:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class NorthwindODataService : DataService<NorthwindEntities>
{
private readonly IUserPrivilegesRepository userPrivilegesRepository;
private readonly IClaimsIdentity identity;
public NorthwindODataService()
: this(HttpContext.Current.User.Identity as IClaimsIdentity, new InfrastructureEntities())
{
}
public NorthwindODataService(IClaimsIdentity identity, IUserPrivilegesRepository userPrivilegesRepository)
{
this.identity = identity;
this.userPrivilegesRepository = userPrivilegesRepository;
}
protected string UserId
{
get
{
var nameIdentifierClaim = this.identity.Claims.SingleOrDefault(c => c.ClaimType == ClaimTypes.NameIdentifier);
if (nameIdentifierClaim == null)
{
throw new DataServiceException(401, "Unauthorized", "The request requires authentication.", "en-US", null);
}
return nameIdentifierClaim.Value;
}
}
/// <summary>
/// Initializes service-wide policies. This method is called only once.
/// </summary>
/// <param name="config"></param>
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Products", EntitySetRights.All);
config.SetEntitySetAccessRule("Categories", EntitySetRights.AllRead);
config.SetEntitySetAccessRule("Suppliers", EntitySetRights.AllRead);
config.SetEntitySetPageSize("Products", 20);
config.SetEntitySetPageSize("Categories", 20);
config.SetEntitySetPageSize("Suppliers", 20);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.UseVerboseErrors = true;
}
/// <summary>
/// Define a query interceptor for the Products entity set.
/// </summary>
/// <returns></returns>
[QueryInterceptor("Products")]
public Expression<Func<Product, bool>> OnQueryProducts()
{
this.ValidateAuthorization("Products", PrivilegeConstants.SqlReadPrivilege);
// The user has Read permission.
return p => true;
}
/// <summary>
/// Define a query interceptor for the Categories entity set.
/// </summary>
/// <returns></returns>
[QueryInterceptor("Categories")]
public Expression<Func<Category, bool>> OnQueryCategories()
{
this.ValidateAuthorization("Categories", PrivilegeConstants.SqlReadPrivilege);
// The user has Read permission.
return p => true;
}
/// <summary>
/// Define a query interceptor for the Suppliers entity set.
/// </summary>
/// <returns></returns>
[QueryInterceptor("Suppliers")]
public Expression<Func<Supplier, bool>> OnQuerySuppliers()
{
this.ValidateAuthorization("Suppliers", PrivilegeConstants.SqlReadPrivilege);
// The user has Read permission.
return p => true;
}
/// <summary>
/// Define a change interceptor for the Products entity set.
/// </summary>
/// <param name="product"></param>
/// <param name="operations"></param>
[ChangeInterceptor("Products")]
public void OnChangeProducts(Product product, UpdateOperations operations)
{
if (operations == UpdateOperations.Change)
{
this.ValidateAuthorization("Products", PrivilegeConstants.SqlUpdatePrivilege);
var entry = default(ObjectStateEntry);
if (this.CurrentDataSource.ObjectStateManager.TryGetObjectStateEntry(product, out entry))
{
// Reject changes to a discontinued Product.
// Because the update is already made to the entity by the time the
// change interceptor in invoked, check the original value of the Discontinued
// property in the state entry and reject the change if 'true'.
if ((bool)entry.OriginalValues["Discontinued"])
{
throw new DataServiceException(400, "Bad Request", "A discontinued product cannot be modified.", "en-US", null);
}
}
else
{
throw new DataServiceException(404, "Not Found", "The requested product could not be found in the data source.", "en-US", null);
}
}
else if (operations == UpdateOperations.Add)
{
this.ValidateAuthorization("Products", PrivilegeConstants.SqlCreatePrivilege);
}
else if (operations == UpdateOperations.Delete)
{
this.ValidateAuthorization("Products", PrivilegeConstants.SqlDeletePrivilege);
var entry = default(ObjectStateEntry);
if (this.CurrentDataSource.ObjectStateManager.TryGetObjectStateEntry(product, out entry))
{
// Only a discontinued Product can be deleted.
if (!(bool)entry.OriginalValues["Discontinued"])
{
throw new DataServiceException(400, "Bad Request", "Products that are not discontinued cannot be deleted.", "en-US", null);
}
}
else
{
throw new DataServiceException(404, "Not Found", "The requested product could not be found in the data source.", "en-US", null);
}
}
}
private static string BuildMessage(string entitySetName, string privilege)
{
var message = string.Empty;
switch (privilege)
{
case PrivilegeConstants.SqlCreatePrivilege:
message = string.Format(CultureInfo.InvariantCulture, "You are not authorized to create new rows in the {0} entity set.", entitySetName);
break;
case PrivilegeConstants.SqlReadPrivilege:
message = string.Format(CultureInfo.InvariantCulture, "You are not authorized to query the {0} entity set.", entitySetName);
break;
case PrivilegeConstants.SqlUpdatePrivilege:
message = string.Format(CultureInfo.InvariantCulture, "You are not authorized to update rows in the {0} entity set.", entitySetName);
break;
case PrivilegeConstants.SqlDeletePrivilege:
message = string.Format(CultureInfo.InvariantCulture, "You are not authorized to delete rows in the {0} entity set.", entitySetName);
break;
default:
message = string.Format(CultureInfo.InvariantCulture, "You are not authorized to access the {0} entity set.", entitySetName);
break;
}
return message;
}
private void ValidateAuthorization(string entitySetName, string privilege)
{
if (!this.userPrivilegesRepository.HasUserPrivilege(this.UserId, privilege))
{
// The user does not have Read permission.
throw new DataServiceException(401, "Unauthorized", BuildMessage(entitySetName, privilege), "en-US", null);
}
}
}
}
on the client, the Product model class:
[EntitySetAttribute("Products")]
[DataServiceKeyAttribute("ProductID")]
public class Product : INotifyPropertyChanged
{
private int id;
private string productName;
private int? supplierID;
private int? categoryID;
private string quantityPerUnit;
private decimal? unitPrice;
private short? unitsInStock;
private short? unitsOnOrder;
private short? reorderLevel;
private bool discontinued;
public event PropertyChangedEventHandler PropertyChanged;
public int ProductID
{
get
{
return this.id;
}
set
{
this.id = value;
this.OnPropertyChanged("ProductID");
}
}
public string ProductName
{
get
{
return this.productName;
}
set
{
this.productName = value;
this.OnPropertyChanged("ProductName");
}
}
public int? SupplierID
{
get
{
return this.supplierID;
}
set
{
this.supplierID = value;
this.OnPropertyChanged("SupplierID");
}
}
public int? CategoryID
{
get
{
return this.categoryID;
}
set
{
this.categoryID = value;
this.OnPropertyChanged("CategoryID");
}
}
public string QuantityPerUnit
{
get
{
return this.quantityPerUnit;
}
set
{
this.quantityPerUnit = value;
this.OnPropertyChanged("QuantityPerUnit");
}
}
public decimal? UnitPrice
{
get
{
return this.unitPrice;
}
set
{
this.unitPrice = value;
this.OnPropertyChanged("UnitPrice");
}
}
public short? UnitsInStock
{
get
{
return this.unitsInStock;
}
set
{
this.unitsInStock = value;
this.OnPropertyChanged("UnitsInStock");
}
}
public short? UnitsOnOrder
{
get
{
return this.unitsOnOrder;
}
set
{
this.unitsOnOrder = value;
this.OnPropertyChanged("UnitsOnOrder");
}
}
public short? ReorderLevel
{
get
{
return this.reorderLevel;
}
set
{
this.reorderLevel = value;
this.OnPropertyChanged("ReorderLevel");
}
}
public bool Discontinued
{
get
{
return this.discontinued;
}
set
{
this.discontinued = value;
this.OnPropertyChanged("Discontinued");
}
}
public static Product CreateProduct(int productID, string productName, bool discontinued)
{
return new Product
{
ProductID = productID,
ProductName = productName,
Discontinued = discontinued,
};
}
protected virtual void OnPropertyChanged(string changedProperty)
{
var propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(changedProperty));
}
}
}
}
And the NorthwindContext class:
public class NorthwindContext : DataServiceContext
{
public NorthwindContext(Uri serviceRoot)
: base(serviceRoot)
{
this.MergeOption = MergeOption.OverwriteChanges;
this.SaveChangesDefaultOptions = SaveChangesOptions.ContinueOnError;
}
public void AddToCategories(Category category)
{
this.AddObject("Categories", category);
}
public void AddToProducts(Product product)
{
this.AddObject("Products", product);
}
public void AddToSuppliers(Supplier supplier)
{
this.AddObject("Suppliers", supplier);
}
public void AttachToCategories(Category category)
{
this.AttachTo("Categories", category);
}
public void AttachToProducts(Product product)
{
this.AttachTo("Products", product);
}
public void AttachToSuppliers(Supplier supplier)
{
this.AttachTo("Suppliers", supplier);
}
}
}
and here is the ProductsPageViewModel class :
public class ProductsPageViewModel : ListViewModel<Product>
{
public ProductsPageViewModel()
: this(Deployment.Current.Dispatcher, App.CloudClientFactory.ResolveNorthwindContext())
{
}
public ProductsPageViewModel(Dispatcher dispatcher, NorthwindContext northwindContext)
: base(dispatcher, northwindContext)
{
}
protected override string EntitySetName
{
get
{
return "Products";
}
}
}
}
When you are working with EF, it doesn't suppose any direct queries to the database. Spent some time reading what is Domain Driven Design. There is described strategy Persistent Ignorance, which means, that when you are designing your system you should not consider data storage at all. Work only with the model. Data Mapper pattern describes the place where all the things related with the database should be done. Here is good article, which I think can help you.
Another advice, do not forget that MVC supports restful style of Web Development. Leveraging these capabilities should be helpful in your design.
paramosh,
So are you able to explain it to a newbie or not? I just went to that website, and read through that page, but it wasn't really much help for trying to lean the EF network (it's mainly reinforcing how I did things prior to EF).

Can Client of a WCF Duplex Service(TCP binding) send and recive at the same time?

My code at the moment looks like this:
Server side:
#region IClientCallback interface
interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveWcfElement(WcfElement wcfElement);
}
#endregion
#region IService interface
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]
interface IService
{
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
void ReadyToReceive(string userName, int source, string ostatniTypWiadomosci);
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)]
bool SendWcfElement(WcfElement wcfElement);
[OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]
List<int> Login(Client name, string password, bool isAuto, bool isSuperMode);
}
#endregion
#region Public enums/event args
public delegate void WcfElementsReceivedFromClientEventHandler(object sender, WcfElementsReceivedFromClientEventArgs e);
public class WcfElementsReceivedFromClientEventArgs : EventArgs
{
public string UserName;
}
public class ServiceEventArgs : EventArgs
{
public WcfElement WcfElement;
public Client Person;
}
#endregion
#region Service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service : IService
{
#region Instance fields
//thread sync lock object
private static readonly Object SyncObj = new Object();
//callback interface for clients
IClientCallback _callback;
//delegate used for BroadcastEvent
public delegate void ChatEventHandler(object sender, ServiceEventArgs e);
public static event ChatEventHandler ChatEvent;
private ChatEventHandler _myEventHandler;
//holds a list of clients, and a delegate to allow the BroadcastEvent to work
//out which chatter delegate to invoke
static readonly Dictionary<Client, ChatEventHandler> Clients = new Dictionary<Client, ChatEventHandler>();
//current person
private Client _client;
#endregion
#region Helpers
private bool CheckIfPersonExists(string name)
{
return Clients.Keys.Any(p => p.UserName.Equals(name, StringComparison.OrdinalIgnoreCase));
}
private ChatEventHandler getPersonHandler(string name)
{
foreach (var c in Clients.Keys.Where(c => c.UserName.Equals(name, StringComparison.OrdinalIgnoreCase)))
{
ChatEventHandler chatTo;
Clients.TryGetValue(c, out chatTo);
return chatTo;
}
return null;
}
private Client GetPerson(string name)
{
return Clients.Keys.FirstOrDefault(c => c.UserName.Equals(name, StringComparison.OrdinalIgnoreCase));
}
#endregion
#region IService implementation
public List<int> Login(Client client, string password, bool isAuto, bool isSuperMode)
{
if (client.ElementsVersions == null)
{
client.ElementsVersions = new WcfElement(WcfElement.RodzajWiadomosci.VersionControl, client.UserName);
}
//create a new ChatEventHandler delegate, pointing to the MyEventHandler() method
_myEventHandler = MyEventHandler;
lock (SyncObj)
{
if (!CheckIfPersonExists(client.UserName))
{
_client = client;
Clients.Add(client, _myEventHandler);
}
else
{
_client = client;
foreach (var c in Clients.Keys.Where(c => c.UserName.Equals(client.UserName)))
{
ChatEvent -= Clients[c];
Clients.Remove(c);
break;
}
Clients[client] = _myEventHandler;
}
_client.LockObj = new object();
}
_callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
ChatEvent += _myEventHandler;
var rValue = isAuto ? bazaDanych.Login(client.UserName, isSuperMode) : bazaDanych.Login(client.UserName, password);
return rValue;
}
public void PerformDataSync(Client c)
{
WcfElement wcfDelete = null;
WcfElement wcfUpdate = null;
//...
//this method prepares elements for client
//when done it adds them to clients queue (List<WcfElement)
try
{
var counter = 0;
if (wcfDelete != null)
{
foreach (var wcf in WcfElement.SplitWcfElement(wcfDelete, false))//split message into small ones
{
c.AddElementToQueue(wcf, counter++);
}
}
if (wcfUpdate != null)
{
foreach (var wcf in WcfElement.SplitWcfElement(wcfUpdate, true))
{
c.AddElementToQueue(wcf, counter++);
}
}
SendMessageToGui(string.Format("Wstępna synchronizacja użytkownika {0} zakończona.", c.UserName));
c.IsSynchronized = true;
}
catch (Exception e)
{
}
}
private void SendMessageToClient(object sender, EventArgs e)
{
var c = (Client) sender;
if (c.IsReceiving || c.IsSending)
{
return;
}
c.IsReceiving = true;
var wcfElement = c.GetFirstElementFromQueue();
if (wcfElement == null)
{
c.IsReceiving = false;
return;
}
Clients[c].Invoke(this, new ServiceEventArgs { Person = c, WcfElement = wcfElement });
}
public void ReadyToReceive(string userName)
{
var c = GetPerson(userName);
c.IsSending = false;
c.IsReceiving = false;
if (c.IsSynchronized)
{
SendMessageToClient(c, null);
}
else
{
PerformDataSync(c);
}
}
public bool SendWcfElement(WcfElement wcfElement)
{
var cl = GetPerson(wcfElement.UserName);
cl.IsSending = true;
if (wcfElement.WcfElementVersion != bazaDanych.WcfElementVersion) return false;
//method processes messages and if needed creates creates WcfElements which are added to every clients queue
return ifSuccess;
}
#endregion
#region private methods
private void MyEventHandler(object sender, ServiceEventArgs e)
{
try
{
_callback.ReceiveWcfElement(e.WcfElement);
}
catch (Exception ex)
{
}
}
#endregion
}
#endregion
Client side in a moment
#region Client class
[DataContract]
public class Client
{
#region Instance Fields
/// <summary>
/// The UserName
/// </summary>
[DataMember]
public string UserName { get; set; }
[DataMember]
public WcfElement ElementsVersions { get; set; }
private bool _isSynchronized;
public bool IsSynchronized
{
get { return _isSynchronized; }
set
{
_isSynchronized = value;
}
}
public bool IsSending { get; set; }
public bool IsReceiving { get; set; }
private List<WcfElement> ElementsQueue { get; set; }
public object LockObj { get; set; }
public void AddElementToQueue(WcfElement wcfElement, int position = -1)
{
try
{
lock (LockObj)
{
if (ElementsQueue == null) ElementsQueue = new List<WcfElement>();
if (position != -1 && position <= ElementsQueue.Count)
{
try
{
ElementsQueue.Insert(position, wcfElement);
}
catch (Exception e)
{
}
}
else
{
try
{
//dodaje na koncu
ElementsQueue.Add(wcfElement);
}
catch (Exception e)
{
}
}
}
}
catch (Exception e)
{
}
}
public WcfElement GetFirstElementFromQueue()
{
if (ElementsQueue == null) return null;
if (ElementsQueue.Count > 0)
{
var tmp = ElementsQueue[0];
ElementsQueue.RemoveAt(0);
return tmp;
}
return null;
}
#endregion
#region Ctors
/// <summary>
/// Assign constructor
/// </summary>
/// <param name="userName">The userName to use for this client</param>
public Client(string userName)
{
UserName = userName;
}
#endregion
}
#endregion
ProxySingletion:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
public sealed class ProxySingleton : IClientCallback
{
#region Instance Fields
private static ProxySingleton _singleton;
public static bool IsConnected;
private static readonly object SingletonLock = new object();
private ServiceProxy _proxy;
private Client _myPerson;
private delegate void HandleDelegate(Client[] list);
private delegate void HandleErrorDelegate();
//main proxy event
public delegate void ProxyEventHandler(object sender, ProxyEventArgs e);
public static event ProxyEventHandler ProxyEvent;
//callback proxy event
public delegate void ProxyCallBackEventHandler(object sender, ProxyCallBackEventArgs e);
public static event ProxyCallBackEventHandler ProxyCallBackEvent;
#endregion
#region Ctor
/// <summary>
/// Blank constructor
/// </summary>
private ProxySingleton()
{
}
#endregion
#region Public Methods
#region IClientCallback implementation
public void ReceiveWcfElement(WcfElement wcfElement)
{
//process received data
//...
ReadyToReceive();
}
#endregion
public void ReadyToReceive()
{
try
{
if (bazaDanych.Dane.Client.IsSending) return;
var w = bazaDanych.Dane.Client.GetFirstElementFromQueue();
if (w != null)
{
SendWcfElement(w);
return;
}
_proxy.ReadyToReceive(bazaDanych.Dane.Client.UserName, source, ostatniTypWiadomosci);
}
catch (Exception)
{
IsConnected = false;
}
}
public static WcfElement CurrentWcfElement;
public bool SendWcfElement(WcfElement wcfElement)
{
if (bazaDanych.Dane.Client.IsReceiving)
{
bazaDanych.Dane.Client.AddElementToQueue(wcfElement);
return true;
}
bazaDanych.Dane.Client.IsSending = true;
foreach (var wcfElementSplited in WcfElement.SplitWcfElement(wcfElement, true))
{
CurrentWcfElement = wcfElementSplited;
try
{
var r = _proxy.SendWcfElement(wcfElementSplited);
CurrentWcfElement = null;
}
catch (Exception e)
{
IsConnected = false;
return false;
}
}
bazaDanych.Dane.Client.IsSending = false;
ReadyToReceive();
return true;
}
public void ListenForConnectOrReconnect(EventArgs e)
{
SendWcfElement(WcfElement.GetVersionElement());//send wcfelement for perform PerformDataSync
ReadyToReceive();
}
public static bool IsReconnecting;
public bool ConnectOrReconnect(bool shouldRaiseEvent = true)
{
if (IsReconnecting)
{
return IsConnected;
}
if (IsConnected) return true;
IsReconnecting = true;
bazaDanych.Dane.Client.IsReceiving = false;
bazaDanych.Dane.Client.IsSending = false;
bazaDanych.Dane.Client.IsSynchronized = false;
try
{
var site = new InstanceContext(this);
_proxy = new ServiceProxy(site);
var list = _proxy.Login(bazaDanych.Dane.Client, bazaDanych.Dane.UserPassword, bazaDanych.Dane.UserIsAuto, bazaDanych.Dane.UserIsSuperMode);
bazaDanych.Dane.UserRights.Clear();
bazaDanych.Dane.UserRights.AddRange(list);
IsConnected = true;
if (shouldRaiseEvent) ConnectOrReconnectEvent(null);
}
catch (Exception e)
{
IsConnected = false;
}
IsReconnecting = false;
return IsConnected;
}
}
#endregion
At the moment my app works like this:
After successful login every client sends WcfElements(which contains bunch of list with ids and versions of elements). Then it sends ReadyToReceive one way message which after login fires performsync method. That method prepares data for client and sends first of them using one way receive method. IF there is more than one wcfelement to send then only last one is marked as last. Client responds with ReadyToReceive after every successful receive from Server. All up to this point works quite well. Problem starts later. Mostly packages are lost (method receiveWcfElement). Server has marked that client is receiving and maybe processing message and is waitng for readytoreceive packet, which will never be send because of lost element.
I've made it like this because as far as I know client can't send and receive at the same time. I've tried this and got this problem:
If client send wcfElement with SendWcfElement method and server due to processing this element created another element which was supposed to be ssend back to client then client whoud have faulted proxy if callback was send before sendWcfElement returned true indicating that method was completed.
Now I wonder if it is possible for client to send and receive at the same time using two way methods ?
I ended up with to services(two connections). One for connection from client to server and another with callback which handles connection from server to client.

Categories