Edit /Update with Web API Repository Pattern - javascript

I'm trying to work out the very basics of updating my database using a Web API Controller that is backed by a repository pattern. So far I have everything working POST, GET, DELETE (Create, Read, Delete). But I'm missing the Update.
Below is my angular code, I'm not going to post the Angular Views/Templates, but just know that they do bind and they work just fine. My problem is only on the Edit View, where I try to update using the vm.save function. My save function works fine on the Angular side, but I'm not sure what to do on the Web API & Repository side. You will see that my code to get this working is very basic bare bones. I have all of the code pages from my project in a gist here:
All Files in Gist
Just in case you want to see the big picture, otherwise I will just put here the few pages where I am having trouble getting the Edit/Update methods to work in using http.put with Angular Controller, Web API Controller & Repository.
WORKING Angular Edit Controller:
function editFavoriteController($http, $window, $routeParams) {
var vm = this;
var url = "/api/favorites/" + $routeParams.searchId;
$http.get(url)
.success(function (result) {
vm.search = result[0];
})
.error(function () {
alert('error/failed');
})
.then(function () {
//Nothing
});
vm.update = function (id) {
var updateUrl = "/api/favorites/" + id;
$http.put(updateUrl, vm.editFavorite)
.success(function (result) {
var editFavorite = result.data;
//TODO: merge with existing favorites
//alert("Thanks for your post");
})
.error(function () {
alert("Your broken, go fix yourself!");
})
.then(function () {
$window.location = "#/";
});
};
};
NOT WORKING Web API Controller
public HttpResponseMessage Put(int id,[FromBody]Search editFavorite)
{
if (_favRepo.EditFavorite(id, editFavorite) && _favRepo.Save())
{
return Request.CreateResponse(HttpStatusCode.Created, editFavorite);
}
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
NOT WORKING Repository
public bool EditFavorite(int id, Search editFavorite)
{
try
{
var search = _ctx.Search.FirstOrDefault(s => s.SearchId == id);
search(editFavorite).State = EntityState.Modified;
return true;
}
catch
{
var item = "";
}
}
WORKING Interface
bool EditFavorite(int id, Search newSearch);
Again, my only problems are figuring out what to do for the update in the WebAPI FavoritesController and FavoritesRepository. I have example of how I have done everything else in the Gist, so I'm hoping someone might be able to help me out. I'm just hitting a wall of what I know how to do in Web API.

Fixed Code:
public HttpResponseMessage Put(int id,[FromBody]Search editFavorite)
{
if (_favRepo.EditFavorite(id, editFavorite))
{
_favRepo.Save()
return Request.CreateResponse(HttpStatusCode.Created, editFavorite);
}
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
I am also posting code which should work fine for handling edit on server side using WEB API and Repository Pattern.
WebAPI Controller:
public HttpResponseMessage Put(int id,[FromBody]Search editFavorite)
{
if (!ModelState.IsValid || id != editFavorite.Id)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
db.EditFavorite(editFavorite);
try
{
db.Save();
}
catch (DbUpdateConcurrencyException)
{
if (!db.SearchExists(id))
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
else
{
throw;
}
}
return Request.CreateResponse(HttpStatusCode.Created, editFavorite);
}
Repository Method:
public void EditFavorite(Search editFavorite)
{
db.Entry(editFavorite).State = EntityState.Modified;
}
public void Save()
{
db.SaveChanges();
}
public bool SearchExists(int id)
{
return db.Search.Count(e => e.Id == id) > 0;
}
Modify Interface:
void Save();
void EditFavorite(Search newSearch);
bool SearchExists(int id);
Edit:
I have made some changes so that only operations that are carried out on your db context is done in repository layer (Data Layer) and the error checking is done in the WEB API Controller.
Suggestion:
You should inherit IDisposable on the interface and implement it your repository class so that your entities are properly disposed...
public interface IFavoritesRepository : IDisposable
{
// code here
}
public class FavoritesRepository : IFavoritesRepository
{
// code here
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

Related

HybridWebView EvaluateJS not working for Android Xamarin

I am trying to load a web url in my app which is a login page. On entering the details and after authentication it is supposed to return a token . I have used EvaluateJavascript property in HybridWebView . This is working completely fine in iOS , whereas in Android , after the login process it throws 404 error suddenly for the past few days. All the code is same except for the renderers.I do not have a custom control instead have created the ContentViews for hybridwebview as it is prism Template.
Does anyone know/have any solution for this?
Android HybridWebViewRenderer.cs file
public class HybridWebViewRenderer : WebViewRenderer //ViewRenderer<HybridWebView, Android.Webkit.WebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
var webView = e.NewElement as HybridWebView;
if (webView != null)
{
webView.EvaluateJavascript = async (js) =>
{
var reset = new ManualResetEvent(false);
var response = string.Empty;
Device.BeginInvokeOnMainThread(() =>
{
Control?.EvaluateJavascript(js, new JavascriptCallback((r) => { response = r; reset.Set(); }));
});
await Task.Run(() => { reset.WaitOne(); });
return response;
};
}
}
}
internal class JavascriptCallback : Java.Lang.Object, IValueCallback
{
public JavascriptCallback(Action<string> callback)
{
_callback = callback;
}
private Action<string> _callback;
public void OnReceiveValue(Java.Lang.Object value)
{
_callback?.Invoke(Convert.ToString(value));
}
}
In VM :
I have declared EvaluateJavascript
String innerHtml = await EvaluateJavascript("document.body.innerHTML");
HybridWebView Content View :
public static readonly BindableProperty EvaluateJavascriptProperty =
BindableProperty.Create(nameof(EvaluateJavascript),
typeof(Func<string, Task<string>>),
typeof(HybridWebView),
null,
BindingMode.OneWayToSource);
public Func<string, Task<string>> EvaluateJavascript
{
get { return (Func<string, Task<string>>)GetValue(EvaluateJavascriptProperty); }
set { SetValue(EvaluateJavascriptProperty, value); }
}

SignalR methods in WebApi controller not called in specific setup

Well, I have a signalR hub:
public class ReportHub : Hub
{
private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<ReportHub>();
public void SendMessage(string text)
{
Clients.All.sendMessage(text);
}
public static void ServerSendMessage(string text)
{
hubContext.Clients.All.sendMessage(text);
}
}
Also I have the client code in js, on some view
report.client.sendMessage = message => {
alert('message from server: '+ message);
}
And I have webapi action, like this:
[HttpGet]
[Route("api/Report/test")]
public int GetTest()
{
ReportHub.ServerSendMessage("message");
return 42;
}
When I open the view with signalR-catching js code in one browser, and in another browser window requesting the webapi action, by typing http://../api/report/test - all working, and alert is appearing
But when I calling webapi action via postman, or any other rest client, no effect at all, report.client.sendMessage = message => {
alert('message from server: '+ message);
} - not working
Can anyone help?
ReportHub.ServerSendMessage("message");
It is wrong.
You should return ReportHubContext for the connection before pushing the data to clients;
[HttpGet]
[Route("api/Report/test")]
public int GetTest()
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ReportHub>();
hubContext.ServerSendMessage("message");
return 42;
}

SingalR didn't invoke server side method and broadcast data back to client side

I have a regular running job which is registered into an OWIN host app, I took the project of stock ticker broadcasting app as a reference, and I want to make that job like the stock ticker class which is being able to broadcast data to a JavaScript client.
however, after i followed the example, and set everything right, some weird problem happened and I couldn't figure out why.
(1) the traffic will automatically downgrade to long polling, instead of using server send event like the stock ticker did.
(2) I have called a dynamic method at server side, and defined that method at client side, but it never got called
this is my server side hub definition
[HubName("healthCheckHub")]
public class HealthCheckHub : Hub
{
private readonly PublicVendorCloudHealthJob _healthCheckjob;
public HealthCheckHub()
{
this._healthCheckjob = PublicVendorCloudHealthJob.Instance;
}
public IEnumerable<HealthCheckItemResponse> GetAllHealthCheckResponses()
{
return this._healthCheckjob.GetAllHealthCheckResponses();
}
}
this is my Job class implementation
public class PublicVendorCloudHealthJob : SkyJobGrain, IPublicVendorCloudHealthJob
{
private readonly ConcurrentBag<HealthCheckItemResponse> _response;
private static IConfiguration _configuration;
private static IDeploymentElevator _deploymentElevator;
private static IItineraryElevator _itineraryElevator;
private static IResourceElevator _resourceElevator;
public PublicVendorCloudHealthJob(IConfiguration configuration, IDeploymentElevator deploymentElevator, IItineraryElevator itineraryElevator, IResourceElevator resourceElevator)
: base(configuration, deploymentElevator, itineraryElevator, resourceElevator)
{
this.Clients = GlobalHost.ConnectionManager.GetHubContext<HealthCheckHub>().Clients;
_configuration = configuration;
_deploymentElevator = deploymentElevator;
_itineraryElevator = itineraryElevator;
_resourceElevator = resourceElevator;
this._response = new ConcurrentBag<HealthCheckItemResponse>
{
new HealthCheckItemResponse
{
Description = "Larissa test"
}
};
}
public override Task Execute(object obj)
{
this.Clients.All.publishHealthChecks("Larissa" + DateTime.UtcNow);
return TaskDone.Done;
}
public static PublicVendorCloudHealthJob Instance => (PublicVendorCloudHealthJob)Activator.CreateInstance(typeof(PublicVendorCloudHealthJob), _configuration, _deploymentElevator, _itineraryElevator, _resourceElevator);
public IEnumerable<HealthCheckItemResponse> GetAllHealthCheckResponses()
{
return this._response;
}
private IHubConnectionContext<dynamic> Clients { get; }
}
I also configure the hub in Owin startup.cs file like this
app.UseCors(CorsOptions.AllowAll);
GlobalHost.Configuration.KeepAlive = null;
GlobalHost.Configuration.TransportConnectTimeout = TimeSpan.FromSeconds(5);
app.MapSignalR();
for the JS client side, after I get the generated proxy, i did sth like this
signalrService.signalr().then((value) => {
if (value === "success"){
const healthCheckHub = $.connection.healthCheckHub;
const healthCheckHub2 = $.connection.hub.proxies.healthcheckhub;
healthCheckHub.client.publishHealthChecks = (data) => {
console.log(data);
};
healthCheckHub2.client.publishHealthChecks = (data) => {
console.log(data);
};
$.connection.hub.logging = true;
$.connection.hub.start().done(() => {
const defaultData = healthCheckHub.server.getAllHealthCheckResponses();
console.log(defaultData);
});
}
});
really need some help, It's been taking me for about one week to figure this out, thanks in advanced
P.S.
the things i m using is listed below
(1) Chrome v.51
(2) SingalR.core v.2.2.0
(3) Owin

Use SignalR as Broadcaster for EventBus Events

I recently started a new project with the AspBoilerplate (Abp) and to use SignalR as some kind of broadcasting mechanism to tell the connected clients if some records in the databse changed or were added or removed.
If i use the SignalR Hub as a proxy to my AppService everything works ok and the client is notified
public class TestHub : Hub
{
IMyAppService = _service
public TestHub(IMyAppService service)
{
_service = service;
}
public void CreateEntry(EntryDto entry)
{
_service.Create(entry);
Clients.All.entryCreated(entry);
}
}
But if i try to leverage the advantages of the EventBus of Abp so i implemented my AppSevice to send Events to the EventBus:
class MyAppService : ApplicationService, IMyAppService
{
public IEventBus EventBus { get; set; }
private readonly IMyRepository _myRepository;
public LicenseAppService(ILicenseRepository myRepository)
{
EventBus = NullEventBus.Instance;
_myRepository = myRepository;
}
public virtual EntryDto CreateLicense(EntryDto input)
{
var newEntry = Mapper.Map<EntryDto >(_myRepository.Insert(input));
EventBus.Trigger(new EntryCreatedEventData { Entry = newEntry});
return newEntry;
}
}
Then i tried to use the hub directly as EventHandler, but this failed because abp creates its own instance of the EventHandler classes whenever it needs to handle an event. But here the code just for completeness:
public class TestHub : Hub,
IEventHandler<EntryCreatedEventData>
{
public void Handle(EntryCreatedEventData data)
{
Clients.All.entryCreated(data.Entry);
}
}
After this i created a seperate Listener class and tried to use the hub context like this and use an pretty empty Hub:
public class TestHub : Hub
{
}
public class EntryChangeEventHandler : IEventHandler<EntryCreatedEventData>
{
private IHubContext _hubContext;
public EntryChangeEventHandler()
{
_hubContext = GlobalHost.ConnectionManager.GetHubContext<TestHub>();
public void Handle(EntryCreatedEventData data)
{
_hubContext.Clients.All.entryCreated(eventData.Entry);
}
}
In the last solution everything runs up to the line
_hubContext.Clients.All.entryCreated(eventData.Entry);
but on the client side in my javascript implementation the method is never called. The client side (based on DurandalJs) didn't change between using the Hub as proxy and the new way i want to go.
Client side plugin for working with signalr
define(["jquery", "signalr.hubs"],
function ($) {
var myHubProxy
function connect(onStarted, onCreated, onEdited, onDeleted) {
var connection = $.hubConnection();
myHubProxy = connection.createHubProxy('TestHub');
connection.connectionSlow(function () {
console.log('We are currently experiencing difficulties with the connection.')
});
connection.stateChanged(function (data) {
console.log('connectionStateChanged from ' + data.oldState + ' to ' + data.newState);
});
connection.error(function (error) {
console.log('SignalR error: ' + error)
});
myHubProxy .on('entryCreated', onCreated);
myHubProxy .on('updated', onEdited);
myHubProxy .on('deleted', onDeleted);
connection.logging = true;
//start the connection and bind functions to send messages to the hub
connection.start()
.done(function () { onStarted(); })
.fail(function (error) { console.log('Could not Connect! ' + error); });
}
return signalr =
{
connect: connect
};
});
view using the plugin:
define(['jquery', 'signalr/myHub],
function ($, myHubSR) {
return function () {
var that = this;
var _$view = null;
that.attached = function (view, parent) {
_$view = $(view);
}
that.activate = function () {
myHubSR.connect(that.onStarted, that.onCreated, that.onEdited, that.onDeleted);
}
that.onStarted= function () {
//do something
}
that.onCreated= function (data) {
//do something
}
that.onEdited = function (data) {
//do something
}
that.onDeleted= function (data) {
//do something
}
}
});
So anyone got a clue why onCreated is never called when i call
_hubContext.Clients.All.entryCreated(eventData.Entry);
?
For testing if the signalR communication works at all i added a method that directly calls a client method. Calling this method the update is pushed to the client successfully. so i think the problem is wiht the remote call to all clients using the IHubContext any clues what could go wrong in the usage of IHubContext?
public class TestHub : Hub
{
public TestHub ()
:base()
{ }
public void Test()
{
this.Clients.All.entryCreated(new EntryDto());
}
}
First, have you registered EntryChangeEventHandler to DI? If not, implement also ITransientDependency interface for EntryChangeEventHandler.
Your problem might be related to serializing. It may not serialize eventData.Entry. You can try to send another DTO object.
Also, you can implement
IEventHandler<EntityChangedEventData<Project>>
in order to listen all changes in a Project entity (including insert, update and delete). Project is just a sample entity here.
For your first case, TestHub can not work if it's not registered to DI. You may implement also ITransientDependency for TestHub class. And you should make SignalR to get it from DI container. You can use such a class for it:
public class WindsorDependencyResolver : DefaultDependencyResolver
{
public override object GetService(Type serviceType)
{
return IocManager.Instance.IocContainer.Kernel.HasComponent(serviceType) ? IocManager.Instance.IocContainer.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return IocManager.Instance.IocContainer.Kernel.HasComponent(serviceType) ? IocManager.Instance.IocContainer.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
}
}
And then set it on startup:
GlobalHost.DependencyResolver = new WindsorDependencyResolver();
Maybe my answer was a bit confusing :) I hope you can understand it.
After long searching in several directions i finally found a solution.
If you use a custom dependency Resolver in the HubConfiguration like i did. For example the implementation from hikalkan:
public class WindsorDependencyResolver : DefaultDependencyResolver
{
public override object GetService(Type serviceType)
{
return IocManager.Instance.IocContainer.Kernel.HasComponent(serviceType) ? IocManager.Instance.IocContainer.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return IocManager.Instance.IocContainer.Kernel.HasComponent(serviceType) ? IocManager.Instance.IocContainer.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
}
}
you can no longer use
_hubContext = GlobalHost.ConnectionManager.GetHubContext<TestHub>();
unless you also set your GlobalHost.DependencyResolver to a instance of WindsorDependencyResolver or manually resolve a reference to IConnectionManager.
GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
// A custom HubConfiguration is now unnecessary, since MapSignalR will
// use the resolver from GlobalHost by default.
app.MapSignalR();
or
IDependencyResolver resolver = new AutofacDependencyResolver(container);
IHubContext hubContext = resolver.Resolve<IConnectionManager>().GetHubContext<MyHub>();
app.MapSignalR(new HubConfiguration
{
Resolver = resolver
});

SignalR Get Clients outside of Hub not working

I am trying to call signalR from a controller, but it doesnt work.
Controller:
public ActionResult Index(){
var hub = GlobalHost.ConnectionManager.GetHubContext<ClientControl>();
hub.Clients.All.sendMessage("ahahhaaa");
//... return and staff
}
Hub
[Authorize]
[HubName("userTracking")]
public class ClientControl : Hub
{
public void RegisterConnection(String controller, String action)
{
}
public override Task OnConnected()
{
}
public override Task OnDisconnected()
{
return base.OnDisconnected();
}
}
Frontend:
$(document).ready(function () {
var trackhub = $.connection.userTracking;
trackhub.client.sendMessage = function (msg) {
alert(msg);
}
$.connection.hub.logging = true;
$.connection.hub.start();
});
In the controller it doesnt throw an error. When I debug and pause it, hub object exists, but when I perform it, it sends no message to the frontend. If I call the same method from the hub - works as a charm.
Any idea, what can be the root cause of the problem? If needed - write in the comments I will provide more info.
Server
public class ChatHub : Hub
{
public int TryAddNewUser(string userName)
{
//some logic...
Clients.All.AddUserToUserList(id, userName);
return id;
}
public void AddNewMessageToPage(int id, string message)
{
//some logic...
Clients.All.addNewMessageToPage(u.Login, message);
}
}
Client
$(document).ready(function () {
//first need register client methods
var chat = $.connection.chatHub;
chat.client.addUserToUserList = function (id, login) {
//client logic for add new user
}
chat.client.addNewMessageToPage = function (login, message) {
//client logic for add new message from user
}
//second need start chat
$.connection.hub.start().done(function () {
chat.server.tryAddNewUser(login).done(function (id) {
alert("Added " + id)
});
});
});
Controller
public class ChatController : Controller
{
private IMyDataService _service;
public ChatController(IMyDataService s)
{
_service = s;
}
public ActionResult Index()
{
return View(new MyDataViewModel(_service));
}
}
Note, dynamic js file must be added with the same path
<script type="text/javascript" src="~/signalr/hubs"></script>
And i add following code to Startup.cs
using Owin;
using Microsoft.Owin;
[assembly: OwinStartup(typeof(ChatRoom.Startup))]
namespace ChatRoom
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
}
About this see link
Please change the hub name in controller to work
Use
var hub = GlobalHost.ConnectionManager.GetHubContext<UserTracking>();
hub.Clients.All.sendMessage("ahahhaaa");
Instead of
var hub = GlobalHost.ConnectionManager.GetHubContext<ClientControl>();
hub.Clients.All.sendMessage("ahahhaaa");
Please note the change in Hub Class Name in GetHubContext<>.
Hope this helps.

Categories

Resources