OutputCache varying by a complex object property passed using ModelBinder from Session - asp.net-mvc-4

We are using Couchbase for our Session and for OutputCache.
In this context, how can we cache by a complex object that is being passed to the method using a Custom Model Binder that retrieves a value from the Session?
This is the signature of the method I want to cache with the OutputCache attribute:
[HttpGet]
[OutputCache(CacheProfile = "MyObjectsCache", VaryByParam = "myParam")]
public ActionResult Index([ModelBinder(typeof (CustomVariableSessionModelBinder<MyClass>))] MyClass myParam)
{
Note: The ModelBinder is being used here for reasons beyond me and I cannot change it.
MyClass is a complex object that has an Id. I want to use the Id as the caching identifier.
public class MyClass
{
public int Id{get;set;}
//Other Properties
This is how the object is being retrieved from Session:
var sessionKey = typeof (MyNamespace.MyClass).FullName;
var httpContext = HttpContext.Current;
MyNamespace.MyClass newObject = null;
if (httpContext.Session != null)
{
newObject = httpContext.Session[sessionKey] as MyNamespace.MyClass;
}
Is it possible yo use VaryByParam for this scenario or will I have to use VaryByCustom?

I haven't tested this, but it should work. It's pretty much your only option anyways, though.
In addition to the built in ways to vary, you can vary by "Custom". This will call into a method in Global.asax you'll need to override: GetVaryByCustomString. Importantly for you situation here, this method is passed HttpContext, so you should be able to look into the session. Essentially, the solution will look something like:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
var args = custom.ToLower().Split(';');
var sb = new StringBuilder();
foreach (var arg in args)
{
switch (arg)
{
case "session":
var obj = // get your object from session
// now create some unique string to append
sb.AppendFormat("Session{0}", obj.Id);
}
}
return sb.ToString();
}
This is designed to handle multiple different types of "custom" vary types. For example, if you wanted to vary by "User", which is common, you can merely add a case for that in your switch. The important part is that the string returned by this method is actually what the output cache varies on, so you want that to be unique for the situation. This is why I prefixed the object's id with "Session" here. For example, if you just added the id, let's say 123, and then in another scenario you varied by user and that string was composed of just the user's id, which happened to be 123 as well. It would be the same string to the output cache, and you'd end with some weird results. Just be mindful of what the custom string looks like.
Now, you'd just alter your OutputCache attribute like:
[OutputCache(CacheProfile = "MyObjectsCache", VaryByParam = "myParam", VaryByCustom = "Session")]
Note: to vary by multiple custom things at once, you'd separate them with a ; (based on how the code above works). For example: VaryByCustom = "Session;User"

Related

asp.net core api - how to distinguish between `api/cars` and `api/cars?color=red` calls with [FromQuery] object

I am using [FromQuery] atribute in controller's Get method:
//CarsController, etc..
[HttpGet]
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery] CarsParameter? carsParam = null)
{
//param is always not null here
}
Inside the method I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null, so I cannot say if the Color="" (defailt value) is intended to be empty string or it's because of the call was api/cars
CarsParameter is a simple class:
public class CarsParameter
{
public string Color {get; set;} = "";
//more params here
}
Yes, I can use different path, like api/cars/withParams?color=red, but i am looking for more subtle solution.
I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null
Please note that default model binding starts by looking through the sources for the key carsParam.Color. If that isn't found, it looks for Color without a prefix, which cause the issue.
To achieve your requirement, you can try to specify prefix explicitly, like below.
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery][Bind(Prefix = "carsParam")] CarsParameter? carsParam = null)
{
Request to api/cars?color=red&carsParam.color=yellow&carsParam.brand=test and following is test result

String Trim Model Binder in ASP .NET Core 2

I'm working on a .NET Core 2 API project and have been trying to implement a universal string trim model binder that would trim all string values of provided request parameters and field values. So far I have had mixed results and am struggling to find working example that would point me in the right direction. I've been trying to implement the same model binder as posted by Vikash Kumar.
This model binder works fine for all string values that are passed into controller actions via direct parameters, such as public IActionResult Profile(string username), but for string fields in complex objects the BindModelAsync method of the TrimmingModelBinder class never gets called. An example of an HttpPost action in my controller would be public IActionResult Profile([FormBody] ProfileLookupModel model). The model binder does not seem to check the fields of the complex model. It also doesn't work for fields that are Lists of strings.
I recall prior to .NET Core, specifying a string trim model binder would recursively check each field of complex models, even models within complex models. This doesn't seem to be the case in .NET Core, but I might be wrong. My project is targeting the netcoreapp2.0 framework.
I'm curious if anyone has had the same issue as me and possibly found a solution for it.
Note: I haven't posted any sample code as it is the same as the code from the referenced article.
I'll add my 2 cents here. Instead of using some kind of model binding hook, I went to a action filter. One of the advantages is that the developer can select which actions to use, instead of having this processing for all requests and model bindings (not that it should affect performance that much). Btw action filters can also be applied globally.
Here is my code, first create an action filter.
public class TrimInputStringsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
foreach (var arg in context.ActionArguments.ToList())
{
if (arg.Value is string)
{
if (arg.Value == null)
{
continue;
}
string val = arg.Value as string;
if (!string.IsNullOrEmpty(val))
{
context.ActionArguments[arg.Key] = val.Trim();
}
continue;
}
Type argType = arg.Value.GetType();
if (!argType.IsClass)
{
continue;
}
TrimAllStringsInObject(arg.Value, argType);
}
}
private void TrimAllStringsInObject(object arg, Type argType)
{
var stringProperties = argType.GetProperties()
.Where(p => p.PropertyType == typeof(string));
foreach (var stringProperty in stringProperties)
{
string currentValue = stringProperty.GetValue(arg, null) as string;
if (!string.IsNullOrEmpty(currentValue))
{
stringProperty.SetValue(arg, currentValue.Trim(), null);
}
}
}
}
To use it, either register as global filter or decorate your actions with the TrimInputStrings attribute.
[TrimInputStrings]
public IActionResult Register(RegisterViewModel registerModel)
{
// Some business logic...
return Ok();
}
TrimmingModelBinder is essentially configured for strings only, and defaults back to SimpleTypeModelBinder if it fails, or other binders configured. So if your implementation is essentially the same as in TrimmingModelBinder then it will definitely work for strings only.
For complex types, I recommend creating a new binder, and its corresponding provider, which will have to check all string properties in the model type and trim the value before binding. Then register this binder at index 0 such that its the first one checked before any other binders are tried.
services.AddMvc(options => option.ModelBinderProviders.Insert(0, new MyComplexTypeModelBinderProvider());

How to deserialize polymorphic collections in JsonFX?

My JsonFX serialization code works, but the object that I'm serializing contains a list of polymorphic entities, and they're all deserialized as their base type and not their actual type.
Here's my serialization code:
public static string Serialize(System.Object obj)
{
StringBuilder builder = new StringBuilder();
using (TextWriter textWriter = new StringWriter(builder))
{
JsonWriter writer = new JsonWriter(textWriter);
writer.Write(obj);
return builder.ToString();
}
}
public static T Deserialize<T>(string json)
{
using (TextReader textReader = new StringReader(json))
{
var jsonReader = new JsonReader(textReader);
return jsonReader.Deserialize<T>();
}
}
As you can see it's pretty straightforward. I'm also not decorating my classes with any attributes or anything special to make them serializable. Besides the polymorphic problem, it all just seems to be working properly.
So how can I get my polymorphic types to deserialize properly?.
Thanks.
You need to turn on type hinting. Here's example code (this is relevant to JsonFx v1.4, may or may not work with your version):
StringBuilder result = new StringBuilder(string.Empty);
JsonWriterSettings settings = JsonDataWriter.CreateSettings(true);
settings.TypeHintName = "__type";
JsonWriter writer = new JsonWriter(result, settings);
writer.Write(obj);
return result.ToString();
This will add extra data to your JSON string which looks something like:
"__type": "MyNamespace.MyClass, MyAssembly",
Which means it finds out what derived type it is based on the class name. If you change your class name or namespace name, it won't work anymore. Worse, you can't deserialize from your old JSON text data anymore, unless you mass replace all occurrences of the old class name and replace it with the new.
So you have to be careful with it.
EDIT: Forgot to mention that you have to edit the source code of JsonFx for this to work.
In JsonReader.cs, find the ReadArray method:
Change:
object value = this.Read(arrayItemType, isArrayTypeAHint);
to:
object value = this.Read(null, false);
This will ensure that JsonFx will always attempt to figure out the type of each element in an array/list. If you want this to work for just single variables, well you'd need to do the changes on the appropriate code (haven't tried that).

How to Implement Castle Windsor IScopeAccessor to provide NHibernate Session Per ViewModel using LifestyleScoped

I am looking for some help creating an implementation of IScopeAccessor, or finding a new solution, that will allow me to provide an NHibernate session per ViewModel.
I know that Windsor now supports scoped lifestyles as seen (here). However the example creates the special scope with a using block and calling container.resolve within the using.
_container.Register(Component.For<A>().LifestyleScoped());
using (_container.BeginScope())
{
var a1 = _container.Resolve<A>();
var a2 = _container.Resolve<A>();
Assert.AreSame(a1, a2);
}
I can't think of a way to make this work because I don't want to pass around the container and I want the scope to be tied to the ViewModel that gets created, which will happen dynamically as they are needed.
As an alternative it looks like I can create an implementation of IScopeAccessor which, according to Krzysztof Koźmic (here) would allow me to
"... provide any scope you like. Scope is an abstract term here and it can be anything."
Unfortunately I cannot find an implementation of IScopeAccessor that isn't specific to a web based scenario and I am struggling to understand exactly what I need to do to turn "anything" into a valid scope.
I have found an example of exactly what I want to do using Ninject (http://www.emidee.net/index.php/2010/08/23/ninject-use-one-database-session-per-view-model/):
Bind<ISession>().ToMethod(ctx =>
{
var session = ctx.Kernel.Get<....>().BuildSessionFactory().OpenSession();
return session;
})
.InScope(context =>
{
var request = context.Request;
if (typeof(IViewModel).IsAssignableFrom(request.Service))
return request;
while ((request = request.ParentRequest) != null)
if (typeof(IViewModel).IsAssignableFrom(request.Service))
return request;
return new object();
});
In Ninject, the InScope indicates that any instances created by the binding should be reused as long as the object returned by the call back remains alive. Essentially, this call back returns the root level ViewModel (since ViewModels can be nested).
Any thoughts on how I can do the same thing or get the same result using Windsor?
The problem seems to be the place of creation.
If it's all about dependencies of viewmodels being constructed, you could maybe use boud lifestyle, as described in What's new...
Or you could alternatively use your own scope accessor, that is sensitive to viewmodels. for example like this:
public class ViewModelScopeAccessor : IScopeAccessor
{
private IDictionary<Guid, ILifetimeScope> scopes = new Dictionary<Guid, ILifetimeScope>();
private ILifetimeScope defaultScope;
public ViewModelScopeAccessor()
: this(new DefaultLifetimeScope())
{ }
public ViewModelScopeAccessor(ILifetimeScope defaultScope)
{
this.defaultScope = defaultScope;
}
public ILifetimeScope GetScope(CreationContext context)
{
var creator = context.Handler.ComponentModel.Implementation;
var viewModel = creator as IViewModel;
if (viewModel != null)
{
ILifetimeScope scope;
if (!scopes.TryGetValue(viewModel.UID, out scope))
{
scope = new DefaultLifetimeScope();
scopes[viewModel.UID] = scope;
}
return scope;
}
else
{
return defaultScope;
}
}
public void Dispose()
{
foreach (var scope in scopes)
{
scope.Value.Dispose();
}
defaultScope.Dispose();
scopes.Clear();
}
}
for the following viewmodel interface :
public interface IViewModel
{
string DisplayName { get; }
Guid UID { get; }
}
You of course could compare the viewmodels in other ways, it's just an example.
The drawback of both, the bound lifestyle and that scope accessor, is, that it won't work, if you use a typed factory inside your viewmodel, to lazily construct objects, since the scope accessor has no idea, from which object/method its factory method was called. But I think is is a general .NET issue, since a method does actually never know, from where it has been called.
So, you could then use your own factories, that produce only one instance per factory instance and make them scoped to your viewmodels too.
Hope this helps.

How do I convert an object from Reflection to a generic collection?

I'm trying to write a Compare method to compare properties in some POCOs using Reflection to ensure that they've been persisted to the database correctly. For example, let's say I have this POCO:
public class NoahsArk
{
public string Owner { get; set; }
public ICollection<Animal> Animals { get; set; }
}
What I want to do is this:
[Test]
public class Saves_Correctly_To_Database()
{
var noahsArk = new NoahsArk { // some setup code here };
db.Save(noahsArk);
var dbNoahsArk = db.Get<NoahsArk>(noahsArk.Id);
Assert.That(Compare(noahsArk, dbNoahsArk), Is.True);
}
The ORM I'm using is NHibernate. My Compare method looks like this so far:
public static bool EqualsProperties<T>(this T x, T y)
{
var xType = x.GetType();
foreach (var property in xType.GetProperties())
{
if (property.GetValue(x, null).Implements(typeof(ICollection<>)))
{
var xValue = property.GetValue(x, null) as ICollection<T>;
var yValue = property.GetValue(y, null) as ICollection<T>;
}
Object.Implements() is an extension method I wrote to determine if a type implements an interface. As you can see, the method is incomplete. The problem I'm running into is that when I use property.GetValue(x, null), it returns an object, and I don't know how to cast it into its specific generic ICollection type. I need to be able to do this so I can use LINQ to do a x.Contains(y) to compare the two collections for equality. Any idea on how to do this?
P.S. I tried using Compare .NET Objects, but it's giving me a null reference exception somewhere deep within NHibernate. It doesn't properly handle how NHibernate proxies the ICollection for lazy loading. To make matters worse, NHibernate modifies the POCO to support lazy-loading, but this is all done at runtime. In the source code, it looks like you're just working with a regular ICollection, but NHibernate changes this to NHibernate.Collections.Generic.PersistentSet at runtime, and this is what's causing the comparer to fail.
Your question is a bit confusing because you don't need the type parameter T in the declaration of your EqualsProperties method. You just need
public static bool EqualsProperties(this object x, object y)
You then go on to use the same parameter T to cast properties of x and y to ICollection<T>; however, the objects in these collections obviously may have a different type than x and y.
Now to answer your question: you don't need to cast to the correct generic type to use the LINQ Contains method. You can do something like this:
xValue = property.GetValue(x, null);
yValue = property.GetValue(y, null);
if (typeof(IEnumerable).IsInstanceOf(x))
{
IEnumerable<object> xEnumerable = (x as IEnumerable).Cast<object>();
IEnumerable<object> yEnumerable = (y as IEnumerable).Cast<object>();
// use any LINQ method you like now
}
You should also make sure you use the LINQ overloads that take an equality comparer, as your domain objects obviously do not override the Equals method themselves. Otherwise you wouldn't be writing this unit testing code to compare them.
Sharp architecture framework use attribute to decor properties which should be taken into the equals method. See the source code of DomainSignatureAttribute class and EntityWithTypedId<>.Equals method.