Deleting a document using ContentService API doesn't refresh cache - umbraco

In Umbraco 7, I used ContentService API to insert, update or delete documents. After inserting or updating a document, the correct content shows immediately, but after deleting, removed data can be viewed because of the cache. To clear cache, I delete DistCache folder's data from App_data.
How can I refresh the cache programmatically?
Here is my code:
public string DeleteStudent(int studentID)
{
IContentService service = ApplicationContext.Current.Services.ContentService;
if (studentID != 0)
{
IContent student = service.GetById(studentID);
if (student != null && student.ContentType.Alias == "dtStudent")
{
service.Delete(student);
return "Done!";
}
}
return "Error!";
}

Normally you shouldn't have to update the cache manually after a delete (you can check the log file for possible errors)
If you want to do a manual refresh, you can do so by calling the following method
umbraco.library.RefreshContent();
Note: In an Umbraco instance with many nodes the method is very slow

As #Harvey said in the comment, using service.MoveToRecycleBin works
fine. That method will unpublish the content, then move it to the
recycle bin.
Final code:
public string DeleteStudent(int studentID)
{
IContentService service = ApplicationContext.Current.Services.ContentService;
if (studentID != 0)
{
IContent student = service.GetById(studentID);
if (student != null && student.ContentType.Alias == "dtStudent")
{
service.MoveToRecycleBin(student);
return "Done!";
}
}
return "Error!";
}

You can also clear the cache in the Umbraco back office if you right click on the ellipses by the Content tab and then click "Republish entire site". This is technically not a programmatic way but it's quick/easy and does the job!
https://i.stack.imgur.com/MIUOh.jpg

Related

MVC download of clickonce Setup.exe and display confirmation view

I'll start by saying i'm a C# MVC newbie, but I've set up a site with Identity Management and extended the database with some custom tables to store additional info about my users, so I'm not a total neophyte. I've been working on a VB WPF application that I want to deploy from my new website and that is where I'm running into an issue.
I've created a new controller (User) and a couple of views (Download) & (Setup). I created a downloadmodel used by the download view.
In abstract what I am doing is displaying the download view (get) which has three checkboxes to confirm the user has read the Overview, Installation, and Terms of Service. These are boolean values in the model. I also have a string response message in the model, that displays just above the submit button. Here is the model:
public class DownloadModel
{
public bool Overview { get; set; }
public bool Installation { get; set; }
public bool TermsOfService { get; set; }
public string Response { get; set; }
public DownloadModel()
{
Overview = false;
Installation = false;
TermsOfService = false;
Response = "After checking boxes click the button to begin installation";
}
}
My User Controller handles the Get to initially display the download view, and then in the Post it checks to see if all the checkboxes were ticked, if not it updates the response message and returns the view.
If the checkboxes are all checked then it pulls the subscriber (which must exist because it was created when the user verified their e-mail via the account controller - identity management), then proceeds to update the subscriber with the original (if new) or last download date(s). At this point I want to begin downloading the clickonce setup.exe file, before returning the setup view.
[Authorize]
public class UserController : Controller
{
// GET: User/Download
public ActionResult Download()
{
return View(new DownloadModel { });
}
// Post: User/Download
[HttpPost]
public ActionResult Download(DownloadModel downloadcheck)
{
if (!ModelState.IsValid)
{
return View(downloadcheck);
}
//check to see if all the boxes were checked
if (downloadcheck.Overview == true &
downloadcheck.Installation == true &
downloadcheck.TermsOfService == true)
{
//yes - so let's proceed
//first step is to get the subscriber
Subscriber tSubscriber = new Subscriber();
tSubscriber.Email = User.Identity.Name;
bool okLoad = tSubscriber.LoadByEmail();
if (okLoad == false)
{
//we have a real problem. a user has logged in but they are not yet
//a valid subscriber?
throw new Exception("Subscriber not found");
}
// update subscriber with download in process...
if (tSubscriber.OriginalDownload == DateTime.MinValue)
{
tSubscriber.OriginalDownload = DateTime.Now;
tSubscriber.LastDownload = tSubscriber.OriginalDownload;
}
else
{
tSubscriber.LastDownload = DateTime.Now;
}
if (tSubscriber.UpdateDownloaded() == false)
{
//update of download dates failed
//another problem that shouldnt occur.
downloadcheck.Response = "A problem occured downloading your setup."
+ "Try again. If this error continues please contact support.";
return View(downloadcheck);
}
//download dates have been updated for the subscriber so let's start the download!
//THIS IS WHERE I NEED TO BEGIN THE DOWNLOAD
return View("Setup");
}
else
{
// all boxes were not checked - update message
downloadcheck.Response = "Please confirm you have reviewed the above information "
+ "by checking all of the boxes before clicking on the button.";
return View(downloadcheck);
}
}
}
The download view is pretty straight forward, and the setup view simply confirms the download was started and provides a link to the help-setup page.
I'm really a bit lost here. I thought I'd plug in a return new filepathresponse, but I can't do that and return the setup view.
My other thought was to somehow trigger the download of my /xxx/setup.exe from within the setup view as it is returned - but I'm at a loss as to how to accomplish this.
I'll be the first to admit that my mvc c# code is probably overly verbose and my approach to how I've done this may be totally wrong, but I'm just scrambling to get this done so I can deploy my WPF app to select Beta users for testing. It's been a long time living off savings and I can smell go-live from here.
One final note, I'm using setup.exe clickonce deployment of my wpf app for simplicity, as there are .net and localsqldb prerequisites, but I will not be using automated updates - not that this is really relevant.
Appreciate all input and advice.
After more digging and hacking I've found a solution that works. Firstly in my setup view (confirmation page) I added a simple script to initiate a new function in my user controller:
<script type="text/javascript">
window.location.href = "/user/sendfile/"
</script>
The controller change was simple too. For testing I just used a txt file.
// User/SendFile
public ActionResult SendFile()
{
string path = #"~/xxxx/anyfile.txt";
string content = "application/txt";
//string content = "application/x-ms-application";
return new FilePathResult(path, content)
{
FileDownloadName = "mynewtext.txt"
};
}
What is really interesting about this solution is that the FileDownloadName is what the file content is downloaded as. So in this way I can refer to the actual setup.exe file in the path, but then name the downloaded exe anything I want. Bonus :)

SaveChanges is not working on modify the data

when i try to save the the new Student object. it works fine but when i am trying to modify the data, it doesn't update the data. Instead none error is thrown on SaveChanges.
i am using code first approach (using mysql provider) and here it is the complete source.
https://www.dropbox.com/s/e34frntq8u5gsmh/SchoolManagementSystem.rar?dl=0
my tired code is this :
public ActionResult Create(Student student, HttpPostedFileBase Image)
{
try
{
if (ModelState.IsValid)
{
student = db.Students.Find(student.ID);
if (student.ID > 0)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
}
else
{
if (student.Basic == null) student.Basic = new BasicInformation();
if (Image != null && Image.ContentLength > 0)
{
student.Basic.PictureUrl = Image.FileName;
string path = Server.MapPath(("~/Images/"));
Image.SaveAs(path + Image.FileName);
}
db.Students.Add(student);
db.SaveChanges();
}
return RedirectToAction("StudentList");
}
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(student);
}
This seems to be the modify scenario :
if (student.ID > 0)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
}
Eliminate this line of code :
db.Entry(student).State = EntityState.Modified;
and instead add code that updates the object with the new modified values.
Hope this helps.
By this line
student = db.Students.Find(student.ID);
you overwrite the student that enters the method and you lose all changes. You can do two things in stead:
Only remove the line. Your code will now attach the modified student as EntityState.Modified.
Fetch the original student from the database and copy the modified values to it:
var studentOrg = db.Students.Find(student.ID);
db.Entry(studentOrg).CurrentValues.SetValues(student);
(both SaveChanges calls can be moved to one just before return RedirectToAction...)
Option 1 will generate an update statement containing all Student's fields, but not have a roundtrip to get the original record.
Option 2 has this roundtrip, but only updates modified fields. This (option 2) can be beneficial when changes are audited or when concurrency should be minimized.

Why isn't my updated observable List reflected in the template?

I've got:
my-app
community-list
On attached, my-app gets the user and loads the app.user. In the meantime, community-list is attached (even before app.user is loaded) and so I haven't been able to get the user's starred communities yet. Therefore, the solution I'm working on is as follows.
In community-list.attached():
app.changes.listen((List<ChangeRecord> records) {
if (app.user != null) {
getUserStarredCommunities();
}
});
Elsewhere in community-list is said metho:
// This is triggered by an app.changes.listen.
void getUserStarredCommunities() {
// Determine if this user has starred the community.
communities.forEach((community) {
var starredCommunityRef = new db.Firebase(firebaseLocation + '/users/' + app.user.username + '/communities/' + community['id']);
starredCommunityRef.onValue.listen((e) {
if (e.snapshot.val() == null) {
community['userStarred'] = false;
} else {
community['userStarred'] = true;
}
});
});
}
Note that communities is an observable list in community-list:
#observable List communities = toObservable([]);
Which is initially populated in community-list.attached():
getCommunities() {
var f = new db.Firebase(firebaseLocation + '/communities');
var communityRef = f.limit(20);
communityRef.onChildAdded.listen((e) {
var community = e.snapshot.val();
// If no updated date, use the created date.
if (community['updatedDate'] == null) {
community['updatedDate'] = DateTime.parse(community['createdDate']);
}
// snapshot.name is Firebase's ID, i.e. "the name of the Firebase location"
// So we'll add that to our local item list.
community['id'] = e.snapshot.name();
// Insert each new community into the list.
communities.add(community);
// Sort the list by the item's updatedDate, then reverse it.
communities.sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"]));
communities = communities.reversed.toList();
});
}
In summary, I load the list of communities even before I have a user, but once I have a user I want to update each community (Map) in the list of communities with the userStarred = true/false, which I then use in my community-list template.
Alas, it doesn't seem like the List updates. How do I achieve this?
This whole app.changes.listen business is expensive. What's the proper practice in a case like this, where an element is loaded before I load objects (like app.user) that will modify it in some way.
1)
toList() creates a copy of the list. You need to apply toObservable again to get an observable list.
communities = toObservable(communities.reversed.toList());
This also assigns a new list to communities which is covered by #observable.
I think it should trigger anyway
2) You update your communities explicitly. It shouldn't be necessary to listen for changes. You can call a method containing
if (app.user != null) {
getUserStarredCommunities();
}
explicitly each time you change the list.
You also call Firebase for each community when a change in communities occurs. I don't know Firebase but it seems you send a request to a server each time which is of course expensive.
You should remember for what user+community combination you already made the call and use the remembered result instead.
With app.changes.listen you listen to any updated of any #observable field in your component. If you have other observable fields beside communities this method might be called too often.
If you are only interested in changes to communities you should put this code into a method like
communitiesChanged(oldVal, newVal) {
if (app.user != null) {
getUserStarredCommunities();
}
}
but the better option is to not listen to changes and another method name and call it explicitly as state above anyways if possible.

EntityFramework ObjectContext Refresh issue

I have DataContext.Refresh Method:
public void RefreshDataSource()
{
_entities.Refresh(RefreshMode.ClientWins,Departments);
}
And observable collection:
public ObservableCollection<Department> Departments
{
get
{
if (_departments == null && _entities != null)
{
_entities.Departments.Include("Drivers").ToArray();
_departments = new EntityObservableCollection<Department>(_entities.Departments);
}
return _departments;
}
}
If i update records outside context i see only changed records but can't see inserted and removed. Why?
Because Refresh doesn't look for new records. It takes records you already have and updates them with current values. It also probably doesn't handle deleted records especially if you use ClientWins strategy which takes your state as more important.

Discard Changes with Entity Framework 4 POCO

I am using Entity Framework 4.0 POCO with WPF.
I have a form displaying to the user a an object with a many to many relationship like the following:
public class BPartner : BaseEntity
{
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = bpartnerValidatorLight.ValidateName(value);
OnPropertyChanged("Name",true);
}
}
}
public virtual ObservableCollection<BPAddress> BPAddresses { get; set; }
}
public class BPAddress : BaseEntity
{
public string Line1
{
get
{
return line1;
}
set
{
if (line1 != value)
{
line1 = bpAddressValidatorLight.ValidateLine1(value);
OnPropertyChanged("Line1",true);
}
}
}
public virtual City City
{
get { return city; }
set
{
if (city != value)
{
city = value;
OnPropertyChanged("City");
}
}
}
}
The user may add and delete addresses in the BPAddresses collection and change the "Name" of the BPartner. When the user is done modifying the BPArtner object he/she may click "Save" or "Cancel". The problem is that when the user clicks "Cancel" I need to tell the Entity Framework to revert all the changes.
Any approach for handling this is very welcome including reloading.
Here is what I have tried:
1. Discard the objectContext, create a new object context and just query the database to reload everything again. The problem here is that Entity Framework caches things and old objects linger attached to the old context and I get exceptions if the user clicks cancel then edits again and clicks save.
2.
repoBPartner.Refresh(bp);
IQueryable<BPAddress> query = from e in addressRepo.AsQueryable()
where e.BPartnerId == bp.Id
select e;
ObjectQuery<BPAddress> objectQuery = (ObjectQuery<BPAddress>)query;
objectQuery.Include("City");
objectQuery.Include("Country");
ObjectResult<BPAddress> result = (ObjectResult<BPAddress>)objectQuery.Execute(MergeOption.OverwriteChanges);
bp.BPAddresses = new System.Collections.ObjectModel.ObservableCollection<BPAddress>(result.ToList<BPAddress>());
The problem here is that "City" property does not get refreshed.
3. Tried: objectContext.LoadProperty(bp, "BPAddresses", MergeOption.OverwriteChanges);
All the above worked partially. What is the best practice for achieving this?
Any help will be appreciated.
Thanks,
K. Mitev
EF doesn't offer discarding changes. EF also doesn't have second level cache so once you dispose a context with changes and create a new context to load data from database you will get unchanged data.
The problem here is that Entity
Framework caches things and old
objects linger attached to the old
context and I get exceptions if the
user clicks cancel then edits again
and clicks save.
When user clicks cancel you must throw away all objects used in editation together with their context. These objects and context are dead. Once user clicks edit again you must load everything again from database.
Refreshing navigation properties doesn't work very well. For example once you add something to navigation collection it will never be removed by refreshing from database.

Resources