postsharp - Parameter value based caching - caching

I am using the postsharp cache and I have created a sample function to cache the result of that - but I want to do caching based on parameter values only.
For example, my function looks like this
[Cache(SlidingExpiration = OneHour, IgnoreThisParameter = true )]
public IEnumerable<String> Get(int dateId)
{
// code processing
}
I want the output of this function to be cached if the dateid= T-1 (yesterday) and not when dateid is not T.
But with [Cache] attribute decorated, everything gets cached. Basically I want to cache T-1 data only and not data for any other dates.
This pattern is used so that we can invalidate the caches using custom methods but I dont see any option for selective parameter value based caching.
Can anybody respond to this?

Currently this is not possible. At this moment you need to split method into three methods and have:
public IEnumerable<string> Get(int dateId)
{
if (dateId == CurrentDate - 1)
GetCached(dateId);
else
GetCore(dateId);
}
[Cache(SlidingExpiration = OneHour, IgnoreThisParameter = true )]
private IEnumerable<string> GetCached(int dateId)
{
return GetCore(dateId);
}
private IEnumerable<string> GetCore(int dateId)
{
// ...
}
This is in the backlog, but it is not scheduled to any release at the moment.

Related

LINQ Distinct does not invoke IEquatable<T>.Equals

I have a set of domain object, deriving from a base, where I've overridden Equals, IEquatable<T>.Equals and equality operators. I've successfully used Contains, but now I am trying to use Distinct differently. Here's look at a sample code:
var a = new Test { Id = 1 };
var a2 = new Test { Id = 1 };
var list = new List<Test> { a, a2 };
var distinct = list.Distinct().ToList(); // both objects, Equal implementations not called
var containsA = list.Contains(a); // true, Equal implementations called
var containsA2 = list.Contains(a); // true
var containsNewObjectWithSameId = list.Contains(new Test { Id = 1 }); // true
public class Test : IEquatable<Test>
{
public int Id { get; init; }
public bool Equals(Test other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
if (this.GetType() != other.GetType())
return false;
return this.Id == other.Id;
}
public override int GetHashCode() => base.GetHashCode + this.Id;
}
Contains finds matches, but Distinct is feeling very inclusive and keeps them both. From MS docs:
The first search does not specify any equality comparer, which means FindFirst uses
EqualityComparer.Default to determine equality of boxes. That in turn uses the implementation
of the IEquatable.Equals method in the Box class.
What am I missing?
Thanks #JonSkeet for your insight in the comments.
The problem in this case is the way I wrote my GetHashCode method. It has nothing to do with LINQ, as I originally thought.
Explanation
GetHashCode has to be identical for objects that compare equally. In my case - since the base implementation of object.Equals only checks for reference equality and I am comparing two separate objects - a and b, their base.GetHashCode would result in different values, which in turn would render those two objects as not equal.
Solution
In this case, simply returning the Id value is enough as is shown in MS docs:
One of the simplest ways to compute a hash code for a numeric value that has the same or a smaller range than the Int32 type is to simply return that value.
So changing the above code sample like this:
public override int GetHashCode() => this.Id;
would solve the issue. Please keep in mind that if the value of Id is not unique, this will cause ill behavior. In such cases you'll need another property to check and you will have to compose GetHashCode from ALL those properties. For further info refer to MS docs

How to retrieve data by property in Couchbase Lite?

My documents have the property docType that separated them based on the purpose of each type, in the specific case template or audit. However, when I do the following:
document.getProperty("docType").equals("template");
document.getProperty("docType").equals("audit");
The results of them are always the same, it returns every time all documents stored without filtering them by the docType.
Below, you can check the query function.
public static Query getData(Database database, final String type) {
View view = database.getView("data");
if (view.getMap() == null) {
view.setMap(new Mapper() {
#Override
public void map(Map<String, Object> document, Emitter emitter) {
if(String.valueOf(document.get("docType")).equals(type)){
emitter.emit(document.get("_id"), null);
}
}
}, "4");
}
return view.createQuery();
}
Any hint?
This is not a valid way to do it. Your view function must be pure (it cannot reference external state such as "type"). Once that is created you can then query it for what you want by setting start and end keys, or just a set of keys in general to filter on.

How to store parameters for action to be used again later

I have a list view that can be sorted, searched and filtered. From that list view the user can edit items in multiple steps. Finally after editing and reviewing the changes the user goes back to the list. Now I want the list to use the same sorting, search term and filters that the user set before and show the correct results.
How can multiple paramters (sorting, search, filter) be stored and reused when showing the list action?
Possible unsatisfactory ways that I thought of:
pass through all the needed parameters. Does work hardly if there are multiple actions involved between the two list action calls
save the parameters in the session object. This seems to require a lot of code to handle multiple parameters (check if parameter was passed to action, store new value, if parameter was not passed, get old parameter from session, handle empty string parameters):
Long longParameter
if(params.containsKey('longParameter')) {
longParameter = params.getLong('longParameter')
session.setAttribute('longParameter', longParameter)
} else {
longParameter = session.getAttribute('longParameter') as Long
params['longParameter'] = longParameter
}
If you want to make it more generic you could use an Interceptor instead.
This could perhaps be generalized like this:
class SessionParamInterceptor {
SessionParamInterceptor() {
matchAll() // You could match only controllers that are relevant.
}
static final List<String> sessionParams = ['myParam','otherParam','coolParam']
boolean before() {
sessionParams.each {
// If the request contains param, then set it in session
if (params.containsKey(it)) {
session[it] = params[it]
} else {
// Else, get the value from session (it will be null, if not present)
params[it] = session[it]
}
}
true
}
}
The static sessionParams holds the parameters you want to store/retrieve from session.
If the params contains an element from the list, it is stored in session under the same name. If not, it is taken from session (given that it exists).
In your controller, you can now just access params.getLong('theParam') like you always would. You could also use Grails parameter conversion:
def myAction(Long theParam) {
}
Lots of LOC saved.
I use the session as well. Here is a sample that you may adapt to your needs:
def list() {
if (request.method == 'GET' && !request.queryString) {
if (session[controllerName]) {
// Recall params from memory
params.putAll(session[controllerName])
}
} else {
// Save params to memory and redirect to get clean URL
session[controllerName] = extractParams(params)
redirect(action: actionName)
return
}
// Do your actions here...
}
def extractParams(params) {
def ret = [:]
params.each { entry ->
if (entry.key.startsWith("filter_") || entry.key == "max" || entry.key == "offset" || entry.key == "sort" || entry.key == "order") {
ret[entry.key] = entry.value
}
}
return ret
}
Using session is your best bet. Just save the preference when preferred. I mean, when user sorts, or filter, just save that information in the session, for that particular <controller>.<action>, before returning the page. Next time, check the session, if it has anything related to that <controller>.<action>, apply those; otherwise render the default page.
You might like to use some Interceptor for this, as suggested by sbglasius, here.
I hope you're getting my point.

How do I store a comma-separated list in Orchard CMS?

Using Orchard CMS, I am dealing with a record and a part proxy, but cannot figure out how to save it into the DB. In fact, I confess I don't even know how to get the items I'm trying to save into this paradigm. I was originally using enum's for choices:
MyEmum.cs:
public enum Choices { Choice1, Choice2, Choice3, Choice4 }
MyRecord.cs:
public virtual string MyProperty { get; set; }
MyPart.cs:
public IEnumerable<string> MyProperty
{
get
{
if (String.IsNullOrWhiteSpace(Record.MyProperty)) return new string[] { };
return Record
.MyProperty
.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim())
.Where(r => !String.IsNullOrEmpty(r));
}
set { Record.MyProperty = value == null ? null : String.Join(",", value); }
}
Now, in my service class, I tried something like:
public MyPart Create(MyPartRecord record)
{
MyPart part = Services.ContentManager.Create<MyPart>("My");
...
part.MyProperty = record.MyProperty; //getting error here
...
return part;
}
However, I am getting the following error: Cannot implicitly convert 'string' to System.Collections.Generic.IEnumerable<string>'
Essentially, I am trying to save choices from a checkboxlist (one or more selections) as a comma-separated list in the DB.
And this doesn't even get me over the problem of how do I use the enum. Any thoughts?
For some background:
I understand that the appropriate way to handle this relationship would be to create a separate table and use IList<MyEnum>. However, this is a simple list that I do not intend to manipulate with edits (in fact, no driver is used in this scenario as I handle this on the front-end with a controller and routes). I am just capturing data and redisplaying it in the Admin view for statistical/historical purposes. I may even consider getting rid of the Part (considering the following post: Bertrand's Blog Post.
It should be:
part.MyProperty = new[] {"foo", "bar"};
for example. The part's setter will store the value on the record's property as a comma-separated string, which will get persisted into the DB.
If you want to use enum values, you should use the Parse and ToString APIs that .NET provide on Enum.

How do I delete records from a child collection in LINQ to SQL?

I have two tables in my database connected by foreign keys: Page (PageId, other data) and PageTag (PageId, Tag). I've used LINQ to generate classes for these tables, with the page as the parent and the Tag as the child collection (one to many relationship). Is there any way to mark PageTag records for deletion from the database from within the Page class?
Quick Clearification:
I want the child objects to be deleted when the parent DataContext calls SubmitChanges(), not before. I want TagString to behave exactly like any of the other properties of the Page object.
I would like to enable code like the following:
Page page = mDataContext.Pages.Where(page => page.pageId = 1);
page.TagString = "new set of tags";
//Changes have not been written to the database at this point.
mDataContext.SubmitChanges();
//All changes should now be saved to the database.
Here is my situation in detail:
In order to make working with the collection of tags easier, I've added a property to the Page object that treats the Tag collection as a string:
public string TagString {
get {
StringBuilder output = new StringBuilder();
foreach (PageTag tag in PageTags) {
output.Append(tag.Tag + " ");
}
if (output.Length > 0) {
output.Remove(output.Length - 1, 1);
}
return output.ToString();
}
set {
string[] tags = value.Split(' ');
PageTags.Clear();
foreach (string tag in tags) {
PageTag pageTag = new PageTag();
pageTag.Tag = tag;
PageTags.Add(pageTag);
}
}
}
Basically, the idea is that when a string of tags is sent to this property, the current tags of the object are deleted and a new set is generated in their place.
The problem I'm encountering is that this line:
PageTags.Clear();
Doesn't actually delete the old tags from the database when changes are submitted.
Looking around, the "proper" way to delete things seems to be to call the DeleteOnSubmit method of the data context class. But I don't appear to have access to the DataContext class from within the Page class.
Does anyone know of a way to mark the child elements for deletion from the database from within the Page class?
After some more research, I believe I've managed to find a solution. Marking an object for deletion when it's removed from a collection is controlled by the DeleteOnNull parameter of the Association attribute.
This parameter is set to true when the relationship between two tables is marked with OnDelete Cascade.
Unfortunately, there is no way to set this attribute from within the designer, and no way to set it from within the partial class in the *DataContext.cs file. The only way to set it without enabling cascading deletes is to manually edit the *DataContext.designer.cs file.
In my case, this meant finding the Page association, and adding the DeleteOnNull property:
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true)]
public Page Page
{
...
}
And adding the DeleteOnNull attribute:
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true, DeleteOnNull = true)]
public Page Page
{
...
}
Note that the attribute needed to be added to the Page property of the PageTag class, not the other way around.
See also:
Beth Massi -- LINQ to SQL and One-To-Many Relationships
Dave Brace -- LINQ to SQL: DeleteOnNull
Sorry, my bad. That won't work.
It really looks like you need to be doing this in your repository, rather than in your Page class. There, you have access to your original data context.
There is a way to "attach" the original data context, but by the time you do that, it has become quite the code smell.
Do you have a relationship, in your Linq to SQL entity diagram, linking the Page and PageTags tables? If you don't, that is why you can't see the PageTags class from the Page class.
If the foreign key in the PageTags database table is set to Allow Nulls, Linq to SQL will not create the link when you drag the tables into the designer, even if you created a relationship on the SQL Server.
This is one of those areas where OR mapping can get kind of hairy. Providing this TagString property makes things a bit more convenient, but in the long run it obfuscates what is really happening when someone utilizes the TagString property. By hiding the fact that your performing data modification, someone can very easily come along and set the TagString without using your Page entity within the scope of a DataContext, which could lead to some difficult to find bugs.
A better solution would be to add a Tags property on the Page class with the L2S model designer, and require that the PageTags be edited directly on the Tags property, within the scope of a DataContext. Make the TagString property read only, so it can be genreated (and still provide some convenience), but eliminate the confusion and difficulty around setting that property. This kind of change clarifies intent, and makes it obvious what is happening and what is required by consumers of the Page object to make it happen.
Since Tags is a property of your Page object, as long as it is attached to a DataContext, any changes to that collection will properly trigger deletions or insertions in the database in response to Remove or Add calls.
Aaron,
Apparently you have to loop thru your PageTag records, calling DeleteOnSubmit for each one. Linq to SQL should create an aggregate query to delete all of the records at once when you call SubmitChanges, so overhead should be minimal.
replace
PageTags.Clear();
with
foreach (PageTag tag in PageTags)
myDataContext.DeleteOnSubmit(tag);
Aaron:
Add a DataContext member to your PageTag partial class.
partial class PageTag
{
DataClassesDataContext myDataContext = new DataClassesDataContext();
public string TagString {
..etc.
Larger code sample posted at Robert Harvey's request:
DataContext.cs file:
namespace MyProject.Library.Model
{
using Tome.Library.Parsing;
using System.Text;
partial class Page
{
//Part of Robert Harvey's proposed solution.
MyDataContext mDataContext = new TomeDataContext();
public string TagString {
get {
StringBuilder output = new StringBuilder();
foreach (PageTag tag in PageTags) {
output.Append(tag.Tag + " ");
}
if (output.Length > 0) {
output.Remove(output.Length - 1, 1);
}
return output.ToString();
}
set {
string[] tags = value.Split(' ');
//Original code, fails to mark for deletion.
//PageTags.Clear();
//Robert Harvey's suggestion, thorws exception "Cannot remove an entity that has not been attached."
foreach (PageTag tag in PageTags) {
mDataContext.PageTags.DeleteOnSubmit(tag);
}
foreach (string tag in tags) {
PageTag PageTag = new PageTag();
PageTag.Tag = tag;
PageTags.Add(PageTag);
}
}
}
private bool mIsNew;
public bool IsNew {
get {
return mIsNew;
}
}
partial void OnCreated() {
mIsNew = true;
}
partial void OnLoaded() {
mIsNew = false;
}
}
}
Repository Methods:
public void Save() {
mDataContext.SubmitChanges();
}
public Page GetPage(string pageName) {
Page page =
(from p in mDataContext.Pages
where p.FileName == pageName
select p).SingleOrDefault();
return page;
}
Usage:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string pageName, FormCollection formValues) {
Page updatedPage = mRepository.GetPage(pageName);
//TagString is a Form value, and is set via UpdateModel.
UpdateModel(updatedPage, formValues.ToValueProvider());
updatedPage.FileName = pageName;
//At this point NO changes should have been written to the database.
mRepository.Save();
//All changes should NOW be saved to the database.
return RedirectToAction("Index", "Pages", new { PageName = pageName });
}

Resources