ZiggyCreatures / FusionCache

FusionCache is an easy to use, fast and robust hybrid cache with advanced resiliency features.
MIT License
1.9k stars 97 forks source link

[FEATURE] Can factory return nullable item? #304

Closed brotherhorse closed 2 months ago

brotherhorse commented 2 months ago

Problem

TItem is a class or record,OnCallBack must return a UNnullable TItem,but readObjectAsync return a nullable TItem, so I return item!. But I don't think this meets strict standards

Code:

 var item2 = await _fusionCache.GetOrSetAsync<TItem>(realKey, OnCallBack);
 async  Task<TItem> OnCallBack(FusionCacheFactoryExecutionContext<TItem> context,CancellationToken token)
 {                   
      var item = await  readObjectAsync();
      if(item is null)
      {
          if(context.Options.Duration.Seconds>60)
              context.Options.Duration=TimeSpan.FromMinutes(1);
      }
      return item!;                    
  }

Solution

factory return nullable TValue

GetOrSetAsync<TValue>(this IFusionCache cache, string key, Func<CancellationToken, Task<TValue>> factory, FusionCacheEntryOptions? options = null, CancellationToken token = default)

==>

GetOrSetAsync<TValue>(this IFusionCache cache, string key, Func<CancellationToken, Task<TValue?>> factory, FusionCacheEntryOptions? options = null, CancellationToken token = default)
brotherhorse commented 2 months ago

Code:

var item2 = await _fusionCache.GetOrSetAsync<TItem>(realKey, OnCallBack);
async Task OnCallBack<TItem>(FusionCacheFactoryExecutionContext <TItem> context,CancellationToken token)
{
  var item = await readObjectAsync();
  if(item is null)
  {
    if(context.Options.Duration.Seconds>60)
    context.Options.Duration=TimeSpan.FromMinutes(1);
  }
  return item!;
}
brotherhorse commented 2 months ago

Why are generic types removed from the code after I submit the edited question

jodydonetti commented 2 months ago

Why are generic types removed from the code after I submit the edited question

When you post some code, you need to enclose it in triple-backtick (the character ` ), like this:

var foo = "bar";

Otherwise the < character is interpreted as html, and will be processed.

Also, it is suggested to specify the language, like "```csharp"

Lokk here for more info.

PS: I updated your original comment to fix the code rendering issues, hope this helps.

jodydonetti commented 2 months ago

Hi @brotherhorse

TItem is a class or record,OnCallBack must return a UNnullable TItem

Ok, so this is a constraint.

but readObjectAsync return a nullable TItem, so I return item!. But I don't think this meets strict standards

In general I agree: if a method MUST NOT return null and it calls another method that CAN return null, you need to handle that case specifically: one way may be to use a default value of type TItem (non null of course), and return it instead of null.

Solution

factory return nullable TValue

I don't understand 2 things:

Help me understand more what is the problem related to FusionCache.

I don't know if this is what you are looking for, but you can use TItem? (nullable TItem) in the GetOrSet call, like this:

var item2 = await _fusionCache.GetOrSetAsync<TItem?>(realKey, OnCallBack);
async  Task<TItem?> OnCallBack(FusionCacheFactoryExecutionContext<TItem?> context, CancellationToken token)
{                   
  var item = await  readObjectAsync();
  if (item is null)
  {
    if (context.Options.Duration.Seconds > 60)
      context.Options.Duration = TimeSpan.FromMinutes(1);
  }
  return item;
}

Is this what you meant?

The other way, as I said above, is to use a (non null) default value of type TItem:

TItem myDefaultValue = ... ;

var item2 = await _fusionCache.GetOrSetAsync<TItem>(realKey, OnCallBack);
async  Task<TItem> OnCallBack(FusionCacheFactoryExecutionContext<TItem> context, CancellationToken token)
{                   
  var item = await  readObjectAsync();
  if (item is null)
  {
    if (context.Options.Duration.Seconds > 60)
      context.Options.Duration = TimeSpan.FromMinutes(1);
  }
  return item ?? myDefaultValue;
}
jodydonetti commented 2 months ago

Oh, also: there's a bug in your code that checks for the current cache duration, specifically here:

if (context.Options.Duration.Seconds > 60)
  context.Options.Duration = TimeSpan.FromMinutes(1);

In the code above Duration.Seconds gives you the "seconds component of the time interval" (see here).

This is NOT the same as getting the whole TimeSpan itself expressed in seconds, for which you have to use TimeSpan.TotalSeconds (see here).

For example if you have a TimeSpan with a value of 10 min and 20 sec:

Hope this helps!

brotherhorse commented 2 months ago
  • TotalSeconds

Yes,it is a mistake,it should be TotalSeconds

brotherhorse commented 2 months ago

What I mean is this:

await _fusionCache.GetOrSetAsync<TItem?>(realKey, OnCallBack)
async Task<TItem?> OnCallBack(FusionCacheFactoryExecutionContext<TItem?> context, CancellationToken token)

Thank you!