I tried to implement WCF duplex service for my test automation. call is happening fine to server but it is not calling back to client method. I have already gone through other blogs and stacoverflow also. using Isoneway property as true, set Concurrancy mode as reentrant and usesynchronizationcontext as false also. previously it was working fine. i don't know what is causing this issue.
In this service contract, callback is added
[ServiceContract(CallbackContract=typeof(ICollaborationServiceCallBack),SessionMode=SessionMode.Required)]
public interface ICollaborationInfrastructureService
{
In test , initializing the proxy
[TestFixture]
[CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Reentrant,IncludeExceptionDetailInFaults=true)]
public class SAF_TestCollaboration : TestFixtureBase, ICollaborationInfrastructureServiceCallback
{
private SfdTestSteps steps = null;
private CollaborationInfrastructureServiceClient client = null;
private SfdTask task = null;
protected override void TestFixtureSetupBegin()
{
base.TestFixtureSetupBegin();
steps = new SfdTestSteps();
task = new SfdTask();
PrepareEnvironment();
client = new CollaborationInfrastructureServiceClient(new InstanceContext(this));
}
public void Login(string username,string password)
{
TestMonitor.Do("xyz","xyz", "xyz",
() =>
{
xyzzzzz.....
OperationContext.Current.GetCallbackChannel<ICollaborationServiceCallBack>().DoLoginIn("xyz", "xyz");
});
}
Method is implemented in test side...
public void DoLoginIn(string username, string password)
{
steps.Login.UserName = username;
steps.Login.Password = password;
steps.Login.DoLogin();
}
The problem is that OperationContext.Current is bound to the thread. If you switch to a different thread OperationContext.Current would be null.
Try this:
public void Login(string username,string password)
{
var callbackChannel = OperationContext.Current.GetCallbackChannel<ICollaborationServiceCallBack>();
TestMonitor.Do("xyz","xyz", "xyz",
() =>
{
xyzzzzz.....
callbackChannel.DoLoginIn("xyz", "xyz");
});
}
Related
I have a ASP NET Core application that will serve as a RabbitMQ producer.I have read the tutorial and guides regarding the RabbitMQ .NET client and i still do not know how to deal with the channel lifetime and concurrent access.
From what i have read i understood the following:
IConnection is threadsafe ,but is costly to create
IModel is not threadsafe but is lightweight
For the IConnection i would initialize it in the Startup and inject it as a singleton (service).
However i I do not know how to deal with IModel management.Lets say i have a couple of services that use it, is it scalable to just :
Solution 1
public void Publish(IConnection connection)
{
using(IModel model=connection.CreateChannel())
{
model.BasicPublish(...);
}
}
Solution 2
From what i have read , i understood that its not really scalable.
So another solution would be to create a separate service which would contain a loop , a ConcurrentQueue, and all services would dispatch messages here.
This service would be the sole publisher to RabbitMQ
Publisher
public class Publisher
{
private CancellationTokenSource tcs=new CancellationTokenSource();
public BlockingCollection<byte[]> messages=new BlockingCollection<byte[]>();
private IModel channel;
private readonly string ExchangeName;
private Task loopTask;
public void Run()
{
this.loopTask=Task.Run(()=>Loop(tcs.Token),tcs.Token);
}
private void Loop(Cancellation token)
{
while(true)
{
token.ThrowIfCancellationRequested();
queue.Take(out byte[]data);
channel.BasicPublish(...,body:data);
}
}
public void Publish(byte[] message)
{
this.queue.Add(message);
}
}
Usage
public class SomeDIService
{
private IConnection connection;
SomeDIService(Publisher publisher)
{
this.publisher=publisher;
}
public void DoSomething(byte[] data)
{
//do something...
this.publisher.Publish(data);
}
}
I would prefer solution 1 but i do not know the performance penalty ,while i do not like solution 2 since i wanted to just publish messages directly to RabbitMQ.Now i have to deal with this long running Task too.
Is there any other solution , am i missing something ? Is there a simpler way?
Update
I mentioned concurrent access.I meant i need a way to publish messages from multiple endpoints (services) to RabbitMQ.
Real scenario
public class Controller1:Controller
{
private SomeDIService service; //uses Publisher
[HttpGet]
public void Endpoint1()
{
this.service.DoSomething();
}
[HttpPost]
public void Endpoint2()
{
this.service.DoSomething();
}
}
public class Controller2:Controller
{
private SomeDIService service;
[HttpGet]
public void Endpoint3()
{
this.service.DoSomething();
}
[HttpPost]
public void Endpoint4()
{
this.service.DoSomething();
}
}
after searching for long time i found this solution and it works very good for me
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace BSG.MessageBroker.RabbitMQ
{
public class Rabbit : IRabbit
{
private readonly EnvConfigModel EnvConfig;
private readonly string _hostname;
private readonly string _password;
private readonly string _exchangeName;
private readonly string _username;
private IConnection _connection;
private IModel _Model;
public Rabbit(IOptions<EnvConfigModel> appSettings)
{
EnvConfig = appSettings.Value;
_Logger = services;
_exchangeName = EnvConfig.Rabbit_ExchangeName;
_hostname = EnvConfig.Rabbit_Host;
_username = EnvConfig.Rabbit_Username;
_password = EnvConfig.Rabbit_Password;
CreateConnection();
_Model = _connection.CreateModel();
}
private void CreateConnection()
{
try
{
var factory = new ConnectionFactory
{
HostName = _hostname,
UserName = _username,
Password = _password,
AutomaticRecoveryEnabled = true,
TopologyRecoveryEnabled = true,
NetworkRecoveryInterval = TimeSpan.FromSeconds(3)
};
_connection = factory.CreateConnection();
}
catch (Exception ex)
{
Console.WriteLine($"Could not create connection: {ex.Message}");
}
}
private bool ConnectionExists()
{
if (_connection != null)
{
return true;
}
CreateShredderConnection();
return _connection != null;
}
public bool PushToQueue(string Message)
{
try
{
if (ConnectionExists())
{
byte[] body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(Message));
_Model.BasicPublish(exchange: _exchangeName,
routingKey: 1001,
basicProperties: null,
body: body);
}
return true;
}
catch (Exception ex)
{
return false;
}
}
}
}
Working on building signalR hub, I'm able to get data from hub to the client but I'm, not sure how do I push it every 1 second.
I'm not sure where do I set the timer in the controller where getApps method exists or in the hub?
Hub:
public class nphub : Hub
{
public readonly sbController _sbcontroller;
public nphub(sbController sbcontroller)
{
_sbcontroller = sbcontroller;
}
public async Task NotifyConnection()
{
IActionResult result = await _sbcontroller.getApps();
await Clients.All.SendAsync("TestBrodcasting", result);
}
}
In Controller:
public async Task<IActionResult> getApps()
{
// var request = new HttpRequestMessage(HttpMethod.Get, "apps");
// var response = await _client_NP.SendAsync(request);
// var json = await response.Content.ReadAsStringAsync();
return Ok($"Testing a Basic HUB at {DateTime.Now.ToLocalTime()}");
}
Client:
let connection = new signalR.HubConnectionBuilder()
.withUrl("/nphub").build();
connection.start().then(function () {
TestConnection();
}).catch(function (err) {
return console.error(err.toString());
});
function TestConnection() {
connection.invoke("NotifyConnection").catch(function (err) {
return console.error(err.toString());
});
}
connection.on("TestBrodcasting", function (time) {
document.getElementById('broadcastDiv').innerHTML = time.value;
document.getElementById('broadcastDiv').style.display = "block";
});
Just for the test purpose to see realtime changes, I'm trying to return time. I'm able to see time on the client but it's not changing.
You need to use a hosted service, as described in the docs. Add a class like:
internal class SignalRTimedHostedService : IHostedService, IDisposable
{
private readonly IHubContext<nphub> _hub;
private readonly ILogger _logger;
private Timer _timer;
public SignalRTimedHostedService(IHubContext<nphub> hub, ILogger<SignalRTimedHostedService> logger)
{
_hub = hub;
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(1));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
// send message using _hub
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
Note: A hosted service lives in singleton scope. You can inject IHubContext<T> directly, though, because it too is in singleton scope.
Then in ConfigureServices:
services.AddHostedService<SignalRTimedHostedService>();
Hello i am building an tcp server where i will have multiple clients connected that will send and receive data to the server.
I want to know if the framework does not create a 1:1 Thread to Client ratio but uses the threadpool how do the following happen:
1.If the method that gets executed after accepting the socket contains a loop inside it, won't the allocated thread (by the threadpool) get blocked on a client context?
2.Where is the context for each client stored?
P.S In my picture i do not understand how the blue thread (given by the threadpool to service the two clients)gets reused.
The code below contains the Handler (contains all connections) and the Client (a socket wrapper , with basic read/write functionality).
Sockets Handler
class Handler
{
private Dictionary<string, Client> clients = new Dictionary<string, Client>();
private object Lock = new object();
public Handler()
{
}
public async Task LoopAsync(WebSocketManager manager)
{
WebSocket clientSocket = await manager.AcceptWebSocketAsync();
string clientID = Ext.MakeId();
using(Client newClient = Client.Create(clientSocket, clientID))
{
while (newClient.KeepAlive)
{
await newClient.ReceiveFromSocketAsync();
}
}
}
public bool RemoveClient(string ID)
{
bool removed = false;
lock (Lock)
{
if (this.clients.TryGetValue(ID, out Client client))
{
removed= this.clients.Remove(ID);
}
}
return removed;
}
}
SocketWrapper
class Client:IDisposable
{
public static Client Create(WebSocket socket,string id)
{
return new Client(socket, id);
}
private readonly string ID;
private const int BUFFER_SIZE = 100;
private readonly byte[] Buffer;
public bool KeepAlive { get; private set; }
private readonly WebSocket socket;
private Client(WebSocket socket,string ID)
{
this.socket = socket;
this.ID = ID;
this.Buffer = new byte[BUFFER_SIZE];
}
public async Task<ReadOnlyMemory<byte>> ReceiveFromSocketAsync()
{
WebSocketReceiveResult result = await this.socket.ReceiveAsync(this.Buffer, CancellationToken.None);
this.KeepAlive = result.MessageType==WebSocketMessageType.Close?false:true;
return this.Buffer.AsMemory();
}
public async Task SendToSocketAsync(string message)
{
ReadOnlyMemory<byte> memory = Encoding.UTF8.GetBytes(message);
await this.socket.SendAsync(memory, WebSocketMessageType.Binary,true,CancellationToken.None);
}
public void Dispose()
{
this.socket.Dispose();
}
}
The service that will get injected in the application:
class SocketService
{
Handler hander;
public SocketService(Handler _handler)
{
this.hander = _handler;
}
RequestDelegate next;
public async Task Invoke(HttpContext context)
{
if (!context.WebSockets.IsWebSocketRequest)
{
await this.next(context);
return;
}
await this.hander.AddClientAsync(context.WebSockets);
}
}
NServiceBus 4.4.0
Hi !
I use message mutators for impersonification purpose. Basicaly, I serialize the ClaimsPrincipal.Identity in a JWT token in the outgoing mutator and deserialize it in the incoming mutator to add it to the principal of the NServiceBus host app. (based on this article : http://beingabstract.com/2014/10/serializing-the-claimsidentity/). The problem is that when we're in the outgoing mutator (IMutateOutgoingTransportMessages) the ClaimsPrincipal.Identity doesn't contain all the claims. Only the name. But if I'm looking just before the "Bus.Send" command, I have the correct claims (groups, permissions, etc.).
The outgoing message mutator resides in an outside class, which is referenced by my main project. Here's the code from the outgoing mutator :
public class OutgoingAccessMutator : IMutateOutgoingTransportMessages, INeedInitialization
{
public IBus Bus { get; set; }
public void Init()
{
Configure.Instance.Configurer.ConfigureComponent<OutgoingAccessMutator>(DependencyLifecycle.InstancePerCall);
}
public void MutateOutgoing(object[] messages, TransportMessage transportMessage)
{
if (!transportMessage.Headers.ContainsKey(Common.Constants.Securite.AuthenticationTokenID))
{
transportMessage.Headers[Common.Constants.Securite.AuthenticationTokenID] =
TokenHelper.GenerateToken(ClaimsPrincipal.Current.IdentitePrincipale() as ClaimsIdentity);
}
}
}
The GenerateToken is in a static helper class in the mutator dll :
public static string GenerateToken(ClaimsIdentity identity)
{
var now = DateTime.UtcNow;
var tokenHandler = new JwtSecurityTokenHandler();
var securityKey = System.Text.Encoding.Unicode.GetBytes(Common.Constants.Securite.NServiceBusMessageTokenSymetricKey);
var inMemorySymmetricSecurityKey = new InMemorySymmetricSecurityKey(securityKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = identity,
TokenIssuerName = Common.Constants.Securite.NServiceBusMessageTokenIssuer,
AppliesToAddress = Common.Constants.Securite.NServiceBusMessageTokenScope,
Lifetime = new Lifetime(now, now.AddMinutes(5)),
SigningCredentials = new SigningCredentials(inMemorySymmetricSecurityKey, Common.Constants.Securite.SignatureAlgorithm, Common.Constants.Securite.DigestAlgorithm)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return tokenString;
}
Then in the incoming message mutator which lives in another process (windows service executable host), I deserialize it :
public class IncomingAccessTokenMutator : IMutateIncomingTransportMessages, INeedInitialization
{
public IBus Bus { get; set; }
public void Init()
{
Configure.Instance.Configurer.ConfigureComponent<IncomingAccessTokenMutator>(DependencyLifecycle.InstancePerCall);
}
public void MutateIncoming(TransportMessage transportMessage)
{
if (transportMessage.Headers.ContainsKey(Common.Constants.Securite.AuthenticationTokenID))
{
try
{
var token = transportMessage.Headers[Common.Constants.Securite.AuthenticationTokenID];
var identity = TokenHelper.ReadToken(token);
if (identity != null)
{
identity.Label = Common.Constants.Securite.NomIdentitePrincipale;
ClaimsPrincipal.Current.AddIdentity(identity);
}
}
catch (Exception)
{
Bus.DoNotContinueDispatchingCurrentMessageToHandlers();
throw;
}
}
}
}
Does anyone have any idea why the identity context is not the same inside the mutator and in the service that sends the message ?
I have a wpf application which calls a wcf service method. The method runs fine in debugging mode but the method doesnt return back to the client call.
Here is the code.
Client:
public class Provider
{
private static ActionServiceClient Client { get; set; }
static Provider()
{
Client = new ActionServiceClient();
}
public UserResponse GetUsers(UserRequest request)
{
UserResponse resp = new UserResponse();
resp = Client.GetUsers(request);
return resp;
}
}
WCF Service :
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
public class ActionService : IActionService
{
public MovieResponse GetReviews(MovieRequest request)
{
List<MovieReview> reviews = DataAccess.GetMovieReviews(0);
MovieResponse response = new MovieResponse();
response.movieReviews = reviews;
return response;
}
public UserResponse GetUsers(UserRequest request)
{
List<User> users = DataAccess.GetUsers(0);
UserResponse resp = new UserResponse();
resp.users = users;
return resp;
}
[DataContract]
public class UserResponse
{
[DataMember]
public List<User> users;
}
[DataContract]
public class UserRequest
{
[DataMember]
public int userId;
}
I run the program in debug mode and after completion of the service call the wpf application hangs....
Oops....Passing an image is not allowed.