I´m developing a windows 8 store app in c# and xaml for a website(the website is in .net mvc 4) .In the web site i can create, edit, and delete products (i´m using sql server 2008 r2).
In my w8 app i call a wcf service to get the data and populate the items.
I wanted to know if there is a way to notify or update the data in the w8 app after someone create or edit a product through the website.
One of the posible solution that i thought, was call the wcf service every time that the user click to see the list of products but i dont think is a good practice.
Here is my code:
//App.xaml.cs
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
ServiceProduct serviceData = (ServiceProduct)App.Current.Resources["serviceProducts"];
if (serviceData != null)
{
if (serviceData.ListaIos.Count == 0)
{
try
{
await serviceData.GetListProductsAsync();
}
catch(ServiceException)
{
var messageDialog = new Windows.UI.Popups.MessageDialog("Error");
var result = messageDialog.ShowAsync();
}
}
}
*** code ***
}
//Items page
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
// TODO: Assign a bindable collection of items to this.DefaultViewModel["Items"]
ServiceProduct serviceData = (ServiceProduct)App.Current.Resources["serviceProducts"];
if (serviceData != null)
{
this.DefaultViewModel["Items"] = serviceData.GetProducts;
}
}
public async Task GetListProductsAsync()
{
ProductServiceClient service = new ProductServiceClient();
Product product;
try
{
var wcfCall = await service.GetListaAsync();
foreach (var item in wcfCall.List)
{
map product
this.ListaIos.Add(product);
}
}
catch(Exception)
{
throw new NotImplementedException();
}
}
Related
I am getting following error when accessing HttpContext.Session from static method placed in separate task:
Session has not been configured for this application or request.
I used this article to implement access to HttpContext outside the controller
From controller I invoke this static method that used to retrieve image data:
public static void CreateDummyGallery(Gallery gallery)
{
Logger.LogDebug(LogModule.Dummy, $"Starting gallery creation.");
Task.Factory.StartNew(() =>
{
try
{
List<DummyPicture> pictures;
using (var context = new MyzeumContext())
{
int top = 10;
pictures = context.DummyPictures.FromSql($"SELECT * FROM dummypictures ORDER BY RAND() LIMIT {top}").ToList();
}
Logger.LogDebug(LogModule.Dummy, $"Starting retrieving images.");
Parallel.ForEach(pictures, picture => {
using (WebClient client = new WebClient())
{
}
});
Logger.LogDebug(LogModule.Dummy, $"Done retrieving images.");
}
catch(Exception e)
{
Logger.LogError(LogModule.Server, e.Message, e);
}
});
}
The problem occurs in Logger.LogDebug() because this is where I access HttpContext:
public void LogDebug(LogModule module, string message, Exception stackTrace = null)
{
Log record = new Log();
record.Module = module;
record.ThreadId = Environment.CurrentManagedThreadId;
record.SessionId = HttpContextHelper.Current?.Session?.Id;
record.Message = message;
record.Logged = DateTime.UtcNow;
if(stackTrace != null)
{
record.Message += $" :{stackTrace.StackTrace}";
}
queue.Enqueue(record);
}
The problem 99% occurs in the first call inside task:
Logger.LogDebug(LogModule.Dummy, $"Starting retrieving images.");
BUT, right after application starts this whole task block works fine and does not throw any exception. Problem starts after following requests.
Where would be the best place to call GetDataFromServer method?
My gut feeling and reason say it belongs in the repository, but I've no clue where to call it. I've tried to call it in the constructor, but that didn't work out too well. It had issues with it being an async method.
public class SQLiteRepository : ISQLiteRepository
{
private HttpClient _httpClient = new HttpClient();
private readonly SQLiteAsyncConnection _efContext;
public SQLiteRepository()
{
_efContext = DependencyService.Get<ISQLiteDb>().GetAsyncConnection();
_efContext.CreateTableAsync<EfPartner>();
}
public async Task<IEnumerable<EfPartner>> GetAllPartnersAsync()
{
try
{
var partners = await _efContext.Table<EfPartner>().ToListAsync();
return partners;
}
catch (Exception ex)
{
throw ex;
}
}
public async Task GetDataFromServerAsync()
{
try
{
var partners = await GetPartnersFromServerAsync();
var companies = await GetCompaniesFromServerAsync();
await _efContext.InsertAllAsync(partners);
}
catch (Exception ex)
{
throw ex;
}
}
private async Task<IEnumerable<EfPartner>> GetPartnersFromServerAsync()
{
try
{
var jsonObject = await _httpClient.GetStringAsync(Constants.PartnersUrl);
var dotNetObject = JsonConvert.DeserializeObject<List<EfPartner>>(jsonObject);
return new List<EfPartner>(dotNetObject);
}
catch (Exception ex)
{
throw ex;
}
}
private async Task<IEnumerable<EfCompany>> GetCompaniesFromServerAsync()
{
try
{
var jsonObject = await _httpClient.GetStringAsync(Constants.CompaniesUrl);
var dotNetObject = JsonConvert.DeserializeObject<List<EfCompany>>(jsonObject);
return new List<EfCompany>(dotNetObject);
}
catch (Exception ex)
{
throw ex;
}
}
}
I called the GetDataFromServerAsync from PartnersListPage.xaml.cs -> which feels wrong.
I'd appreciate any help.
Thank you.
============================ UPDATE ============================
The app I'm working on creates the pages in a MasterDetailPage like so:
private void MenuListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
return;
var menuItem = e.SelectedItem as EfMenuItem;
if (menuItem == null)
return;
var page = (Page)Activator.CreateInstance(menuItem.TargetPage);
page.Title = menuItem.Title;
Detail = new NavigationPage(page);
IsPresented = false;
MdpMasterPage.MenuListView.SelectedItem = null;
}
And here is the PartnersListPage.xaml.cs, from where is GetDataFromServer called at the moment:
public partial class PartnersListPage : ContentPage
{
private readonly SQLiteRepository _repo;
public PartnersListPage()
{
InitializeComponent();
_repo = new SQLiteRepository();
}
protected override async void OnAppearing()
{
await _repo.GetDataFromServerAsync();
var partners = await _repo.GetAllPartnersAsync();
InitializeGrid(partners);
base.OnAppearing();
}
This is an important topic related to MVVM, what you are doing is "correct" but it may have issues later on once your application grow bigger, let's imaging this scenario:
The user is a desperate one and he/she wants to Navigate on your app real fast, he/she open this specific page 5 or 10 times, what will happen then?
Your OnAppearing() method will be called every time the user lands on this page and a new thread will be created for instance your application will start to behave poorly on the performance part. (Specially on Android).
So my suggestion will be to use an MVVM framework to handle those cases based on the pattern, here is a small tutorial using Prism:
https://xamgirl.com/prism-in-xamarin-forms-step-by-step-part-1/
And in regards of your question, your UI ListView specifically your ItemSource (If you are using a ListView) property should be binded to your GetAllPartnersAsync() return type.
If that is not the case and based on the context I suppose InitializeGrid(partners); is creating cells for a Grid in your XAML class so I suppose you can debug step by step if the UI is being created correctly. A Grid does not have an ItemSource property
If you want to have a Grid with an ItemSource property I suggest you sue this ones:
https://github.com/Manne990/XamTest
https://github.com/daniel-luberda/DLToolkit.Forms.Controls/tree/master/FlowListView
I have created an MVC 4 application which targets .NET 4.0. After deploying to my production server, it will show the login page but will not redirect to the default page. However, when I add debugging, I can see that the authentication process works but then the error I am getting is an error that says it can't find my View for my Error Page and then shows my Error Page. It just seems that it will not go to my "Home/Index" page - even when I remove the authorize attribute. Of course the application works in development. Additionally, it will not go to my register page or forgot login page.
My Login Controller looks like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiforgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if(ModelStat.IsValid && _userService.Login(model.UserId, model.Password))
{
var user = _userService.GetUser(model.UserId);
var loggedInUser = new LoggedInUser
{
// Build the user for custom IPrincipal
};
var userData = JsonConvert.SerializeObject(loggedInUser);
var compressData = StringCompression.Compress(userData);
var authTicket = new FormsAuthenticationTicket(
1,
user.UserId,
DateTime.Now,
DateTime.Now.AddHours(1),
false,
compressData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
if(encTicket != null)
{
var faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
{
HttpOnly = true
};
Response.Cookies.Add(faCookie);
}
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
_userService.UpdateUser(user);
_uow.Commit();
return Url.IsLocalUrl(returnUrl) ? (ActionResult)Redirect(returnUrl) : RedirectToAction("Index", "Home");
}
return View(model);
and in my Global.asax:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if(authCookie != null)
{
var decompressedData = StringCompression.Decompress(authTicket.UserData);
var loggedInUser = JsonConvert.DesrializeObject<LoggedInUser>(decompressedData);
var currrentUser = new CustomPrincipal(authTicket.Name)
{
// Build the CustomPrincipal from the loggedInUser
};
if(HttpContext.Current.User.Identity.IsAuthenticated)
{
HttpContext.Current.User = currentUser;
}
}
}
I hope that this is enough to give someone an idea of what I may be doing wrong. Somehow I feel that it is something small that I am missing. Thanks in advance.
~InDireStraits
Update:
After more troubleshooting, it would seem that the issue may have something to do with the fact that I am using a BaseController for specifying permissions but I am still baffled as to why the application works as intended in my development environment but not in production. To verify my IIS settings I installed the default MVC4 App to production which does not have .NET 4.5, and it runs. I am using VS 2012 so I do have 4.5. Could I somehow be introducing .NET 4.5 classes or functionality even if this targets .NET 4.0? At any rate, here is my BaseController code:
public class BaseController: Controller
{
private string _actionKey;
private const string PermisisionList = "permissionList";
private Dictionary<string, string> _requiredActionPermissions;
private static readonly IControllerActionService<ControllerAction> _actionService;
protected new CustomPrincipal User
{
get
{
return HttpContext.User as CustomPrincipal;
}
}
public BaseController(IControllerActionService<ControllerAction> actionService)
{
_actionService = actionService;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check to see if the PermissionList is loaded and load if necessary
if(!CacheLayer.Exists(PermissionList))
{
_requiredActionPermissions = _actionService.GetControllerActionDictionary();
CacheLayer.Add(_requiredActionPermissions, PermissionList);
}
else
{
_requiredActionPermission = CacheLayer.Get<Dictionary<string, string>>(PermissionList);
}
// Get the Controller/Action of the current request
_actionKey = string.Format("{0}-{1}", filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor.ActionName);
// If the user is authenticated, grab the permissions
if(filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var userPermissions = User.Permissions;
if(!_requiredActionPermissions.Values.Any(a=>a.Equals(_actionKey, StringComparison.OrdinalIgnoreCase)))
{
return;
}
if(userPermissions.Contains(_requiredActionsPermissions.FirstOrDefault(x=>x.Value == _actionKey).Key))
{
return;
}
filterContext.Result = new RedirectResult("~/Error/ErrorUnauthorized");
return;
}
if(!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if(!_requiredActionPermissions.Values.Any(a=>a.Equals(_actionKey, StringComparison.OrdinalIgnoreCase)))
{
return;
}
}
if(filterContext.HttpContext.Request.Url == null)
{
return;
}
if(filterContext.HttpContext.Request.Url.AbsolutePath == FormsAuthentication.LoginUrl)
{
return;
}
var redirectUrl = string.Format("?returnUrl={0}", filterContext.HttpContext.Request.Url.PathAndQuery);
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + redirectUrl, true);
}
UPDATE 2: Installed .NET 4.52 on Staging Server and the application now works as intended. The problem is that I will not be able to install this on the production server. I don't understand what it is that 4.5 is fixing that 4.0 does not seem to facilitate. HELLLLLLPPP!!!!!!
The answer can be found here. To summarize, I added an extra parameter to my route config that worked in 4.5 but not in 4.0. Will follow up on the linked question. Thanks
I'm trying to get my head around designing a UI that remains responsive while a long running task is being executed.
To that end, I created a simple app in VS2012 and added the following class to it:
using System.Threading.Tasks;
namespace TaskTest
{
class Class1
{
public async Task<int> Async()
{
//simulate a long running process
for (long x = 0; x < long.MaxValue; x++) { }
return 1;
}
}
}
I then modified the main page's LoadState() method thusly:
protected override async void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
await DoLongRunningProcess();
}
private async Task DoLongRunningProcess()
{
var id = 0;
id = await new Class1().Async();
await new MessageDialog(id + "").ShowAsync();
}
I want the page to remain responsive while that process executes. However, when I run this code, the page takes a long time to load. What am I doing wrong?
TIA
async isn't magic; it just gives you the capability to write asynchronous code. In particular, async does not execute code on a background thread. You can use Task.Run to do this.
You may find my async/await intro or the MSDN documentation helpful.
That was helpful. I made the following changes and I got the result I was looking for:
class Class1
{
public int Launch()
{
//throw new Exception("Class1 exception");
for (var i = 0; i < int.MaxValue / 2; i++) ;
return 1;
}
}
...
protected async override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
var task = DoLongRunningProcess();
await task;
await new MessageDialog(task.Result + "").ShowAsync();
}
private Task<int> DoLongRunningProcess()
{
return Task.Run<int>(() => new Class1().Launch());
}
The page continues to load and after a short pause the message dialog is displayed. Now however, I need to know how to catch exceptions. If I uncomment the //throw new Exception ... line in method Launch(), it is reported as an unhandled exception. I want to catch this exception in the main UI thread (i.e., in the body of method LoadState) but I can't seem to manage it.
I have a series of web parts I need to implement in SharePoint 2010. The data provider web part uses an UpdatePanel and asynchronously makes a web service call which can potentially be slow. To keep it simple, I've put a single consumer web part on the page (Chart) which will use the consumer as its data provider.
My problem is that I can't get the consumer to wait for the provider - I get a variety of errors but all basically come back to "There is no data available". This may be because it is a Chart web part but the question also applies to the other custom parts I will be developing as they will pull the same data.
The question is: how do I either push data to my consumers when my provider is ready or somehow let them wait for my provider to have data (via polling or whatever).
Note: this is just a prototype, I haven't added error handling, etc yet.
Code is below:
[ToolboxItem(true)]
public partial class ClarityProjectGeneral : System.Web.UI.WebControls.WebParts.WebPart , IWebPartTable
{
public DataTable ProjectVitals = new DataTable(); For web part communication
// bunch of properties
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
InitializeControl();
// For web part communication
// Initialize our datatable so the chart doesn't barf
DataColumn col = new DataColumn();
col.DataType = typeof(string);
col.ColumnName = "Name";
this.ProjectVitals.Columns.Add(col);
col = new DataColumn();
col.DataType = typeof(DateTime);
col.ColumnName = "Start";
this.ProjectVitals.Columns.Add(col);
col = new DataColumn();
col.DataType = typeof(DateTime);
col.ColumnName = "End";
this.ProjectVitals.Columns.Add(col);
}
protected void Page_Load(object sender, EventArgs e)
{
loading.Visible = true;
content.Visible = false;
}
public ClarityObjectClasses.Projects GetProject(string projectID)
{
Clarity.ClarityAbstractorProject ca = new Clarity.ClarityAbstractorProject(this.Username, this.Password);
Dictionary<string, string> queryParams = new Dictionary<string, string>();
queryParams.Add("projectID", projectID);
// Class for making web service call
ClarityObjectClasses.Projects response = new ClarityObjectClasses.Projects();
response = ca.GetProject(queryParams);
return response;
}
protected void Timer1_Tick(object sender, EventArgs e)
{
if (this.ProjectID == null || this.Username == null || this.Password == null)
{
lblConfigError.Visible = true;
lblConfigError.Text = "One or more required configuration values are not set. Please check the web part configuration.";
panelProjectDetails.Visible = false;
}
else
{
loading.Visible = true;
content.Visible = false;
panelProjectDetails.Visible = true;
ClarityObjectClasses.Projects projects = GetProject(this.ProjectID);
//Assign a bunch of values
// For web part communication
LoadTable(projects.Project[0]);
Timer1.Enabled = false;
loading.Visible = false;
content.Visible = true;
}
}
/* Interface functions for Graph Chart communication */
For web part communication
protected void LoadTable(ClarityObjectClasses.Project project)
{
DataRow row = ProjectVitals.NewRow();
row["Name"] = project.name;
row["Start"] = project.start;
row["End"] = project.finish;
this.ProjectVitals.Rows.Add(row);
}
public PropertyDescriptorCollection Schema
{
get
{
return TypeDescriptor.GetProperties(ProjectVitals.DefaultView[0]);
}
}
public void GetTableData(TableCallback callback)
{
callback(ProjectVitals.Rows);
}
public bool ConnectionPointEnabled
{
get
{
object o = ViewState["ConnectionPointEnabled"];
return (o != null) ? (bool)o : true;
}
set
{
ViewState["ConnectionPointEnabled"] = value;
}
}
[ConnectionProvider("Table", typeof(TableProviderConnectionPoint), AllowsMultipleConnections = true)]
public IWebPartTable GetConnectionInterface()
{
return this;
}
public class TableProviderConnectionPoint : ProviderConnectionPoint
{
public TableProviderConnectionPoint(MethodInfo callbackMethod, Type interfaceType, Type controlType, string name, string id, bool allowsMultipleConnections)
: base(callbackMethod, interfaceType, controlType, name, id, allowsMultipleConnections)
{
}
public override bool GetEnabled(Control control)
{
return ((ClarityProjectGeneral)control).ConnectionPointEnabled;
}
}
}
Do not quite understand, but if it helps
You may not use "connectable" web-parts inside UpdatePanel,
because of lack of corresponding events to bind data on asynchronous callback.
I just stumbled across this. I had exactly the same problem trying to implement a custom webpart just as a proof to myself. I applied filters to both my webpart and a list, and then let a chart consume them. What I found was that my webpart sent the wrong data, but the list webpart worked as expected.
So I reflected the XsltListViewWebPart (or whatever it's exact name is) and I discovered that there is an IConnectionData interface. This allows you to specify the dependencies and get the correct delay binding you need. GetRequiresData indicates that there are still more connections to be consumed before the data can be requested.