assign out parameter totalRecords when using caching - asp.net

I'm using asp.net Sqlmembership.GetAll() method ( paging overload ) . now I want to add a caching layer to cache results but there is problem with GetAll method's out parameter that returns the total number of records . how can I assign a value to totalRecords parameter when data are retrieved from cache ?

If my understanding is correct this little flow will help you to achieve your goal
This is a basic flow:
When you want to access the cached object, ask it to the cache provider
If the object is not null, then cast to the correct type and return the object from the cache (in this case, the list of users). End process
If the object is null then
Retrieve the object from its original source (using the GetAll methods)
Save the retrieved object to the cache
Return the retrieved object. End process
In any case, I would recommend you to work with a custom domain class instead of the MembershipUser class
This is a basic example:
public IEnumerable<DomainUser> GetDomainUsers()
{
var context = HttpContext.Current;
var cache = context.Cache;
var domainUsers = cache["domainUsers"] as IEnumerable<DomainUser>;
if (domainUsers == null)
{
domainUsers = Membership.GetAllUsers().OfType<MembershipUser>().Select(x => new DomainUser
{
Email = x.Email,
Username = x.UserName
});
cache.Insert(
"domainUsers", // cache key
domainUsers, // object to cache
null, // dependencies
DateTime.Now.AddMinutes(30), // absoulute expiration
Cache.NoSlidingExpiration, // slading expiration
CacheItemPriority.High, // cache item priority
null // callback called when the cache item is removed
);
context.Trace.Warn("Data retrieved from its original source");
}
else
{
context.Trace.Warn("Data retrieved from cache");
}
return domainUsers;
}

Related

Adding newly created header by Asp.Net HeaderPropogation library to telemetry

services.AddHeaderPropogation(o =>
{
o.Headers.Add("Id")
o.Headers.Add("Id", context => {
return new StringValues(Guid.NewGuid().ToString())
});
});
The above code helps me to create a header called id if it doesnt exist with a new guid and if it exists, it would just use the value. This is using Microsoft Header Propogation nuget package. And it works.
But now i have a requirement to add this to Azure Application insights, but the standard way of doiing it only works when the incoming request has headers. If the new GUID is created, it doesnt trigger the ITelemetryInitializer call.
Because for adding Telmetry custom values, we have a class which inherits ITelemtryInitializer and inside that i do call to Request.Headers like below:
var requestTelemetry = telemetry as RequestTelemetry
if(context.Request.Headers.TryGetValue(id, out var value))
requestTelemtry.Properties[id] = value.ToString()
But the above line is never triggered since the Request.Headers never had this id. This id will be created only by the middleware when the api calls the next service.
So my question, is there a way to call the telemetry classes from the Startup> ConfigfureServices and inside the HeaderPropogation code, so that as soon as the new GUID is created, i can add it to telemtry. All the examples of adding to telemetry shows either from controller or DI. How to call it from the Startup itself ?
Or is there a better way to achieve the same ?
Let me post the solution I found. I didnt need to have a telemetryinitializer class to populate the guid in another class. I wanted it to be added as soon as we create it, so this is how i modified th header propogiation code in services in startup.
services.AddHeaderPropagation(options =>
{
var correlationId = "YourId";
options.Headers.Add(correlationId, context => {
var requestTelemetry = context.HttpContext.Features.Get<RequestTelemetry>();
if (context.HttpContext.Request.Headers.TryGetValue(correlationId, out var value))
{
requestTelemetry.Properties[correlationId] = value.ToString();
return value.ToString();
}
else
{
var guidId = Guid.NewGuid().ToString();
requestTelemetry.Properties[correlationId] = guidId;
return new StringValues(guidId);
}
});
});
Key for me has been the realization i can get RequestTelemetry anywhere we want in the code with this properties option.
var requestTelemetry = context.HttpContext.Features.Get<RequestTelemetry>();

Changing application server key in push manager subscription

I implemented web push notifications using service worker. I collected user subscriptions with a particular application server key. Suppose if we change the application server key, then when we get the subscription using "reg.pushManager.getSubscription()", we will get the old subscription information which was created using the old application server key. How to handle this scenario? How to get the new subscription from the user?
Get the subscription using reg.pushManager.getSubscription() and check whether current subscription uses the new application server key. If not, then call unsubscribe() function on the existing subscription and resubscribe again.
After properly starting the service worker and getting the permissions, call navigator.serviceWorker.ready in order to get access to the *.pushManager object.
From this object we call another promise to get the pushSubscription object we actually care about.
If the user was never subscribed pushSubscription will be null otherwise we get the key from it and check if it's different, if that's the case we unsubscribe the user and subscribe them again.
var NEW_PUBLIC_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
Notification.requestPermission(function (result) {
if (permissionResult == 'granted'){
subscribeUser();
}
});
function subscribeUser() {
navigator.serviceWorker.ready
.then(registration => {
registration.pushManager.getSubscription()
.then(pushSubscription => {
if(!pushSubscription){
//the user was never subscribed
subscribe(registration);
}
else{
//check if user was subscribed with a different key
let json = pushSubscription.toJSON();
let public_key = json.keys.p256dh;
console.log(public_key);
if(public_key != NEW_PUBLIC_KEY){
pushSubscription.unsubscribe().then(successful => {
// You've successfully unsubscribed
subscribe(registration);
}).catch(e => {
// Unsubscription failed
})
}
}
});
})
}
function subscribe(registration){
registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(NEW_PUBLIC_KEY)
})
.then(pushSubscription => {
//successfully subscribed to push
//save it to your DB etc....
});
}
function urlBase64ToUint8Array(base64String) {
var padding = '='.repeat((4 - base64String.length % 4) % 4);
var base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
var rawData = window.atob(base64);
var outputArray = new Uint8Array(rawData.length);
for (var i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
In my case, I managed to solve it by clearing the cache and cookies
The key you get from calling sub.getKey('p256dh') (or sub.toJSON.keys.p256dh) is the client's public key, it will always be different from the server public key. You need to compare the new public server key and sub.options.applicationServerKey.
sub above is the resolved promise from reg.pushManager.getSubscription().
Therefore:
Get PushSubscription interface - reg.pushManager.getSubscription().then(sub => {...}), if sub is null no subscription exists, therefore no worry, but if it's defined:
Inside the block get the current key in use sub.options.applicationServerKey
Convert it to string, because you can't compare ArrayBuffer directly - const curKey = btoa(String.fromCharCode.apply(null, new Uint8Array(sub.options.applicationServerKey)))
Compare it with your new key. If the keys are different call sub.unsubscribe() and then subscribe again by calling reg.pushManager.subscribe(subscribeOptions), where subscribeOptions uses your new key. You call 'unsubscribe' on PushSubscription, but subscribe on PushManager

How to send List of objects to google execution api and handle it in google apps script

In general I want to export data from asp.net mvc application to Google Sheets for example list of people. I've already set up connection and authenticated app with my Google account (trough OAuth2) but now I'm trying to send my list of objects to api and then handle it in script (by putting all data in new file) and couldn't get my head around this.
Here is some sample code in my app that sends the request.
public async Task<ActionResult> SendTestData()
{
var result = new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
AuthorizeAsync(CancellationToken.None).Result;
if (result.Credential != null)
{
string scriptId = "MY_SCRIPT_ID";
var service = new ScriptService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "Test"
});
IList<object> parameters = new List<object>();
var people= new List<Person>(); // next i'm selecting data from db.Person to this variable
parameters.Add(people);
ExecutionRequest req = new ExecutionRequest();
req.Function = "testFunction";
req.Parameters = parameters;
ScriptsResource.RunRequest runReq = service.Scripts.Run(req, scriptId);
try
{
Operation op = runReq.Execute();
if (op.Error != null)
{
// The API executed, but the script returned an error.
// Extract the first (and only) set of error details
// as a IDictionary. The values of this dictionary are
// the script's 'errorMessage' and 'errorType', and an
// array of stack trace elements. Casting the array as
// a JSON JArray allows the trace elements to be accessed
// directly.
IDictionary<string, object> error = op.Error.Details[0];
if (error["scriptStackTraceElements"] != null)
{
// There may not be a stacktrace if the script didn't
// start executing.
Newtonsoft.Json.Linq.JArray st =
(Newtonsoft.Json.Linq.JArray)error["scriptStackTraceElements"];
}
}
else
{
// The result provided by the API needs to be cast into
// the correct type, based upon what types the Apps
// Script function returns. Here, the function returns
// an Apps Script Object with String keys and values.
// It is most convenient to cast the return value as a JSON
// JObject (folderSet).
Newtonsoft.Json.Linq.JObject folderSet =
(Newtonsoft.Json.Linq.JObject)op.Response["result"];
}
}
catch (Google.GoogleApiException e)
{
// The API encountered a problem before the script
// started executing.
AddAlert(Severity.error, e.Message);
}
return RedirectToAction("Index", "Controller");
}
else
{
return new RedirectResult(result.RedirectUri);
}
}
The next is how to handle this data in scripts - are they serialized to JSON there?
The execution API calls are essentially REST calls so the payload should be serialized as per that. Stringified JSON is typically fine. Your GAS function should then parse that payload to consume the encoded lists
var data = JSON.parse(payload);

AutoMapper: Keep Destination Value if the property doesnt exists in source

I tried to search a lot, and try different options but nothing seems to work.
I am using ASP.net Identity 2.0 and I have UpdateProfileViewModel . When Updating the User info, I want to map the UpdateProfileViewModel to ApplicationUser (i.e. the Identity Model); but I want to keep the values, I got from the db for the user. i.e. the Username & Email address, that doesn't needs to change.
I tried doing :
Mapper.CreateMap<UpdateProfileViewModel, ApplicationUser>()
.ForMember(dest => dest.Email, opt => opt.Ignore());
but I still get the Email as null after mapping:
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
user = Mapper.Map<UpdateProfileViewModel, ApplicationUser>(model);
I also tried this but doesn't works:
public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType) && x.DestinationType.Equals(destinationType));
foreach (var property in existingMaps.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
and then:
Mapper.CreateMap<UpdateProfileViewModel, ApplicationUser>()
.IgnoreAllNonExisting();
All you need is to create a mapping between your source and destination types:
Mapper.CreateMap<UpdateProfileViewModel, ApplicationUser>();
and then perform the mapping:
UpdateProfileViewModel viewModel = ... this comes from your view, probably bound
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
Mapper.Map(viewModel, user);
// at this stage the user domain model will only have the properties present
// in the view model updated. All the other properties will remain unchanged
// You could now go ahead and persist the updated 'user' domain model in your
// datastore

Validating a field based on a different database table / entity

I am writing an MVC 4 application, and using Entity Framework 4.1. I have a validation question which I cannot seem to find the answer to.
Essentially, I have an Entity (object) called "Product" which contains a field "Name", which must follow strict naming conventions which are defined in a separate Entity called "NamingConvention". When the user enters a value, the system needs to check it against the rules established in the NamingConvention entity, and return an error if need be.
Where should this validation be done, and how? I need to check the NamingConvention entity when doing the validation, which means I would need a database context since I'm referencing a different entity. Is there any validation method which won't require me to create a new context? I was thinking of doing the validation in the Controller, since it already creates a data context, but this doesn't seem like the right place to do it.
Thanks for any help!
I have done things like this using a JQuery post (ajax) call from the webpage where the name is being entered. You then post (the value of name) to a method on your controller which can return a JSON value that contains a flag saying if the validation passed and also a message that you want to return to your user. For example :
Javascript in webpage :
$("#name").change(function () {
var nameVal = $(this).val();
$.post(getRoot() + "/NameController/ValidateName", { name: nameVal },
function (data) {
if (data.valid == "true") {
alert("A valid name was chosen");
} else
{
alert(data.message);
}
}, "json");
});
Controller (NameController) Code :
[HttpPost]
public ActionResult ValidateName(string name)
{
// actual validation carried out in a static utility class (Utils.IsNameValid)
// if you are loading the same validation rules from your table each time
// consider caching the data in the application cache or a static List.
bool nameIsValid = Utils.IsNameValid(name, out string ErrorMessage);
JsonResult result = new JsonResult();
result.Data = new { valid = (nameIsValid "true" : "false"), message = ErrorMessage };
return result;
}
I'm using EF 5 but believe you can use this method ... apologies in advance if I'm misleading you with this answer.
You could do the validation within your context (or a context decorator)
public override int SaveChanges()
{
var products = this.GetChangedProducts();
foreach (var product in products)
{
this.ValidateName(product);
}
return base.SaveChanges();
}
private IEnumerable<Product> GetChangedProducts()
{
return (
from entry in _context.ChangeTracker.Entries()
where entry.State != EntityState.Unchanged
select entry.Entity)
.OfType<Product>();
}
private void ValidateName(Product product)
{
//validate here
}

Resources