IDataObjectAsyncCapability not being handled by Outlook in OLE Drag and Drop - node.js

I work on an electron application, and we would like to support dragging and dropping .msg files from our app into Outlook (or the Windows shell, or wherever, but primarily Outlook). The files will not necessarily be available before the user starts the drag and drop operation, and will need to be downloaded once the user begins the drag and drop. This means we can't use electron's inbuilt startDrag as it requires that the files are already in the filesystem, so I have implemented a node c++ addon to handle it using the OLE Drag and Drop API.
The data extraction (from my app to Outlook) needs to occur asynchronously, otherwise node's event loop will be blocked, halting the download. This means that the data extraction has to occur on another thread so that the event loop can continue actually downloading the data (currently doing the download in c++ is not an option, it has to occur in node). So I have implemented IDataObjectAsyncCapability on my DataObject, to indicate that I support asynchronous data extraction (it would be even nicer to be able to indicate that I don't support synchronous), but Outlook won't even query for the Interface, let alone do the data extraction asynchronously. The Windows shell does however query for the interface, but after the call to GetData, it still performs the extraction synchronously inside DoDragDrop, without even invoking any of the IDataObjectAsyncCapability methods. Is there something lacking in my implementation, or a specific way to encourage the drop target to start up a new thread?
Here is the meat of my DataObject implementation, if there's anything else that could be of use, I can provide.
STDMETHODIMP DataObject::QueryInterface(REFIID iid, LPVOID* ppvObject) {
if (ppvObject == nullptr) {
return E_INVALIDARG;
} else if (iid == IID_IUnknown) {
AddRef();
*ppvObject = reinterpret_cast<LPUNKNOWN>(this);
return S_OK;
} else if (iid == IID_IDataObject) {
AddRef();
*ppvObject = reinterpret_cast<LPDATAOBJECT>(this);
return S_OK;
} else if (iid == IID_IDataObjectAsyncCapability) {
AddRef();
*ppvObject = reinterpret_cast<IDataObjectAsyncCapability*>(this);
return S_OK;
}
*ppvObject = nullptr;
return E_NOINTERFACE;
}
STDMETHODIMP DataObject::GetData(LPFORMATETC queryFormat,
LPSTGMEDIUM outputStorage) {
if (queryFormat->cfFormat == CF_FILEDESCRIPTOR &&
(queryFormat->tymed & TYMED_HGLOBAL) && queryFormat->lindex <= 0 &&
queryFormat->dwAspect == DVASPECT_CONTENT) {
outputStorage->tymed = TYMED_HGLOBAL;
outputStorage->pUnkForRelease = nullptr;
outputStorage->hGlobal =
lockAndDuplicateHGLOBAL(this->groupDescriptorStorage.hGlobal);
return S_OK;
} else if (queryFormat->cfFormat == CF_FILECONTENTS &&
(queryFormat->tymed & TYMED_ISTREAM) &&
queryFormat->dwAspect == DVASPECT_CONTENT &&
queryFormat->lindex >= 0 &&
queryFormat->lindex < this->files.size()) {
// files is vector<pair<FILEDESCRIPTOR, STGMEDIUM>>
// where the STGMEDIUM is set to IStream
// Am I doing something wrong here?
auto file = this->files[queryFormat->lindex].second;
*outputStorage = file;
return S_OK;
}
return DV_E_FORMATETC;
}
STDMETHODIMP DataObject::QueryGetData(LPFORMATETC queryFormat) {
if (queryFormat->cfFormat == CF_FILEDESCRIPTOR ||
(queryFormat->cfFormat == CF_FILECONTENTS &&
(queryFormat->tymed & TYMED_HGLOBAL))) {
return S_OK;
}
return DATA_E_FORMATETC;
}
STDMETHODIMP DataObject::EnumFormatEtc(DWORD dwDirection,
LPENUMFORMATETC* ppEnumFormatEtc) {
if (dwDirection == DATADIR_GET) {
// basic implementation of IEnumFormatEtc (not mine)
return EnumFormat::Create(this->supportedFormats, this->numFormats,
ppEnumFormatEtc);
}
return E_INVALIDARG;
}
// Do the actual drag drop
// files is a list of STGMEDIUMS with IStreams. I'm using data that's readily available while
// implementing this, could that be somehow interfering and causing this problem in the first place?
DWORD dwEffect;
auto dataObject = new DataObject(groupDescriptorStorage, files);
auto dropSource = new DropSource();
// Do these DROPEFFECTS have an effect?
auto result = DoDragDrop(dataObject, dropSource,
DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect);
// By the time we get here, the data extraction would already have occured
// because it's happening inside DoDragDrop. of course, isAsyncOperation will be false
BOOL isInAsyncOperation;
dataObject->InOperation(&isInAsyncOperation);
I've followed the instructions from here as well as various forum posts I've found, but I can't seem to find anything about what to do when the drop target doesn't play ball.
I'm really stuck for ideas as to how to make this work. If the drop target isn't even querying for IDataObjectAsyncCapability then is it hopeless? Should I be returning something other than the file contents when it's first requested?

Related

Memory map and Framebuffer after using ExitBootServices

I guess the question is pretty straight forward itself.
Is there any way of detecting the memory (or getting he memory map) and framebuffer (or Graphics Output Protocol) after using ExitBootServices in UEFI in 64-bit mode? If so then how and is there any documentation?
I know that after using ExitBootServices, you the the sole owner of the entire machine.
There are many ways of getting the memory map but they all take help of the bootloader. I want to get the memory map directly from the kernel. Many websites such as OSDev and github sources use bootloaders to get the memory map and framebuffer.
After using ExitBootServices, I'm left in 64-bit mode and the only page on OSDev wiki about getting the memory map works on 32-bit architecture.
I have no language preferences, it may be in C, assembly or whatever and please don't say that it's useless or complicated or difficult to do. I just want an answer.
First, (Probably in (U)EFI) U cannot call boot services which is the only way to get what you want, you can get the memory map & Frame Buffer before ExitBootServices() and pass them to the kernel, you're lucky cause I'm not always on this platform, I've a Hybrid Boot Mechanism with LEGACY BIOS & UEFI Support, and here is an example of how to do that (I'm working on EDK2 Which is the Official Implementation of EFI)
This is how you get the frame buffer from G.O.P ( UGA documentation is removed ):
FRAME_BUFFER_DESCRIPTOR* GraphicsOutputProtocolInitialize(){
// First, we need to query the firmware of all G.O.P Protocol Instances
// (Each instance may represent a GPU or a monitor, GOP features multiple-screens
// U have asked for a simple implementation so we will use only 1 frame buffer
EFI_STATUS status = 0;
EFI_HANDLE* HandleBuffer = NULL;
UINTN NumProtocolHandles = 0;
if(EFI_ERROR(gBS->LocateHandleBuffer(
ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL,
&NumProtocolHandles, &HandleBuffer
)) || !NumProtocolHandles) return NULL;
// Then u need to create a structure that you can pass to the kernel containing information about frame buffers
FRAME_BUFFER_DESCRIPTOR* FrameBuffer = NULL;
if(EFI_ERROR(gBS->AllocatePool(
EfiLoaderData, sizeof(FRAME_BUFFER_DESCRIPTOR), (void**)&FrameBuffer
))) ALLOCATION_PROBLEM;
ZeroMemory((void*)FrameBuffer, sizeof(FRAME_BUFFER_DESCRIPTOR));
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = NULL;
status = gBS->OpenProtocol(
HandleBuffer[0], // Get first Graphics Output Protocol Instance
&gEfiGraphicsOutputProtocolGuid,
(void**)&gop,
NULL,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if(EFI_ERROR(status) || !gop)
{
return NULL;
}
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* ginfo = NULL;
UINTN info_size = 0;
// if mode is not yet set by firmware, then set first mode
if(status == EFI_NOT_STARTED || !gop->Mode){
status = gop->SetMode(gop, 0);
if(EFI_ERROR(status) || !gop->Mode->Mode)
{
return NULL;
}
}
// Now we will ask firmware for the current Video Mode
status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &ginfo);
if(status != EFI_SUCCESS || !ginfo){
return NULL;
}
// You can also list availaible video modes
for(UINTN i = 0;i<gop->Mode->MaxMode;i++) {
status = gop->QueryMode(gop, i, &info_size, &ginfo);
if(status != EFI_SUCCESS || !ginfo){
return NULL;
}
// To set the mode :
status = gop->SetMode(gop, i);
if(EFI_ERROR(status))
{
return NULL;
}
}
FrameBuffer->FrameBufferSize = gop->Mode->FrameBufferSize;
FrameBuffer->HorizontalResolution = gop->Mode->Info->HorizontalResolution;
FrameBuffer->VerticalResolution = gop->Mode->Info->VerticalResolution;
FrameBuffer->FrameBufferBase = (char*)gop->Mode->FrameBufferBase;
ZeroMemory((void*)FrameBuffer, sizeof(FRAME_BUFFER_DESCRIPTOR));
}
return FrameBuffer;
}
This is how you get the memory map :
On the first Call you will get the size of the memory map (Status must be EFI_BUFFER_TOO_SMALL)
Then you add 2 * descriptor size coz there is always 2 additional entries, then you allocate buffer for memory map
On the second call, you will get the actual memory map (Status must be EFI_SUCCESS)
Then u can list memory map entries normally
This is how u make a call to GetMemoryMap() :
EFI_MEMORY_DESCRIPTOR* memory_map = NULL;
UINTN map_size = 0, map_key = 0, descriptor_size = 0;
// Must return EFI_BUFFER_TOO_SMALL on First Call
// map_size will contain buffer size needed
EFI_STATUS s = SystemTable->BootServices->GetMemoryMap(&map_size,memory_map,&map_key,&descriptor_size,&descriptor_version);
map_size+=2*descriptor_size; // this padding must be added since there is 2 additionnal entries
However, u can always ask for a code sample, the previous code of (G.O.P) is just copy-pasted and modified to make it a little bit easier to understand, it is not compiled so it may contain some faults.

What would be the reason that I can't make the ElementIDs of these objects in Revit match ones in a Revit file?

I am creating a plugin that makes use of the code available from BCFier to select elements from an external server version of the file and highlight them in a Revit view, except the elements are clearly not found in Revit as all elements appear and none are highlighted. The specific pieces of code I am using are:
private void SelectElements(Viewpoint v)
{
var elementsToSelect = new List<ElementId>();
var elementsToHide = new List<ElementId>();
var elementsToShow = new List<ElementId>();
var visibleElems = new FilteredElementCollector(OpenPlugin.doc, OpenPlugin.doc.ActiveView.Id)
.WhereElementIsNotElementType()
.WhereElementIsViewIndependent()
.ToElementIds()
.Where(e => OpenPlugin.doc.GetElement(e).CanBeHidden(OpenPlugin.doc.ActiveView)); //might affect performance, but it's necessary
bool canSetVisibility = (v.Components.Visibility != null &&
v.Components.Visibility.DefaultVisibility &&
v.Components.Visibility.Exceptions.Any());
bool canSetSelection = (v.Components.Selection != null && v.Components.Selection.Any());
//loop elements
foreach (var e in visibleElems)
{
//string guid = ExportUtils.GetExportId(OpenPlugin.doc, e).ToString();
var guid = IfcGuid.ToIfcGuid(ExportUtils.GetExportId(OpenPlugin.doc, e));
Trace.WriteLine(guid.ToString());
if (canSetVisibility)
{
if (v.Components.Visibility.DefaultVisibility)
{
if (v.Components.Visibility.Exceptions.Any(x => x.IfcGuid == guid))
elementsToHide.Add(e);
}
else
{
if (v.Components.Visibility.Exceptions.Any(x => x.IfcGuid == guid))
elementsToShow.Add(e);
}
}
if (canSetSelection)
{
if (v.Components.Selection.Any(x => x.IfcGuid == guid))
elementsToSelect.Add(e);
}
}
try
{
OpenPlugin.HandlerSelect.elementsToSelect = elementsToSelect;
OpenPlugin.HandlerSelect.elementsToHide = elementsToHide;
OpenPlugin.HandlerSelect.elementsToShow = elementsToShow;
OpenPlugin.selectEvent.Raise();
} catch (System.Exception ex)
{
TaskDialog.Show("Exception", ex.Message);
}
}
Which is the section that should filter the lists, which it does do as it produces IDs that look like this:
3GB5RcUGnAzQe9amE4i4IN
3GB5RcUGnAzQe9amE4i4Ib
3GB5RcUGnAzQe9amE4i4J6
3GB5RcUGnAzQe9amE4i4JH
3GB5RcUGnAzQe9amE4i4Ji
3GB5RcUGnAzQe9amE4i4J$
3GB5RcUGnAzQe9amE4i4GD
3GB5RcUGnAzQe9amE4i4Gy
3GB5RcUGnAzQe9amE4i4HM
3GB5RcUGnAzQe9amE4i4HX
3GB5RcUGnAzQe9amE4i4Hf
068MKId$X7hf9uMEB2S_no
The trouble with this is, comparing it to the list of IDs in the IFC file that we imported it from reveals that these IDs do not appear in the IFC file, and looking at it in Revit I found that none of the Guids in Revit weren't in the list that appeared either. Almost all the objects also matched the same main part of the IDs as well, and I'm not experienced enough to know how likely that is.
So my question is, is it something in this code that is an issue?
The IFC GUID is based on the Revit UniqueId but not identical. Please read about the Element Identifiers in RVT, IFC, NW and Forge to learn how they are connected.

Starting Microsoft Edge from MFC with web file paramater

If I open up a console prompt I can type this command:
start msedge "d:\HTML\Verticle Alignment.HTM"
It starts Microsoft Edge and opens the web page.
So I tried to do this programmatically in a test program using MFC:
void CMFCApplication8Dlg::OnBnClickedButton1()
{
ExecuteProgram(_T("start"), _T("msedge d:\\HTML\\Verticle Alignment.HTM"));
}
BOOL CMFCApplication8Dlg::ExecuteProgram(CString strProgram, CString strArguments)
{
SHELLEXECUTEINFO se = { 0 };
MSG sMessage;
DWORD dwResult;
se.cbSize = sizeof(se);
se.lpFile = strProgram;
se.lpParameters = strArguments;
se.nShow = SW_SHOWDEFAULT;
se.fMask = SEE_MASK_NOCLOSEPROCESS;
ShellExecuteEx(&se);
if (se.hProcess != nullptr)
{
do
{
dwResult = ::MsgWaitForMultipleObjects(1, &(se.hProcess), FALSE,
INFINITE, QS_ALLINPUT);
if (dwResult != WAIT_OBJECT_0)
{
while (PeekMessage(&sMessage, nullptr, NULL, NULL, PM_REMOVE))
{
TranslateMessage(&sMessage);
DispatchMessage(&sMessage);
}
}
} while ((dwResult != WAIT_OBJECT_0) && (dwResult != WAIT_FAILED));
CloseHandle(se.hProcess);
}
if ((DWORD_PTR)(se.hInstApp) < 33)
{
// Throw error
AfxThrowUserException();
return FALSE;
}
return TRUE;
}
But when I run it I get this error message:
So how can I launch my file in Microsoft Edge? I am using the latest Windows 10 so it is Microsoft Edge Chromium.
I have seen other questions which refer to making Edge the default browser and then just "opening" the data file and it working it all out but this is not OK in this case. In my editor I have a menu flyout which lists all the installed browsers (excluding Edge for now). But i want to add Edge so need to be able to programatically start it with my file to view.
Based on the comments provided to me, and to factor in for spaces in the file name, this works:
ExecuteProgram(_T("msedge"), _T("\"d:/HTML/Verticle Alignment.HTM\""));
The program to execute needs to be msedge and not start.
The parameter needs to be wrapped in " quotes.

Elegant way to check if multiple strings are empty

How can I check if multiple strings are empty in an elegant way? This is how I currently do it:
//if one required field is empty, close the connection
if (registerRequest.Email == "") ||
(registerRequest.PhoneNumber == "")||
(registerRequest.NachName =="") ||
(registerRequest.VorName =="") ||
(registerRequest.Password =="") ||
(registerRequest.VerificationId ==""){
//Could not proceed
w.WriteHeader(UNABLE_TO_PROCEED)
w.Write([]byte("Unable to register account."))
return
}
Note: You may use the solution below if you keep the "is-valid" condition in your handler, and also if you separate your condition into another function or method.
You can create a simple helper function, which has a variadic parameter, and you can call it with any number of string values:
func containsEmpty(ss ...string) bool {
for _, s := range ss {
if s == "" {
return true
}
}
return false
}
Example using it:
if containsEmpty("one", "two", "") {
fmt.Println("One is empty!")
} else {
fmt.Println("All is non-empty.")
}
if containsEmpty("one", "two", "three") {
fmt.Println("One is empty!")
} else {
fmt.Println("All is non-empty.")
}
Output of the above (try it on the Go Playground):
One is empty!
All is non-empty.
Your example would look like this:
if containsEmpty(registerRequest.Email,
registerRequest.PhoneNumber,
registerRequest.NachName,
registerRequest.VorName,
registerRequest.Password,
registerRequest.VerificationId) {
// One of the listed strings is empty
}
Also registerRequest is a kinda long name, it could be shortened to like r. If you can't or don't want to rename it in the surrounding code and if you want to shorten the condition, you could also do something like this:
If registerRequest is a pointer (or interface), you could also write:
if r := registerRequest; containsEmpty(r.Email,
r.PhoneNumber,
r.NachName,
r.VorName,
r.Password,
r.VerificationId) {
// One of the listed strings is empty
}
Actually you can do this even if registerRequest is not a pointer, but then the struct will be copied. If registerRequest is a struct, then you can take its address to avoid having to copy it like this:
if r := &registerRequest; containsEmpty(r.Email,
r.PhoneNumber,
r.NachName,
r.VorName,
r.Password,
r.VerificationId) {
// One of the listed strings is empty
}
As Mario Santini mentioned in comment, a way to increase testability, encapsulate this logic, and decouple it from your handler method (which judging by the number of fields looks like it is at risk of changing at a different rate than your handler) could be to put this logic in a function:
func validRequest(registerRequest ?) bool {
return registerRequest.Email == "" ||
registerRequest.PhoneNumber == "" ||
registerRequest.NachName == "" ||
registerRequest.VorName == "" ||
registerRequest.Password == "" ||
registerRequest.VerificationId == ""
}
This now supports very focused, table driven tests, that can exercise what it means to be a valid request independent of any method involving writing headers.
It allows you to verify the valid/invalid path of your enclosing function, but to have very focused tests here. It also allows you to change what it means to be a valid request and verify it independent of your enclosing function.
You can use a switch:
switch "" {
case registerRequest.Email,
registerRequest.NachName,
registerRequest.Password,
registerRequest.PhoneNumber,
registerRequest.VerificationId,
registerRequest.VorName:
w.WriteHeader(UNABLE_TO_PROCEED)
w.Write([]byte("Unable to register account."))
return
}
https://golang.org/ref/spec#Switch_statements

Why code running on Azure is so slow?

I have a web app running on Azure shared web site mode. A simple method where I add items to a list and sort this list, when the list size is about 300 items, takes 0.3s on my machine and 10s after deploy (on azure machine).
Does anybody has any idea why Azure is so slow?
Is any configuration I do it wrong? I use default one but replaced FREE mode with SHARED mode because I thought this would help but it seems it does not.
UPDATE:
public ActionResult GetPosts(String selectedStreams, int implicitSelectedVisualiserId, int userId)
{
DateTime begin = DateTime.UtcNow;
List<SearchQuery> selectedSearchQueries = searchQueryRepository.GetSearchQueriesOfStreamsIds(selectedStreams == String.Empty ? new List<int>() : selectedStreams.Split(',').Select(n => int.Parse(n)).ToList());
var implicitSelectedVisualiser = VisualiserModel.ToVisualiserModel(visualiserRepository.GetVisualiser(implicitSelectedVisualiserId));
var twitterSearchQueryOfImplicitSelectedVisualiser = searchQueryRepository.GetSearchQuery(implicitSelectedVisualiser.Stream.Name, Service.Twitter, userId);
var instagramSearchQueryOfImplicitSelectedVisualiser = searchQueryRepository.GetSearchQuery(implicitSelectedVisualiser.Stream.Name, Service.Instagram, userId);
var facebookSearchQueryOfImplicitSelectedVisualiser = searchQueryRepository.GetSearchQuery(implicitSelectedVisualiser.Stream.Name, Service.Facebook, userId);
var manualSearchQueryOfImplicitSelectedVisualiser = searchQueryRepository.GetSearchQuery(implicitSelectedVisualiser.Stream.Name, Service.Manual, userId);
List<SearchResultModel> approvedSearchResults = new List<SearchResultModel>();
if (twitterSearchQueryOfImplicitSelectedVisualiser != null || instagramSearchQueryOfImplicitSelectedVisualiser != null || facebookSearchQueryOfImplicitSelectedVisualiser != null
|| manualSearchQueryOfImplicitSelectedVisualiser != null)
{
// Define search text to be displayed during slideshow;
SearchModel searchModel = new SearchModel();
// Set slideshow settings from implicit selected visualiser.
ViewBag.CurrentVisualiser = implicitSelectedVisualiser;
// Load search results from selected visualisers.
foreach (SearchQuery searchQuery in selectedSearchQueries)
{
approvedSearchResults.AddRange(
SearchResultModel.ToSearchResultModel(
searchResultRepository.GetSearchResults
(searchQuery.Id,
implicitSelectedVisualiser.Language)));
// Add defined query too.
searchModel.SearchValue += " " + searchQuery.Query;
}
// Add defined query for implicit selected visualiser.
if (twitterSearchQueryOfImplicitSelectedVisualiser != null)
searchModel.SearchValue += " " + twitterSearchQueryOfImplicitSelectedVisualiser.Query;
if (instagramSearchQueryOfImplicitSelectedVisualiser != null)
searchModel.SearchValue += " " + instagramSearchQueryOfImplicitSelectedVisualiser.Query;
if (facebookSearchQueryOfImplicitSelectedVisualiser != null)
searchModel.SearchValue += " " + facebookSearchQueryOfImplicitSelectedVisualiser.Query;
ViewBag.Search = searchModel;
// Also add search results from implicit selected visualiser
if (twitterSearchQueryOfImplicitSelectedVisualiser != null)
approvedSearchResults.AddRange(SearchResultModel.ToSearchResultModel(searchResultRepository.GetSearchResults(twitterSearchQueryOfImplicitSelectedVisualiser.Id, implicitSelectedVisualiser.Language)));
if (instagramSearchQueryOfImplicitSelectedVisualiser != null)
approvedSearchResults.AddRange(SearchResultModel.ToSearchResultModel(searchResultRepository.GetSearchResults(instagramSearchQueryOfImplicitSelectedVisualiser.Id, implicitSelectedVisualiser.Language)));
if (facebookSearchQueryOfImplicitSelectedVisualiser != null)
approvedSearchResults.AddRange(SearchResultModel.ToSearchResultModel(searchResultRepository.GetSearchResults(facebookSearchQueryOfImplicitSelectedVisualiser.Id, implicitSelectedVisualiser.Language)));
if (manualSearchQueryOfImplicitSelectedVisualiser != null)
approvedSearchResults.AddRange(SearchResultModel.ToSearchResultModel(searchResultRepository.GetSearchResults(manualSearchQueryOfImplicitSelectedVisualiser.Id, implicitSelectedVisualiser.Language)));
// if user selected to show only posts from specific number of last days.
var approvedSearchResultsFilteredByDays = new List<SearchResultModel>();
if (implicitSelectedVisualiser.ShowPostsFromLastXDays != 0)
{
foreach (SearchResultModel searchResult in approvedSearchResults)
{
var postCreatedTimeWithDays = searchResult.PostCreatedTime.AddDays(implicitSelectedVisualiser.ShowPostsFromLastXDays + 1);
if (postCreatedTimeWithDays >= DateTime.Now)
approvedSearchResultsFilteredByDays.Add(searchResult);
}
}
else
{
approvedSearchResultsFilteredByDays = approvedSearchResults;
}
// Order search results (posts to be displayed by created datetime).
var approvedSearchResultsOrdered = new List<SearchResultModel>();
if (implicitSelectedVisualiser.PostsSortOrder == PostsSortOrder.CREATED_DATE_ASC)
{
approvedSearchResultsOrdered = approvedSearchResultsFilteredByDays.OrderBy(s => s.PostCreatedTime).ToList(); ;
}
else if (implicitSelectedVisualiser.PostsSortOrder == PostsSortOrder.CREATED_DATE_DESC)
{
approvedSearchResultsOrdered = approvedSearchResultsFilteredByDays.OrderByDescending(s => s.PostCreatedTime).ToList(); ;
}
else if (implicitSelectedVisualiser.PostsSortOrder == PostsSortOrder.RANDOM)
{
var rnd = new Random();
approvedSearchResultsOrdered = approvedSearchResultsFilteredByDays.OrderBy(x => rnd.Next()).ToList();
}
// Load background images;
var visualiserImages = visualiserImageRepository.GetImages(implicitSelectedVisualiser.Id);
//foreach (SearchResultModel searchResultModel in approvedSearchResultsOrdered)
//{
// searchResultModel.BackgroundImagePath = TwitterUtils.GetRandomImageBackgroundForDisplay(visualiserImages);
//}
ViewBag.BackgroundImagePath = TwitterUtils.GetRandomImageBackgroundForDisplay(visualiserImages);
approvedSearchResults = approvedSearchResultsOrdered;
}
DateTime end = DateTime.UtcNow;
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception(String.Format("User {0}: Preparing {1} posts for visualiser took {2} seconds", MySession.Current.LoggedInUserName, approvedSearchResults.Count(), (end - begin).TotalMilliseconds / 1000)));
return PartialView("_DisplayPostsNew", approvedSearchResults);
}
This isn't surprising actually. The servers used in Windows Azure are currently mostly 1.6 GHz machines. The larger sized machine you use the more cores you get, but they are all the same speed. This likely is a much slower CPU than the development machine you use.
On Windows Azure Web Sites when you move to Shared mode you are still in a multi-tenant environment, so you could be seeing some noisy neighbors here. The difference between Free and Shared is that many of the quotas for free are removed since you are paying. When you move to Standard then you are assigned a Virtual Machine dedicated to your web sites (up to 100 of them), so that is the best case scenario since you are the only one using the resources at that point.
There was a thread on this on the MSDN forums a while back : http://social.msdn.microsoft.com/Forums/windowsazure/en-US/0d0a3a88-eac4-4b9e-8b10-4a547cbf653b/performance-of-azure-servers-slow-cpus?forum=windowsazuredevelopment
They have started offering different hardware configurations with more memory for Virtual Machines and Cloud Services and such, but I'm not sure the CPUs have been changed. It's hard to find the CPU stated on WindowsAzure.com anymore, but on the pricing calculator for Web Sites it references 1.6Ghz machines when you move the slider to Standard.
Actually I found the issue.
Locally, I tested with a few hundreds of records in my DB while in Azure DB I have over 70 000 records in that table which affects performance of the algorithm...
One mistake I did in the code above: I have filtered records from DB by specific date AFTER taking all out. By filtering directly in Linq, I increased the performance from 10s to 0.3s in Azure too.

Resources