I am new to signalr and I set up a server:
services.AddControllers();
services.AddSignalR(o =>
{
o.EnableDetailedErrors = true;
}).AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.WriteIndented = true;
options.PayloadSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
And in my hub I have a function that returns an object
public async Task<ResultType> FunctionWithTResult(string path)
{
// Do work
return new ResultType { StatusCode = 200 };
}
The problem is that this function is not returning the ResultType. The client only gets an empty object.
I build a client software to test this and it's the same result.
ResultType result = await connection.InvokeAsync<ResultType >("FunctionWithTResult", "test");
Strangely string and int Tasks work just fine. The problem only happens with objects.
Does anyone know what I am doing wrong?
Related
We are using Automappers ProjectTo method to build a DTO object that is a subset of a larger database view. It is working as expected when running the actual application but we have had a problem where it doesn't give expected results when unit testing with an EF Core In-Memory database. It seems to just be giving back 0 results no matter what the query. Here is the test I am trying to run.
[Fact]
public async Task GetTemplateAdHocReportList_ReturnsOnlyTemplateReports()
{
await TestHelper.SeedFull(ReportContext); // Calls SeedAdHocReports below along with other seed methods
var results = await _sut.GetTemplateAdHocReports();
results.Where(x => !x.IsTemplate).Count().Should().Be(0);
}
This is the Seed Data:
public static async Task SeedAdHocReports(ReportContext context)
{
var reports = new AdHocReport[]
{
new AdHocReport()
{
Id = 1,
Name = "DevExtreme Example Report",
IsTemplate = true,
AdHocDataSourceId = 1,
Fields = "[{\"caption\":\"Category\",\"dataField\":\"ProductCategoryName\",\"expanded\":true,\"area\":\"row\"},{\"caption\":\"Subcategory\",\"dataField\":\"ProductSubcategoryName\",\"area\":\"row\"},{\"caption\":\"Product\",\"dataField\":\"ProductName\",\"area\":\"row\"},{\"caption\":\"Date\",\"dataField\":\"DateKey\",\"dataType\":\"date\",\"area\":\"column\"},{\"caption\":\"Amount\",\"dataField\":\"SalesAmount\",\"summaryType\":\"sum\",\"format\":{\"type\":\"currency\",\"precision\":2,\"currency\":\"USD\"},\"area\":\"data\"},{\"caption\":\"Store\",\"dataField\":\"StoreName\"},{\"caption\":\"Quantity\",\"dataField\":\"SalesQuantity\",\"summaryType\":\"sum\"},{\"caption\":\"Unit Price\",\"dataField\":\"UnitPrice\",\"format\":\"currency\",\"summaryType\":\"sum\"},{\"dataField\":\"Id\",\"visible\":false}]",
Status = true
}
};
context.AdHocReports.AddRange(reports);
await context.SaveChangesAsync();
}
Here is the GetTemplateAdHocReports method being tested
public async Task<IList<AdHocReportDto>> GetTemplateAdHocReports()
{
//This gives the expected 1 object in the unit tests:
var test = await _reportContext.AdHocReports.Where(x => x.Status && x.IsTemplate).OrderBy(x => x.Name).ToListAsync();
//This always comes back with a count of 0 even though the seed data should return 1 result
var results = await _reportContext.AdHocReports.Where(x => x.Status && x.IsTemplate).OrderBy(x => x.Name).ProjectTo<AdHocReportDto>(_mapper.ConfigurationProvider).ToListAsync();
return results;
}
Finally in case they are useful here are the constructors:
public AdHocServiceTests()
{
TestHelper = new TestHelper();
var reportOptions = TestHelper.GetMockedReportDbOptions();
ReportContext = new ReportContext(reportOptions);
_sut = new AdHocService(ReportContext, TestHelper.Mapper, TestHelper.GetMockedNiceService().Object);
}
public TestHelper()
{
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile<ReportDtoMapperProfile>();
});
Mapper = mappingConfig.CreateMapper(); // public property on TestHelper
}
public AdHocService(ReportContext reportContext, IMapper mapper, INiceService niceService)
{
_niceService = niceService;
_mapper = mapper;
_reportContext = reportContext;
}
So the final question is why does the "var test = " line above work, but the following "var results =" line with the ProjectTo just keeps giving back 0 results?
I've created a call to Firebase to get the random photo (since we have categories of photos, first I'm trying to get random category, then random photo from it). After that I want to make async UnityWebRequest to get the photo and add it as a texture. The code gets to the inside of the Task but the call to database is never executed. I tried the code to get the image separately and it worked just fine. I also tried using delegate and action, but didn't help much. I'm still pretty newbie to C# and Unity, so my code isn't that good. Will appreciate all the feedback.
I tried the code to get the image separately and it worked just fine. I also tried using delegate and action, but didn't help much. I'm still pretty newbie to C# and Unity, so my code isn't that good. Will appreciate all the feedback.
//Getting the random photo
async Task GetRandomPhoto(){
await photosDbReference.GetValueAsync().ContinueWith(task =>{
List<string> snapshotList = new List<string>();
List<string> snapsnotList2 = new List<string>();
if(task.IsCompleted){
int catNumber = Convert.ToInt32(task.Result.ChildrenCount);
System.Random rnd = new System.Random();
int randCat = rnd.Next(0, catNumber);
foreach (DataSnapshot snapshot in task.Result.Children)
{
snapshotList.Add(snapshot.Key.ToString());
}
photosDbReference.Child(snapshotList[randCat]).GetValueAsync().ContinueWith(task2 =>{
if(task2.IsCompleted){
int photosNumber = Convert.ToInt32(task2.Result.ChildrenCount);
System.Random rnd2 = new System.Random();
int randPhoto = rnd.Next(0, photosNumber);
foreach(DataSnapshot snap2 in task2.Result.Children){
snapsnotList2.Add(snap2.Child("Dblink").Value.ToString());
}
string photoLink = snapsnotList2[randPhoto];
}
});
}
});
}
//Trying to set the photo as a texture
public async void PutTheTexture(string url){
Texture2D texture = await GetTexture(url);
myImage.texture = texture;
}
public async Task<Texture2D> GetTexture(string url){
Debug.Log("Started");
UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
Debug.Log("Sending request: " + url);
var asyncOp = www.SendWebRequest();
Debug.Log("Request sent");
while( asyncOp.isDone==false )
{
await Task.Delay( 1000/30 );
}
if( www.isNetworkError || www.isHttpError )
{
#if DEBUG
Debug.Log( $"{ www.error }, URL:{ www.url }" );
#endif
return null;
}
else
{
return DownloadHandlerTexture.GetContent( www );
}
}
The code gets to the Debug.Log("Started"); inside the Task but apparently the request is never send.
I can't quite tell how your two blocks of code go together, but what I will point out is that .ContinueWith will not continue in Unity's main thread. My suspicion is that the continuation is kicking off the GetTexture via a mechanism I'm not seeing.
As far as I can tell, async/await should always stay in your current execution context but perhaps the Continuations are causing your logic to execute outside of the Unity main thread.
Since you're using Firebase, this would be super easy to test by replacing ContinueWith with the extension method ContinueWithOnMainThread. If this doesn't help, you can generally swap out async/await logic with continuations on tasks or fairly easily convert the above example to use purely coroutines:
//Getting the random photo
void GetRandomPhoto(){
photosDbReference.GetValueAsync().ContinueWithOnMainThread(task =>
{
List<string> snapshotList = new List<string>();
List<string> snapsnotList2 = new List<string>();
if(task.IsCompleted){
int catNumber = Convert.ToInt32(task.Result.ChildrenCount);
System.Random rnd = new System.Random();
int randCat = rnd.Next(0, catNumber);
foreach (DataSnapshot snapshot in task.Result.Children)
{
snapshotList.Add(snapshot.Key.ToString());
}
photosDbReference.Child(snapshotList[randCat]).GetValueAsync().ContinueWithOnMainThread(task2 =>{
if(task2.IsCompleted){
int photosNumber = Convert.ToInt32(task2.Result.ChildrenCount);
System.Random rnd2 = new System.Random();
int randPhoto = rnd.Next(0, photosNumber);
foreach(DataSnapshot snap2 in task2.Result.Children){
snapsnotList2.Add(snap2.Child("Dblink").Value.ToString());
}
string photoLink = snapsnotList2[randPhoto];
}
});
}
});
}
public delegate void GetTextureComplete(Texture2D texture);
private void Completion(Texture2D texture) {
myImage.texture = texture;
}
//Trying to set the photo as a texture
public void PutTheTexture(string url){
GetTexture(url, Completion);
}
public IEnumerator GetTexture(string url, GetTextureComplete completion){
Debug.Log("Started");
UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
Debug.Log("Sending request: " + url);
var asyncOp = www.SendWebRequest();
Debug.Log("Request sent");
yield return asyncOp;
if( www.isNetworkError || www.isHttpError )
{
#if DEBUG
Debug.Log( $"{ www.error }, URL:{ www.url }" );
#endif
completion(null);
}
else
{
completion(DownloadHandlerTexture.GetContent(www));
}
}
(you can do better than my example, and I haven't verified that it runs. Just a quick pass)
Big Thank You to everybody who tried to help! I finally found the way to solve the issue. I changed my async Task to async Task < "Dictionary" > and made it return the dict wilth all the data of the random photo (label, link, user). Then I created async void in which I wrote:
Dictionary photoData = await GetRandomPhoto();
From there it was very easy.
public async Task<ResModel> GetDetails()
{
ResModel resp = new ResModel();
resp = await MobController.GetAllDeatails();
return resp;
}
In the await this calls for an async task method named GetAllDeatails and returns an object ResModel.
How to properly create an unit test for this?
The unit test I've created is right below:
[TestMethod]
public async Task GetFromGetDetails()
{
var controller = new SevicesController();
ResModel resp = new ResModel();
using(ShimsContext.Create())
{
ProjectAPI.Fakes.ShimMobController.GetAllDeatails = () => {
resp.MessageType = "Get"
resp.Message ="contdetail";
return resp;
};
returnValueOfAsyncTask = await controller.GetDetails();
Assert.IsNotNull(returnValueOfAsyncTask);
}
}
This gives errors in the ShimContext creation because the return type must be async in GetAllDeatails but here it only returns the object.
Following is the main error I'm getting:
Error CS0029 Cannot implicitly convert type
'ProjectAPI.Models.ResModel' to
'System.Threading.Tasks.Task<ProjectAPI.Models.ResModel>'
UnitTestProjectAPI
The error message clearly states that it cannot convert the ResModel to a Task<ResModel> which is the return type of the method being shimmed.
In the shim you are returning the model when the method is suppose to be returning a Task. Use Task.FromResult in order to wrap the model in a Task so that it can be return and awaited.
In the exercising of the method under test you stated that the variable used to hold the result of the await was a Task<ResModel>. That should be changed because when you await a Task that returns a result it will just return the result not the task. So that result variable needs to be changed to from Task<ResModel> to ResModel.
Here is the update unit test based on above changes.
[TestMethod]
public async Task GetFromGetDetails() {
//Arrange
var controller = new SevicesController();
var expected = new ResModel();
using(ShimsContext.Create()) {
ProjectAPI.Fakes.ShimMobController.GetAllDeatails = () => {
expected.MessageType = "Get"
expected.Message ="contdetail";
return Task.FromResult(expected);
};
//Act
var actual = await controller.GetDetails();
//Assert
Assert.IsNotNull(actual);
Assert.AreEqual(expected, actual);
}
}
Change the Method return type
to
(public async Task
<
ResModel> GetFromGetDetails()
I have a phone application. When a screen displays I start a timer like this:
base.OnAppearing();
{
timerRunning = true;
Device.BeginInvokeOnMainThread(() => showGridTime(5));
}
async void showGridTime(int time)
{
while (timerRunning)
{
var tokenSource = new CancellationTokenSource();
await Task.Delay(time, tokenSource.Token);
detailGrid.IsVisible = true;
}
}
The code seems to work but there is a warning message in the IDE saying that an async method cannot return null.
Given this code can someone help and give me advice on what I should return and if I am going about this in the correct way?
Just return a task:
async Task ShowGridTimeAsync(int time)
{
while (timerRunning)
{
var tokenSource = new CancellationTokenSource();
await Task.Delay(time, tokenSource.Token);
detailGrid.IsVisible = true;
}
}
This is necessary to have the caller of this method know when it is completed and act accordingly.
It is not recommended to create async void methods unless you're creating an event or forced to do that in order to meet an interface signature.
I created a test source which should send a message to the client every x time. This is the ApiController:
public class TestSourceController : ApiController
{
private static readonly ConcurrentQueue<StreamWriter> ConnectedClients = new ConcurrentQueue<StreamWriter>();
[AllowAnonymous]
[Route("api/sources/test")]
public HttpResponseMessage Get()
{
var response = Request.CreateResponse();
response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>) OnStreamAvailable,
"text/event-stream");
return response;
}
private static void OnStreamAvailable(Stream stream, HttpContent headers, TransportContext context)
{
var clientStream = new StreamWriter(stream);
ConnectedClients.Enqueue(clientStream);
}
private static void DoThings()
{
const string outboundMessage = "Test";
foreach (var clientStream in ConnectedClients)
{
clientStream.WriteLine("data:" + JsonConvert.SerializeObject(outboundMessage));
clientStream.Flush();
}
}
}
The clientStream.Flush(); is called like expected and without exceptions.
I handle it in AngularJS like this:
$scope.handleServerCallback = function (data) {
console.log(data);
$scope.$apply(function() {
$scope.serverData = data;
});
};
$scope.listen = function () {
$scope.eventSource = new window.EventSource("http://localhost:18270/api/sources/test");
$scope.eventSource.onmessage = $scope.handleServerCallback;
$scope.eventSource.onopen = function() { console.log("Opened source"); };
$scope.eventSource.onerror = function (e) { console.error(e); };
};
$scope.listen();
My guess is it's a problem with the server since I can see the "EventStream" from the test call is empty in the chrome debugger.
Does anyone know how to make sure the messages arrive at the client?
The solution was quite easy, according to the spec every line has to end with "\n" and the very last line with "\n\n".
So:
clientStream.WriteLine("data:" + JsonConvert.SerializeObject(outboundMessage) + "\n\n");
Solves it.