How to cache in sharepoint 2010 based on login user - caching

I need to store dynamically created xmldatasource for menu in cache based on the login user. The below code is returning the same data for all the users since i did not mentioned the login user. where do I need to mention the login name while add a cache? also i want to reset or remove the cache while add a new site from event receiver since sitemap has to recreate.
private static object _lock = new object();
public XmlDocument CacheData()
{
XmlDocument item;
lock (_lock)
{
item = (XmlDocument)Cache["SiteMapCache"];
if (item == null)
{
using (SPSite site = new SPSite(SPContext.Current.Site.Url))
{
SPWebApplication webapp = site.WebApplication;
item = GenerateMenu(webapp);
}
Cache.Add("SiteMapCache",
item, null,
DateTime.Now.AddMinutes(1),
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default,
null);
}
return item;
}
}

one Not sure why you would be doing this. As if you built a Sharepoint OOTB publishing site, additional sub site's which a user had access to would appear. If you went down the root of caching each you could end up with loads of entries in the cache, as each one would need to be different.
Else add the USERName to the CacheKey name.
Then change your code so it first checks to see if the "SiteMapCache"+Spcontext.Current.Web.CurrentUser.name is null, if it is fetch it and store it as cache.
You will need another cached item, to store a list of users who have been added to the cache.
Then if a new site is created, loop through each one and set cached item to null.

Related

Uniquely identify Mailitem

I need to store a model for every used MailItem. For this I've written following Method
private readonly static Dictionary<string, PermitCustomPaneViewmodel> ViewmodelLookup = new Dictionary<string, PermitCustomPaneViewmodel>();
public static PermitCustomPaneViewmodel CreateOrGet(MailItem c)
{
if (c.EntryID == null)
c.Save();
if (!ViewmodelLookup.ContainsKey(c.EntryID))
{
var vm = new PermitCustomPaneViewmodel(c);
c.Unload += () => ViewmodelLookup.Remove(c.EntryID);
ViewmodelLookup.Add(c.EntryID, vm);
}
return ViewmodelLookup[c.EntryID];
}
When the Model already exists, I look it up and return it. If it was not created, I create it and remove the entry after the MailItem will be unloaded.
However I have observed that the MailItem object will not be vailid all the time untill unload is called. In order to reliable identify the MailItem I used the EntryID. The problem now is this only works if the Item is saved.
So currently I save the Item if no EntryID was found. But this automaticly saves the item under draft.
Is there a way to distingush MailItem's that is not saved in a way so it can be used in a Dictionary<,>.
New created items don't have the EntryID property set. Get the ID assigned by the store provider you must save it. If you need to identify a new MailItem object you may consider adding a user property to the item by using the UserProperties.Add method which reates a new user property in the UserProperties collection. For example:
Sub AddUserProperty()
Dim myItem As Outlook.ContactItem
Dim myUserProperty As Outlook.UserProperty
Set myItem = Application.CreateItem(olContactItem)
Set myUserProperty = myItem.UserProperties _
.Add("LastDateSpokenWith", olDateTime)
myItem.Display
End Sub
Be aware, the Entry ID changes when an item is moved into another store, for example, from your Inbox to a Microsoft Exchange Server public folder, or from one Personal Folders (.pst) file to another .pst file. Solutions should not depend on the EntryID property to be unique unless items will not be moved. Basically it works fine as long as the message is staying in its parent folder or it may be changed if the Outlook item is moved to a different folder (depends on the store provider).
You may also consider using the message id from the message MIME header (PR_INTERNET_MESSAGE_ID and PR_TRANSPORT_MESSAGE_HEADERS). But they are not set on newly created items. These properties are available on the message received from an SMTP server or through the SMTP connector.

Dnn 8: caching module settings

I put some module settings via
var moduleController = new ModuleController();
moduleController.UpdateModuleSetting(moduleId, "key", value);
Later if I try to access the setting using
var rcModule = ModuleController.Instance.GetModuleByDefinition(PortalSettings.PortalId, "MyModule");
var value = rcModule.ModuleSettings["value"]?.ToString() ?? string.Empty;
the same value is returned (even if I resave the setting) until I clear the app cache. The value is correct after every saving settings in the database but not in the module. I also tried to add ModuleController.SynchronizeModule(moduleId); to my save settings method but it didn't help. Module and page cache both disabled.
What's wrong?
You are creating a new instance of moduleController, not getting the existing one from memory.
You can clear the cache programmatically.
DotNetNuke.Common.Utilities.DataCache.ClearModuleCache(TabId);
DotNetNuke.Common.Utilities.DataCache.ClearTabsCache(PortalId);
DotNetNuke.Common.Utilities.DataCache.ClearPortalCache(PortalId, false);
Or get the correct instance and edit the properties.
ModuleInfo moduleInfo = ModuleController.Instance.GetModule(ModuleId, TabId, false);
moduleInfo.ModuleTitle = "New Title";
ModuleController.Instance.UpdateModule(moduleInfo);

Breeze entity state doesn't change after saving

My application uses BreezeJS, ASP.NET Web API and EF.
I'm trying to save an object using breeze, as follows:
var saveOptions = this.manager.saveOptions.using({ resourceName: "SaveLocationSettings", tag: clientId, allowConcurrentSaves: true });
var obj = self.manager.saveChanges(null, saveOptions).then(saveSucceeded, saveFailed);
I'm using a custom save method on the server side, which returns a SaveResult object. However, on the client side, the entity manager still maintains the modified state.
My controller on the Web API is a BreezeController.
According to the breeze documentation, if your custom method has the signature similar to the Breeze SaveChanges() method, it should work similar to SaveChanges() method. However, if I use the breeze SaveChanges(), the entity state gets updated properly. But my custom endpoint save does not update the entity state, although the data is saved in the database.
UPDATE:
After some investigation, I figured that this happens only with one entity type that goes to this particular save endpoint. Say, I have a 'location' object, with a collection of 'availability' associated with it, as follows:
Class Location {
public Location() {
this.Availabilities = new HashSet<Availability>();
}
}
Now from the client side, if I only change some property of the Location object, it handles the hasChanges property correctly. But if I change the Availability only or Availability along with another property of the location, then the hasChanges is not updated properly on client side.
This is my server side code that's called from the WebAPI controller:
public SaveResult SaveLocation(Location l, List<MaxAvailability> maxAvailability, int changedBy)
{
// Create a SaveResult object
// we need to return a SaveResult object for breeze
var keyMappings = new List<KeyMapping>();
var entities = new List<object> {l, maxAvailability};
var saveResult = new SaveResult() { Entities = entities, KeyMappings = keyMappings, Errors = null };
try
{
if (l.LocationId == -1)
{
// add new location
l.LocationId = this.AddNewLocationWithItsAssociatedData(l, maxAvailability, changedBy);
}
else
{
// do changes to the existing location
this.UpdateExistingLocationWithItsAssociatedData(l, maxAvailability, changedBy);
}
}
catch (DbEntityValidationException ex)
{
// Log the error and add the errors list to SaveResult.
// Retrieve the error messages as a list of strings.
saveResult.Errors = this.GetErrors(ex);
}
return saveResult;
}
I think I figured out the answer. It was due to some bad practice in my code. When modifying the availability of an existing location, instead of updating the existing record, I was deleting the existing record and adding a new one. This was causing the client side availability object and the database object to have two different states. Once it was resolved, the hasChanges() state was behaving as expected.

How to save a record and immediately use its GUID

I'm executing some javascript from a ribbon button and what I want to do is save the record that I am creating and then immediately use its GUID for some code a bit further on. Each time I try it the GUID is coming back null even though I'm requesting it after the record has been saved. If I try the button again after I've saved it then it works, but not as I'm saving it.
Is there a way to do this?
function RibbonButton_AddProduct()
{
//Save the Record
Xrm.Page.data.entity.save();
LoadProductCreate();
}
function LoadProductCreate()
{
var serverUrl;
var errorMessage = "Context to retrieve the Server URL is not available.";
if (typeof GetGlobalContext != "undefined"){
serverUrl = GetGlobalContext().getServerUrl();
} else {
if (typeof Xrm != "undefined"){
serverUrl = Xrm.Page.context.getServerUrl();
} else {
alert(errorMessage);
return;
}
}
if (serverUrl.match(/\/$/)){
serverUrl = serverUrl.substring(0, serverUrl.length - 1);
}
var recordId = Xrm.Page.data.entity.getId();
alert(recordId);
var url = serverUrl + "/main.aspx?etc=10030&extraqs=%3f_CreateFromId%3d%"+recordId
+"%257d%26_CreateFromType%3d10029%26etc%3d10030%26"
+"pagemode%3diframe%26preloadcache%3d1345465354543&pagetype=entityrecord";
window.open(url);
}
Here’s a different approach to solving this problem.
What you are trying to do is ‘working against the system’ - you are effectively making two save buttons. In the rest of Crm when the Id is required for a ribbon button the record must first be saved. E.g. you can’t use the dialog or workflow buttons on an unsaved record, you also can’t 'add new/existing' to an unsaved record.
So my solution would be to disable the button on unsaved forms, force the user to save the record manually and then allow them to use your button - this is the way Crm is meant to be used, and is the way the rest of Crm will work.
You should not work against the system, you should work with it, you have a product to customise and extend – not change.
If this doesn’t meet your requirement I would suggest uses Greg’s suggestion (1) of having flags, though it sounds a bit messy - but then this is a requirement that inherently is.
You could try one of two things:
Add a hidden boolean attribute to your form(e.g. "new_launchProductCreate"), set it in code prior to save and then read it onLoad.
Instead of setting the value prior to create (and therefore potentially commiting it to the database), you could create a plugin registered against the "Create" step of your record that injects a boolean value into the Entity.Attributes collection as the record is returned to the user. This would prevent the value persisting into the database and running every time your form loads.
You can instead use AJAX to reset the value as you launch your onLoad code so that it doesn't trigger on every form load
Assign the record guid manually, use AJAX to save your record, pop your new window using th enew guid and then reload your original form (so that the form is no longer in an "unsaved" state).
At the risk of being proven wrong as I cannot verify this right away... you will need to save and then reload the page.
The value stored in Xrm.Page.data.entity.getId() is set when the page is loaded/initialised and hence won't be updated when you access it after you have called Save().
It is also why it does work when you reload the page.
Perhaps you could call save and then reload the window adding a querystring variable of your own, to indicate that this event has just occurred?
e.g.
function DoSomething() {
//do your stuff
Xrm.Page.data.entity.save();
//something like - sure someone can do better!
window.location = window.location.href + '&foo=bar';
}
and then register something like this onFormLoad
function OnLoad() {
var queryStringParms = Xrm.Page.context.getQueryStringParameters();
//test to see if your query string param exists here
for (var i in queryStringParams) {
//if you find query string, do extra processing here
}
}

Saving Data Locally and Remotely (Syncing)

When data is entered, it ultimately needs to be saved remotely on a server. I do want the app to work if there is no data connection at the time also, so I need to save everything locally on the phone too. The app can then sync with the server when it gets a connection.
This brings up a little issue. I'm used to saving everything on the server and then getting the records back with id's generated from the server for them. If there is no connection, the app will save locally to the phone but not the server. When syncing with the server, I don't see a way for the phone to know when a record comes back which locally record it's associated with. There isn't enough unique data to figure this out.
What is the best way to handle this?
One way I've been thinking is to change the id of the records to a GUID and let the phone set the id. This way, all records will have an id locally, and when saving to the server, it should still be a unique id.
I'd like to know what other people have been doing, and what works and what doesn't from experience.
This is how we done with a first windows phone 7 app finished few days ago with my friend.
It might not be the best solution but 'till additional refactoring it works just fine.
It's an application for a web app like a mint.com called slamarica.
If we have feature like save transaction, we first check if we have connection to internet.
// Check if application is in online or in offline mode
if (NetworkDetector.IsOnline)
{
// Save through REST API
_transactionBl.AddTransaction(_currentTransaction);
}
else
{
// Save to phone database
SaveTransactionToPhone(_currentTransaction);
}
If transaction is successfully saved via REST, it responses with transaction object and than we save it to local database. If REST save failed we save data to local database.
private void OnTransactionSaveCompleted(bool isSuccessful, string message, Transaction savedTransaction)
{
MessageBox.Show(message);
if(isSuccessful)
{
// save new transaction to local database
DatabaseBl.Save(savedTransaction);
// save to observable collection Transactions in MainViewModel
App.ViewModel.Transactions.Add(App.ViewModel.TransactionToTransactionViewModel(savedTransaction));
App.ViewModel.SortTransactionList();
// Go back to Transaction List
NavigationService.GoBack();
}
else
{
// if REST is failed save unsent transaction to Phone database
SaveTransactionToPhone(_currentTransaction);
// save to observable collection Transactions in MainViewModel
App.ViewModel.Transactions.Add(App.ViewModel.TransactionToTransactionViewModel(_currentTransaction));
App.ViewModel.SortTransactionList();
}
}
Every Transaction object has IsInSync property. It is set to false by default until we got confirmation from REST API that it's saved successful on the server.
User has ability to refresh transactions. User can click on a button Refresh to fetch new data from the server. We do the syncing in the background like this:
private void RefreshTransactions(object sender, RoutedEventArgs e)
{
if (NetworkDetector.IsOnline)
{
var notSyncTransactions = DatabaseBl.GetData<Transaction>().Where(x => x.IsInSync == false).ToList();
if(notSyncTransactions.Count > 0)
{
// we must Sync all transactions
_isAllInSync = true;
_transactionSyncCount = notSyncTransactions.Count;
_transactionBl.AddTransactionCompleted += OnSyncTransactionCompleted;
if (_progress == null)
{
_progress = new ProgressIndicator();
}
foreach (var notSyncTransaction in notSyncTransactions)
{
_transactionBl.AddTransaction(notSyncTransaction);
}
_progress.Show();
}
else
{
// just refresh transactions
DoTransactionRefresh();
}
}
else
{
MessageBox.Show(ApplicationStrings.NETWORK_OFFLINE);
}
}
private void DoTransactionRefresh()
{
if (_progress == null)
{
_progress = new ProgressIndicator();
}
// after all data is sent do full reload
App.ViewModel.LoadMore = true;
App.ViewModel.ShowButton = false;
ApplicationBl<Transaction>.GetDataLoadingCompleted += OnTransactionsRefreshCompleted;
ApplicationBl<Transaction>.GetData(0, 10);
_progress.Show();
}
OnTransactionRefreshCompleted we delete all transaction data in local database and get the latest 10 transactions. We don't need all the data, and this way user have synced data. He can always load more data by taping load more at the end of transaction list. It's something similar like those twitter apps.
private void OnTransactionsRefreshCompleted(object entities)
{
if (entities is IList<Transaction>)
{
// save transactions
var transactions = (IList<Transaction>)entities;
DatabaseBl.TruncateTable<Transaction>();
DatabaseBl.Save(transactions);
((MainViewModel) DataContext).Transactions.Clear();
//reset offset
_offset = 1;
//update list with new transactions
App.ViewModel.LoadDataForTransactions(transactions);
App.ViewModel.LoadMore = false;
App.ViewModel.ShowButton = true;
}
if (entities == null)
{
App.ViewModel.ShowButton = false;
App.ViewModel.LoadMore = false;
}
// hide progress
_progress.Hide();
// remove event handler
ApplicationBl<Transaction>.GetDataLoadingCompleted -= OnTransactionsRefreshCompleted;
}
Caveat - I haven't tried this with windows phone development but use of GUID identities is something I usually do when faced with similar situations - eg creating records when I only have a one-way connection to the database - such as via a message bus or queue.
It works fine, albeit with a minor penalty in record sizes, and can also cause less performant indexes. I suggest you just give it a shot.

Resources