Sharepoint 2010 Web Part Communication - How to make consumer wait for the provider - sharepoint-2010

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.

Related

Accessing HttpContext.Session from static method

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 to call async GetDataFromServer in Xamarin.Forms?

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

Google+ unable to insert moment - A Year and 6 Revisions After

NOTE: Using the Sign-in button is NOT an option
A year ago I was having a problem creating a moment. Back then I was using version 1.2 of the Google+ API .Net client. As I described in this post, I had it working although the code failed to insert a moment from time to time. I was hoping that the process is more stable and easier to implement now, and it seems like it as can be seen in the example that you can download here - the current version as of this writing is v1.8. So I created a simple project following the SimpleOAuth2 sample in the download, but implementing Google+. This is the code I came up:
public partial class _Default : System.Web.UI.Page
{
private PlusService service;
// Application logic should manage users authentication.
// This sample works with only one user. You can change
// it by retrieving data from the session.
private const string UserId = "user-id";
protected void Page_Load(object sender, EventArgs e)
{
GoogleAuthorizationCodeFlow flow;
var assembly = Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(
"GPlusSample.client_secrets.json"))
{
flow = new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
DataStore = new FileDataStore("GPlusSample.Store"),
ClientSecretsStream = stream,
//
// Tried only this scope but it did not work
//Scopes = new[] { PlusService.Scope.PlusMe }
//
// I tried the following: but did not work either
//Scopes = new[] { PlusService.Scope.PlusMe,
// "https://www.googleapis.com/auth/plus.moments.write" }
//
// I tried this as well and it failed
//Scopes = new[] { PlusService.Scope.PlusLogin }
//
// Maybe this... but still no joy
Scopes = new[] { PlusService.Scope.PlusLogin,
PlusService.Scope.PlusMe }
});
}
var uri = Request.Url.ToString();
var code = Request["code"];
if (code != null)
{
var token = flow.ExchangeCodeForTokenAsync(UserId, code,
uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result;
// Extract the right state.
var oauthState = AuthWebUtility.ExtracRedirectFromState(
flow.DataStore, UserId, Request["state"]).Result;
Response.Redirect(oauthState);
}
else
{
var result = new AuthorizationCodeWebApp(flow, uri, uri)
.AuthorizeAsync(UserId, CancellationToken.None).Result;
if (result.RedirectUri != null)
{
// Redirect the user to the authorization server.
Response.Redirect(result.RedirectUri);
}
else
{
// The data store contains the user credential,
// so the user has been already authenticated.
service = new PlusService(new BaseClientService.Initializer
{
ApplicationName = "Plus API Sample",
HttpClientInitializer = result.Credential
});
}
}
}
/// <summary>Gets the TasksLists of the user.</summary>
public async System.Threading.Tasks.Task InsertMoment()
{
try
{
var me = service.People.Get("me").Execute();
var request = service.Moments.Insert(new Moment()
{
Target = new ItemScope {
Id=Guid.NewGuid().ToString(),
Image="http://www.google.com/s2/static/images/GoogleyEyes.png",
Type="",
Name = "test message",
Description="test",
Text="test message",
},
Type = "http://schemas.google.com/AddActivity",
}, me.Id, MomentsResource.InsertRequest.CollectionEnum.Vault);
var response =await request.ExecuteAsync();
output.Text = "<h1>" + response.Id + "</h1>";
}
catch (Exception ex)
{
var str = ex.ToString();
str = str.Replace(Environment.NewLine, Environment.NewLine + "<br/>");
str = str.Replace(" ", " ");
output.Text = string.Format("<font color=\"red\">{0}</font>", str);
}
}
protected async void createMomentButton_Click(object sender, EventArgs e)
{
await InsertMoment();
}
}
That code always give me a 401 Unauthorized error, even if I have the Google+ API turned on for my project. Here's the actual error I got:
The service plus has thrown an exception: Google.GoogleApiException:
Google.Apis.Requests.RequestError Unauthorized [401] Errors [
Message[Unauthorized] Location[ - ] Reason[unauthorized]
Domain[global] ]
It's interesting to see that the insert moment is failing even though the call to People.Get("me") works - get("me") works with all of the scope combinations I listed above. It's important to note that each time I try a new scope, I first log out of my Google account and delete the access token that is stored in GPlusSample.Store.
EDIT
I tried setting just the Url instead of individual items as suggested by Ian and I got the exact same error.
var request = service.Moments.Insert(new Moment()
{
Target = new ItemScope {
Url = "https://developers.google.com/+/web/snippet/examples/thing"
},
Type = "http://schemas.google.com/AddActivity",
}, me.Id, MomentsResource.InsertRequest.CollectionEnum.Vault);
var response =await request.ExecuteAsync();
https://www.googleapis.com/auth/plus.login is the right scope for writing moments, but you need to have requested the specific app activity types you want to write as well. The parameter for this is request_visible_actions, and it takes a space separated list of arguments of the types (Listed on https://developers.google.com/+/api/moment-types/ - e.g. http://schemas.google.com/AddActivity).
The client library may not have a method for adding request_visible_actions, so you may have to add it on to the auth URL you redirect the user to manually (remember to URLencode the app activity type URLs!)

Windows 8 app, getting data on db change

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();
}
}

C# Web Cam with Remoting

My project is about Remoting and i want to add a webcam component to it. Here it goes: I have 3 project in my solution... Client, Server, Remote.dll. In Remote.dll is a common class which has methods works in server machine. When i call these methods from Client it executes in server side. So now my question is i put the code of Webcam in remote.dll and it has an event called "video_NewFrame" which it works everytime when webcam catch an image. But i cant reach to the images from my Client side because when code drops to this event it executes infinitely
and my timer in Client side doesnt work as well. I tried to assing image to my global variable but whenever code goes to client and comes to Remote.dll again my variable is null...
How can i reach simultaneously captured images from my client? here is my code:
(i use AForge framework for webcam)
private bool DeviceExist = true;
private FilterInfoCollection videoDevices;
private VideoCaptureDevice videoSource = null;
public bool WebCamStart(int DeviceIndex)
{
if (DeviceExist)
{
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
//string myDevice = videoDevices[0].Name;
videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
CloseVideoSource();
videoSource.DesiredFrameSize = new Size(640, 480);
//videoSource.DesiredFrameRate = 10;
videoSource.Start();
return true;
}
else return false;
}
public Bitmap lastImg;
private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
//in executes infinitely when execution comes here and i cant reach from Cliend side...
}
public string getFPS()
{
return videoSource.FramesReceived.ToString();
}
public void CloseVideoSource()
{
if (!(videoSource == null))
if (videoSource.IsRunning)
{
videoSource.SignalToStop();
videoSource.Stop();
videoSource = null;
}
}
public string getCamList()
{
string result = "No Device Found";
try
{
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
//comboBox1.Items.Clear();
if (videoDevices.Count == 0)
throw new ApplicationException();
DeviceExist = true;
foreach (FilterInfo device in videoDevices)
{
//comboBox1.Items.Add(device.Name);
result = device.Name;
return result;
}
//comboBox1.SelectedIndex = 0; //make dafault to first cam
}
catch (ApplicationException)
{
DeviceExist = false;
//comboBox1.Items.Add("No capture device on your system");
return "No capture device on your system";
}
return result;
}
// and my client side...
private void timerWebCam_Tick(object sender, EventArgs e)
{
//lblFPS.Text ="Device Running... " + remObj.getFPS() + " FPS";
pictureBox1.Image = remObj.lastImg;
}