How to use Exceptionally stage in CompletableFuture objects in Xamarin? - xamarin

I want to use ARCore with Xamarin and I stuck with error handling in CompletableFuture objects for example ModelRenderable.Builder(). Documentation says that I should use Exceptionally() function but I can't find any samples how to do it in C#.
In Xamarin Exceptionally() function require to use something that implements Java.Util.Function.IFunctions interface but my very simple class throws "Specified cast is not valid" error on runtime. Does anyone know how to use Exceptionally() function?
//here is java code how this should be implemented
ModelRenderable.builder()
.setSource(this, Uri.parse("..."))
.build()
.thenAccept(renderable -> andyRenderable2 = renderable)
.exceptionally(
throwable -> {
//show error
return null;
});
//here is my code in C#
var builder = new ModelRenderable.Builder();
builder.SetSource(this, Uri.parse("..."));
builder.Build()
.ThenAccept(new RenderableConsumer(renderable => AddNodeToScene(renderable)))
.Exceptionally(new Foo(() => { throw new Exception("error"); }));
//here is my very simple implementation of IFunction interface
internal class Foo : Java.Lang.Throwable, IFunction
{
public Foo(Action action) { }
public Java.Lang.Object Apply(Java.Lang.Object t)
{
return t;
}
}
EDITED
//alternative approach to ThenAccept() but in this case Exception is also not thrown
builder.Build().GetAsync()
.ContinueWith(renderable => AddNodeToScene((Renderable)renderable.Result), new System.Threading.CancellationToken(), TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(error => new Exception("Error!"), TaskContinuationOptions.OnlyOnFaulted);

Related

Return mono object with response entity webflux

I have the below controller
#RestController
#RequestMapping("/view")
public class ViewController {
#GetMapping(value = "/{channelId}/**")
public Mono<ResponseEntity<ViewResponse>> viewObject(#PathVariable(value = "channelId") String channelId) {
return redisController.getChannelData(channelInfoset, channelId).map(response -> {
Mono<ViewResponse> processOutput = processViewUrl(channelId); // returns object of Mono.just
return new ResponseEntity<>(processOutput, responseHeaders, HttpStatus.OK);
}}
The method which returns the mono object
private Mono<ViewResponse> processViewUrl(String channelId){
return Mono.just(new ViewResponse("val","temp",false));
}
This gives me an error saying
Incompatible types. Found: 'reactor.core.publisher.Mono<java.lang.Object>', required: 'reactor.core.publisher.Mono<org.springframework.http.ResponseEntity<com.api.model.ViewResponse>>'
What is wrong over here?
processOutput is a Mono<ViewResponse>, not a ViewResponse. To obtain a Mono<ResponseEntity<ViewResponse>>, you should map processOutput to a ResponseEntity:
return redisController.getChannelData(channelInfoset, channelId)
.map(response -> {
Mono<ViewResponse> processOutput = processViewUrl(channelId);
return processOutput.map(value -> new ResponseEntity(value, response.getHeaders(), HttpStatus.OK));
}}
Note that if there's no constraint over your processViewUrl method signature (API compliance, potential other async/complex implementation, etc.), I would recommend to change it to return directly a ViewResponse instead of a mono. Mono.just is not necessary here.
In general cases, Mono.just is required only when you want to provide a value to an operation that requires a publisher as input.
If you return directly a ViewResponse instead of a Mono<ViewResponse>, your initial code should work.

Sending new parameters in MvxViewModelRequest from a IMvxNavigationFacade when deeplinking

I am using deeplinking in my app and Im looking to preset some parameters when navigating to the viewmodel using a IMvxNavigationFacade. The deep link url is like this:
myappname://deeplink/toviewwithdata/?navigatetoview=viewtype1&id=78910
So the deep linking is working and im getting to the navigation facade using the assembly attribute
[assembly: MvxNavigation(typeof(RoutingFacade), #"myappname://deeplink/toviewwithdata/\?navigatetoview=(?<viewtype>viewtype1)&id=(?<id>\d{5})")]
I tried to add other parameters to the MvxViewModelRequest using a MvxBundle but dont think im doing it right. here is my navigation facade:
public class RoutingFacade : IMvxNavigationFacade
{
public Task<MvxViewModelRequest> BuildViewModelRequest(string url, IDictionary<string, string> currentParameters)
{
var viewModelType = typeof(FirstViewModel);
var parameters = new MvxBundle();
try
{
// TODO: Update this to handle different view types and add error handling
if (currentParameters != null)
{
Debug.WriteLine($"RoutingFacade - {currentParameters["viewtype"]}, {currentParameters["id"]}");
switch (currentParameters["viewtype"])
{
case "viewtype1":
viewModelType = typeof(FirstViewModel);
parameters.Data.Add("test", "somevalue");
break;
default:
case "viewtype2":
viewModelType = typeof(FirstViewModel);
break;
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"RoutingFacade - Exception: {ex.Message}");
//TODO viewModelType = typeof(ErrorViewModel);
}
return Task.FromResult(new MvxViewModelRequest(viewModelType, parameters, null));
}
then my viewmodel Init method
public void Init(string id, string viewtype, string test)
{
// Do stuff with parameters
}
but the test parameter is null? How do you pass parameters into a MvxViewModelRequest?
Update:
Don’t know if its possible from looking at the source here https://github.com/MvvmCross/MvvmCross/blob/f4b2a7241054ac288a391c4c7b7a7342852e1e19/MvvmCross/Core/Core/Navigation/MvxNavigationService.cs#L122 as the request parameters get set from the regex of the deeplink url and the return from BuildViewModelRequest, facadeRequest.parameterValues get ignored.
Added this functionality in this pull request

how to test fluent validations error message

I am trying to get to grips with TDD, I have reviewed some tutorials and am trying to implement tests on my validation classes that I have created using fluent validation.
public SomeFormValidator()
{
RuleFor(x => x.MyClass).NotNull()
.WithMessage("MyClass cannot be null");
}
I have looked at the TDD examples specifically for fluent validation and created a couple of tests
[Test]
public void Should_have_error_when_MyClass_is_null()
{
MyClass myClass = null;
SomeFormValidator.ShouldHaveValidationErrorFor(aup => aup.MyClass, myClass);
}
[Test]
public void Should_not_have_error_when_MyClass_is_not_null()
{
MyClass myClass = new MyClass();
SomeFormValidator.ShouldNotHaveValidationErrorFor(aup => aup.MyClass, myClass);
}
I would like to now test that the string "MyClass cannot be null" is returned when it is null. I have not been able to find anything covering returned message and I have not been able to work it out.
Thanks to the guidance of #Surgey I was able to come up with a solution that uses the fluent validation built in methods, in addition to that I have been able to better layout my test which I have added below
using FluentValidation.TestHelper;
using NUnit.Framework;
using MyProject.Models...
using MyProject...
namespace MyProject.Tests.Classes.Validation
{
[TestFixture]
public class SomeFormValidatorTest
{
private SomeFormValidator SomeFormValidator;
[SetUp]
public void Setup()
{
SomeFormValidator = new SomeFormValidator();
}
[Test]
public void Should_display_correct_error_message_MyClass_is_null()
{
//arrange
MyClass myClass = null;
//act
var result = SomeFormValidator.ShouldHaveValidationErrorFor(x => x.MyClass, myClass);
//assert
result.WithErrorMessage("MyClass is required");
//first attempt prior to finding WithErrorMessage exists
//foreach (var r in result)
//{
// Assert.AreEqual("MyClass is required", r.ErrorMessage);
//}
}
}
}
I am using result.WithErrorMessage as that is was is provided in the result but I have left the foreach in, but commented, as I find the error message produced by using Assert.AreEqual produce a better message in the test runner.
There is Arrange-Act-Assert (AAA) technique that helps to structure unit tests properly.
Arrange
First of all, you need to create a System Under Test (SUT) and inputs. In this case that are SomeFormValidator and SomeForm instances:
// arrange
var sut = new SomeFormValidator();
var someFormWithNullProp = new SomeForm { MyClass = null };
Act
Then you need to call the SUT to perform real work. For validators, that is the Validate() method call:
// act
ValidationResult result = sut.Validate<SomeForm>(someFormWithNullProp);
Assert
The last part of the unit test checks if the actual result matches the expectations:
// assert
Assert.False(result.IsValid);
Assert.AreEqual(
"MyClass cannot be null",
result.Errors.Single().ErrorMessage);

how to unit test controller when automapper is used?

here's my controller
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = Mapper.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
here's my unit test:
[Test]
public void Signup_Action_When_The_User_Model_Is_Valid_Returns_RedirectToRouteResult()
{
// Arrange
const string expectedRouteName = "~/Views/Home/Index.cshtml";
var registeredUser = new UserRegisterViewModel { Email = "newuser#test.com", Password = "123456789".Hash()};
var confirmation = new ActionConfirmation<User>
{
WasSuccessful = true,
Message = "",
Value = new User()
};
_userService.Setup(r => r.AddUser(new User())).Returns(confirmation);
_accountController = new AccountController(_userService.Object);
// Act
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result, "Should have returned a RedirectToRouteResult");
Assert.AreEqual(expectedRouteName, result.RouteName, "Route name should be {0}", expectedRouteName);
}
Unit test failed right here.
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
when I debug my unit test, I got following error message: "Missing type map configuration or unsupported mapping."
I think its because configuration is in web project, not the unit test project. what should I do to fix it?
You need to have the mapper configured, so in your test class set up, not the per-test setup, call the code to set up the mappings. Note, you'll also probably need to modify your expectation for the user service call as the arguments won't match, i.e, they are different objects. Probably you want a test that checks if the properties of the object match those of the model being passed to the method.
You should really use an interface for the mapping engine so that you can mock it rather than using AutoMapper otherwise it is an integration test not a unit test.
AutoMapper has an interface called IMappingEngine that you can inject into your controller using your IoC container like below (this example is using StructureMap).
class MyRegistry : Registry
{
public MyRegistry()
{
For<IMyRepository>().Use<MyRepository>();
For<ILogger>().Use<Logger>();
Mapper.AddProfile(new AutoMapperProfile());
For<IMappingEngine>().Use(() => Mapper.Engine);
}
}
You will then be able to use dependency injection to inject AutoMapper's mapping engine into your controller, allowing you to reference your mappings like below:
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = this.mappingEngine.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
You can read more about this here: How to inject AutoMapper IMappingEngine with StructureMap
Probably it is cool to abstract mapping into MappingEngine.
Sometimes I use following approach to IOC Automapper
In IOC builder:
builder.RegisterInstance(AutoMapperConfiguration.GetAutoMapper()).As<IMapper>();
where GetAutoMapper is:
public class AutoMapperConfiguration
{
public static IMapper GetAutoMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<OrderModelMapperProfile>();
cfg.AddProfile<OtherModelMapperProfile>();
//etc;
});
var mapper = config.CreateMapper();
return mapper;
}
}
And finally in Controller ctor
public MyController(IMapper mapper)
{
_mapper = mapper;
}

asp.net mvc ObjectDisposedException with ef

I need some help with this error "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
It's a asp.net mvc3, EF4, and ms sql.
Here is the razor with two dropdowns:
<div class="editRow">
#Html.DropDownListFor(m=>m.IndustryId, (SelectList)ViewBag.Industry, #Empower.Resource.General.ddlDefaultVal, new { #class = "ddl400" })
#Html.ValidationMessageFor(m => m.IndustryId)
</div>
<div class="editRow">
#Html.DropDownListFor(m=>m.ProvinceId, (SelectList)ViewBag.Province, #Empower.Resource.General.ddlDefaultVal, new {#class = "ddl400"})
#Html.ValidationMessageFor(m => m.ProvinceId)
</div>
Controller:
IndustryService indService = new IndustryService();
ViewBag.Industry = new SelectList(indService.GetAllIndustry(), "IndustryId", "IndustryName");
ProvinceService proService = new ProvinceService();
ViewBag.Province = new SelectList(proService.GetAllProvince(), "ProvinceId", "ProvinceName");
return View();
ProvinceService:
public IEnumerable<Province> GetAllProvince()
{
using (var context = DBContext.ObjectContext)
{
var pros = context.Provinces;
return pros;
}
}
IndustryService is identical as above...
public class DBContext
{
private static EmpowerDBEntities _empowerContext;
public static EmpowerDBEntities ObjectContext
{
get
{
if (_empowerContext == null)
_empowerContext = new EmpowerDBEntities();
return _empowerContext;
}
}
}
I know the problem occurs in second dropdown when it tries to retrive data while the connection is desposed by previous query. Please help me with this, thanks.
The fix is simple - convert to a .ToList() or First() before using. LINQ has deferred execution and tries to run this command after the context is disposed (when your object results are referenced) - not when you actually make the call.. You need to force it to run now while the context is in scope.
using (var context = DBContext.ObjectContext)
{
var pros = context.Provinces;
return pros.ToList();
}
Also - your code above is checking for null in the get accessor. However this object won't be null - it will be disposed, so you cannot do your check this way, you need to check if its null and not disposed.
public class DBContext
{
private static EmpowerDBEntities _empowerContext;
public static EmpowerDBEntities ObjectContext
{
get
{
if (_empowerContext == null || _empowerContext.IsDisposed())
_empowerContext = new EmpowerDBEntities();
return _empowerContext;
}
}
}
something like that anyways :)
I ran into a similar problem. I had been following this pattern, which I had seen in many code examples on the web:
public ActionResult Show(int id)
{
using (var db = new MyDbContext())
{
var thing = db.Things.Find(id);
return View(thing);
}
}
However, this was causing the ObjectDisposedException listed above whenever I tried to access anything that hadn't been loaded into memory in the View code (in particular, one-to-many relationships in the main view model).
I found a different pattern in this example:
public class MyController : Controller
{
private MyDbContext _db;
public MyController()
{
_db = new MyDbContext();
}
public ActionResult Show(int id)
{
// Do work such as...
var thing = _db.Things.Find(id);
return View(thing);
}
protected override void Dispose(bool disposing)
{
_db.Dispose();
base.Dispose(disposing);
}
}
This pattern keeps the database connection alive until the View has finished rendering, and neatly disposes of it when the Controller itself is disposed.
Here is the problem when you are using
using(var context=new CustomerContext())
{
return View(context.Customers.ToList());
}
when the block of code executes all references are disposed that you are lazzy loading so that's why it is throwing this error.
so i used
return View(context.Customers.ToList()) directly it will work perfectly fine.

Resources