I'm trying to implement custom paging with the EntitySetController.
public class MyController : EntitySetController<Poco, int>
{
public IQueryable<Poco> Get()
{
var result = _myBusinessLogic.Search(QueryOptions.Top.Value);
return result.AsQueryable()
}
}
I think I'm missing something because it looks like the controller is trying to apply paging to the results of the Search method that already returns just one page. How can I prevent it from doing that and apply paging myself?
It looks like I can just inherit from ODataController instead and implement:
public IEnumerable<Poco> Get(ODataQueryOptions odataQueryOptions)
but I was wondering if I can stay with EntitySetController so that there's less plumbing code to write.
I wanted to stick to the OData format and not return PageResult<>
You can only take full control of the query using ODataQueryOptions or let the framework handle it completely for you using QueryableAttribute. There is no middle ground unfortunately.
So, I think doing an ODataController is the right way to solve this now.
That said, I can suggest a dirty workaround that would work for the time being. Beware that this relies on internal implementations that might/will change and break you.
public class MyController : EntitySetController<Poco, int>
{
public IQueryable<Poco> Get()
{
var result = _myBusinessLogic.Search(QueryOptions.Top.Value);
RemoveQueryString(Request, "$top");
return result.AsQueryable()
}
// This method relies that code that looks for query strings uses the extension
// method Request.GetQueryNameValuePairs that relies on cached implementation to
// not parse request uri again and again.
public static void RemoveQueryString(HttpRequestMessage request, string name)
{
request.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] = request.GetQueryNameValuePairs().Where(kvp => kvp.Key != name);
}
}
Related
For Asp.net mvc core app:
In one of my dbcontext tables I have records with HelpInfo (id as primary key, and string for helpInfo) that I want to use as html title attributes in my razor views, such as:
<div title='#I(12)'></div>
Where 12 is an example id of a HelpInfo record and #I should be a global accessible class method that fetches the corresponding helpInfo string.
I tried to implement King Kings approach such as:
The CustomView:
public abstract class CustomView<TModel> : RazorPage<TModel>
{
protected IInfo Info => Context.RequestServices.GetRequiredService<IInfo>();
public Task<string> I(int code)
{
return Info.GetAsync(code);
}
}
The Interface:
public interface IInfo
{
Task<string> GetAsync(int code);
}
The Service:
public class Info : IInfo
{
private readonly ApplicationDbContext context;
public Info(ApplicationDbContext context)
{
this.context = context;
}
public async Task<string> GetAsync(int code)
{
var myRecord = await context.MyRecords.FindAsync(code);
if (myRecord != null)
{
return myRecord.Info;
}
return null;
}
}
In _ViewImports.cshtml I added #inherits CustomView<TModel>
In the view I used <div title='#(await I(12))'>Test</div>
When I load the view I get
No service for type '...Models.IInfo' has been registered
Any help to pin down the problem would be appreciated.
As I understand looks like you want something like #Html or #Url which are supported in the default Page and RazorPage. That's just a custom property exposed by the base page. So in your case, you need a custom view (for mvc) or a custom page (for razor pages). Here is an example of a custom view used for MVC:
public abstract class CustomView<TModel> : RazorPage<TModel>
{
//custom members can be declared in here
}
Based on your desired usage of I, it must be a method. So it can be a method exposed by some service injected in your base page. Suppose that interface is like this:
public interface IInfo {
string Get(int code);
}
Now your custom page can implement the I method like this:
public abstract class CustomView<TModel> : RazorPage<TModel>
{
//NOTE: the custom page class does not support constructor injection
//So we need to get the injected services via the Context like this.
protected IInfo Info => Context.RequestServices.GetRequiredService<IInfo>();
public string I(int code){
return Info.Get(code);
}
}
To use your custom view, you need to use the directive #inherits in your view or better in the _ViewImports.cshtml so that you don't have to repeat that #inherits everywhere, like this:
#inherits CustomView<TModel>
Sometimes the base view is not applied (so the base members are not available) until you rebuild your project.
Now you can use the I in your views as what you desire, like this:
<div title="#I(12)"></div>
Note the I method returns a string in my example, you can also make it return an IHtmlContent (e.g: HtmlString) or whatever type you need for your requirement.
NOTE:
Technically you can use any services (including ones querying for data from database), but in such cases please ensure that the querying is as fast as possible and use async to have the best performance & avoid thread starvation. Here is an example of the IInfo service that queries from a database:
public interface IInfo {
Task<string> GetAsync(int code);
}
public class Info : IInfo {
//inject whatever your Info needs
//here we just need some DbContext (used in EFCore)
public Info(InfoDbContext dbContext){
_infoDbContext = dbContext;
}
readonly InfoDbContext _infoDbContext;
public async Task<string> GetAsync(int code){
var info = await _infoDbContext....
return info;
}
}
The I method then should be async as well:
public Task<string> I(int code){
return Info.GetAsync(code);
}
I hope that you know how to register a DbContext to use in your code (that's part of EFCore so you may have to learn more about that first). Now to use the async method, you call it in your view like this:
<div title="#(await I(12))"></div>
Again, I would try to avoid querying the db in such a helper like that. As I said, the helper's method can be called multiple times right in one same view so usually we have fast methods or use caching for the info it need. This is related to another feature called caching which has more to be put in one short answer, you can learn more about that.
Solution:
To my Startup.cs I added:
services.AddScoped<IInfo, Info>();
services.AddTransient<IInfo, Info>(); also does work.
My ASP.NET Core application is using our self-designed pipelines to process requests. Every pipeline contains 1+ blocks, and the number of blocks have no any limit. it can be up to 200+ blocks in real instance, the pipeline will go through all blocks by a sequence from a configuration, like:
Pipeline<DoActionsPipeline>().AddBlock<DoActionAddUserBlock>().AddBlock<DoActionAddUserToRoleBlock>()...
Like above example(just an example), and there are 200+ blocks configured in this pipeline, the blocks could be DoActionAddUserBlock, DoActionAddUserToRoleBlock, DoActionAddAddressToUserBlock, and so on. many actions are mixed in one pipeline. (Please don't ask why mix them, it's just an example, it doesn't matter to my question.)
For this example, in each block, we will check the action name first, if match, then run logics. but this is pretty bad, it has to instance all blocks and go throgh all of them to get a request done.
Here is sample code, not very good, but it shows my pain:
public class DoActionAddUserBlock : BaseBlock<User, User, Context>
{
public override User Execute(User arg, Context context)
{
if (context.ActionName != "AddUser")
{
return arg;
}
return AddUser(arg);
}
protected User AddUser(User user)
{
return user;
}
}
public abstract class BaseBlock<TArg, TResult, TContext>
{
public abstract TResult Execute(TArg arg, TContext context);
}
public class Context
{
public string ActionName { get; set; }
}
public class User
{
}
I want to avoid instancing blocks by conditions, I think it should be in pipeline-configuration level. how can I reach this? Attributes? or something others.
[Condition("Action==AddUser")] // or [Action("AddUser")] // or [TypeOfArg("User")]
public class DoActionAddUserBlock : BaseBlock<User, User, Context>
{
public override User Execute(User arg, Context context)
{
return AddUser(arg);
}
//...
}
Please show us the Pipeline<T>() method (is a method or a class?), because it's essential for an accurate answer.
Anyway i want to try my best with the current infos.
Your goal is "i want to conditionally instance blocks", so you have to move your condition in a out-of-instance context, something you can do with attributes:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ActionNameAttribute : Attribute
{
public ActionNameAttribute(string name)
{
this.Name = name;
}
public string Name { get; }
}
[ActionName(nameof(AddUser))]
public class DoActionAddUserBlock : BaseBlock<User, User, Context>
{
public override User Execute(User arg, Context context)
{
return AddUser(arg);
}
}
Then, do the check into the .AddBlock<T>() method (that, i guess, is something like that):
public YourUnknownType<T> AddBlock<TBlock>()
{
var type = typeof(TBlock);
var attributes = attributes.GetCustomAttributes(typeof(ActionNameAttribute), inherit: true); // or false if you don't need inheritation
var attribute = attributes.FirstOrDefault() as ActionNameAttribute;
if (attribute.Name == this.Context.ActioName)
{
// place here the block init
}
return AnythingYouActuallyReturn();
}
Hope this helps!
IMO
you should define different pipelines for different usage. That's a design pattern that should be used only for some particular cases. Maybe it is not good pattern in your case?
I think that it shouldn't be in pipeline responsibility to check the action name and MAYBE run logic. If you define a pipeline for some logic it should just "go with the flow".
Therefore, pipelines should be build once on project startup and initializing whole pipeline just once is good.
Please think about if using pipelines is good in your scenario.
I've built a simple pipeline with builder and steps you can check it here. It's in polish but all the code is in English so you might get the point.
I am using Ninject with the following packages:
Ninject
Ninject.MVC5
Ninject.Web.Common (and Common.WebHost)
Ninject.Web.WebApi (and WebApi.WebHost)
I have a WebApi2 Controller that looks like the below. My Get() method must be performant and it doesn't depend on the value of IMyFooService, thus I don't care if it gets injected or not when Get() is requested.
Question:
Is there a way for me to selectively bind interfaces only if certain api methods are called? Whether through using attributes or...?
public class FooController : ApiController {
public IMyFooService fooService;
public FooController(IMyFooService fooService) {
this.fooService = fooService;
}
[NonDependent] // Don't really care about the value of fooService
public JsonResult Get() {}
[Dependent] // Must have valid dependency injection
public async Task<JsonResult> Post([FromBody] IList foos) {
var didMyFoo = this.fooService.DoTheFoo();
}
}
Here is my NinjectWebCommon.cs:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IMyFooService>().To<MyConcreteService>().InRequestScope();
}
I noticed that To<T>() has many .When() options. Perhaps I can make use of this to say .When(/* Controller = Foo, Action = Post */).
The easiest, and probably most succinct, way is to use Lazy<T> which was made exactly for this use case - quoting from the docs:
Use lazy initialization to defer the creation of a large or
resource-intensive object, or the execution of a resource-intensive
task, particularly when such creation or execution might not occur
during the lifetime of the program.
Support for Lazy<T> injection comes with Ninject.Extensions.Factory (also see it's Wiki Page on Lazy<T>). Install it's nuget package and you should be ready to inject Lazy<T>.
Adapt the code of your controller as follows:
public class FooController : ApiController {
public Lazy<IMyFooService> fooService;
public FooController(Lazy<IMyFooService> fooService) {
this.fooService = fooService;
}
public JsonResult Get() {}
public async Task<JsonResult> Post([FromBody] IList foos) {
var didMyFoo = this.fooService.Value.DoTheFoo();
}
}
Please notice that the actual service is accessed by the .Value Property on Lazy<T>. On first access to this property the instance is retrieved.
Similar question has been asked some time ago. Check this out. So for you particular case you can just modify IsRouteValueDefined method (you can think about some better naming, I would suggest something like IsRoutePoitingTo) from original answer to something like this (you might revisit if that works for WebApi, but for sure there is a way to get current route for that as well):
public static bool IsRouteValueDefined(string controller, string action)
{
var mvcHanlder = (MvcHandler)HttpContext.Current.Handler;
var routeValues = mvcHanlder.RequestContext.RouteData.Values;
var containsRouteKey = routeValues.ContainsKey(routeKey);
if (routeValue == null)
return containsRouteKey;
return containsRouteKey &&
routeValues["controller"].ToString().Equals(controller, StringComparison.InvariantCultureIgnoreCase) &&
routeValues["action"].ToString().Equals(action, StringComparison.InvariantCultureIgnoreCase);
}
And binding will look like:
kernel.Bind<IMyFooService>()
.To<MyConcreteService>()
.When(x=> IsRouteValueDefined("foo", "get"));
Just not sure about "get" as for ApiController the actual route could be http://website.com/foo/, if so, simply use string.Empty as "action" param. You can check that with your particular project. As you don't need default injection (which is present in original answer) - I just dropped that.
Maintenance Edit
After using this approach for a while I found myself only adding the exact same boilerplate code in every controller so I decided to do some reflection magic. In the meantime I ditched using MVC for my views - Razor is just so tedious and ugly - so I basically use my handlers as a JSON backend. The approach I currently use is to decorate my queries/commands with a Route attribute that is located in some common assembly like this:
[Route("items/add", RouteMethod.Post)]
public class AddItemCommand { public Guid Id { get; set; } }
[Route("items", RouteMethod.Get)]
public class GetItemsQuery : IQuery<GetItemsResponse> { }
// The response inherits from a base type that handles
// validation messages and the like
public class GetItemsResponse : ServiceResponse { }
I then implemented an MVC host that extracts the annotated commands/queries and generates the controllers and handlers for me at startup time. With this my application logic is finally free of MVC cruft. The query responses are also automatically populated with validation messages. My MVC applications now all look like this:
+ MvcApp
+- Global.asax
+- Global.asax.cs - Startup the host and done
+- Web.config
After realizing I really don't use MVC outside the host - and constantly having issues with the bazillion dependencies the framework has - I implemented another host based on NServiceKit. Nothing had to be changed in my application logic and the dependencies are down to System.Web, NServiceKit and NServiceKit.Text that takes good care of the model binding. I know it's a very similar approach to how NServiceKit/ServiceStack does their stuff but I'm now totally decoupled from the web framework in use so in case a better one comes along I just implement another host and that's it.
The situation
I'm currently working on an ASP.NET MVC site that's implementing the businesslogic-view separation via the IQueryHandler and ICommandHandler abstractions (using the almighty SimpleInjector for dependency injection).
The Problem
I've got to attach some custom validation logic to a QueryHandler via a decorator and that's working pretty well in and of itself. The problem is that in the event of validation errors I want to be able to show the same view that the action would have returned but with information on the validation error of course. Here is a sample for my case:
public class HomeController : Controller
{
private readonly IQueryHandler<SomeQuery, SomeTransport> queryHandler;
public ActionResult Index()
{
try
{
var dto = this.queryHandler.Handle(new SomeQuery { /* ... */ });
// Doing something awesome with the data ...
return this.View(new HomeViewModel());
}
catch (ValidationException exception)
{
this.ModelState.AddModelErrors(exception);
return this.View(new HomeViewModel());
}
}
}
In this scenario I have some business logic that's handled by the queryHandler that is decorated with a ValidationQueryHandlerDecorator that throws ValidationExceptions when it is appropriate.
What I want it to do
What I want is something along the lines of:
public class HomeController : Controller
{
private readonly IQueryHandler<SomeQuery, SomeTransport> queryHandler;
public ActionResult Index()
{
var dto = this.queryHandler.Handle(new SomeQuery { /* ... */ });
// Doing something awesome with the data ...
// There is a catch-all in place for unexpected exceptions but
// for ValidationExceptions I want to do essentially the same
// view instantiation but with the model errors attached
return this.View(new HomeViewModel());
}
}
I've been thinking about a special ValidationErrorHandlerAttribute but then I'm losing the context and I can't really return the proper view. The same goes with the approach where I just wrap the IQueryHandler<,> with a decorator... I've seen some strange pieces of code that did some string sniffing on the route and then instantiating a new controller and viewmodel via Activator.CreateInstance - that doesn't seem like a good idea.
So I'm wondering whether there is a nice way to do this ... maybe I just don't see the wood from the trees. Thanks!
I don't think there's a way to make the action method oblivious to this, since the action method is in control of the returned view model, and in case of a validation exception you need to return a view model with all the actual data (to prevent the user from losing his changes). What you might be able to do however to make this more convenient is add an extension method for executing queries in an action:
public ActionResult Index()
{
var result = this.queryHandler.ValidatedHandle(this.ModelState, new SomeQuery { });
if (result.IsValid) {
return this.View(new HomeViewModel(result.Data));
}
else
{
return this.View(new HomeViewModel());
}
}
The ValidatedHandle extension method could look like this:
public static ValidatedResult<TResult> ValidatedHandle<TQuery, TResult>(
this IQueryHandler<TQuery, TResult> handler,
TQuery query, ModelStateDictionary modelState)
{
try
{
return new ValidatedResult<TResult>.CreateValid(handler.Handle(query));
}
catch (ValidationException ex)
{
modelState.AddModelErrors(ex);
return ValidatedResult<TResult>.Invalid;
}
}
Do note that you should only catch such validation exception if the validation is on data that the user has entered. If you send a query with parameters that are set programmatically, a validation exception simply means a programming error and you should blog up, log the exception and show a friendly error page to the user.
Here is my issue. I am using ASP.NET Web API 2.0 and the QueryableAttribute to take advantage of some of the OData filtering functionality.
public class VideoController : ApiController
{
[HttpGet]
[Route("activevideos")]
[Queryable]
public IEnumerable<Video> GetActiveVideos(ODataQueryOptions<Video> options)
{
return new GetvContext().Videos.Where(c => c.IsActive);
}
}
Now, I have a class that I have been using to modify the response object and contained entities. This was working fine before I started using the QueryableAttribute. Before this, I was returning a List from the previous method instead of IEnumerable.
public class TestMessageProcessHandler : MessageProcessingHandler
{
protected override HttpResponseMessage ProcessResponse(
HttpResponseMessage response, CancellationToken cancellationToken)
{
var content = ((ObjectContent)(response.Content)).Value;
// Do something here with the content. This used to be a List<Video>
// but now the object it is of type:
// System.Data.Entity.Infrastructure.DbQuery<System.Web.Http.OData.Query.Expressions.SelectExpandBinder.SelectSome<Content.Api.Video>>
}
}
I need to be able to get the entity from this and I am not sure how to get from type:
System.Data.Entity.Infrastructure.DbQuery<System.Web.Http.OData.Query.Expressions.SelectExpandBinder.SelectSome<Content.Api.Video>> to something like List<Video> so I can modify the Video object.
Remove the [Queryable] attribute and manage the querying of the data yourself - something like this:
public class VideoController : ApiController
{
[HttpGet]
[Route("activevideos")]
public IList<Video> GetActiveVideos(ODataQueryOptions<Video> options)
{
var s = new ODataQuerySettings() { PageSize = 1 };
var result = options.ApplyTo(
new GetvContext().Videos.Where(c => c.IsActive), s)
.ToList();
return result;
}
}
Can't you just do a ToList() - the DbQuery implements IQueryable...
e.g.
var content = ((ObjectContent)(response.Content)).Value;
var queryable = content as IQueryable<Video>;
var list = queryable.ToList();
Most probably you are getting this problem because you specified which fields should be selected by your OData query (for example, $select=Nr,Date). If you won't do this, after applying query options OData won't return a SelectSome objects, it will return the same objects you are querying, in your case Video. So you can easily modify these objects server-side before returning them to client.
And if you need to exclude properties from Video object which you don't need, instead of $select query option you can use a DTO instead of your Video object and include in this DTO only those fields you need.
Yes, this is a workaround, and hopefully there will be better means to do deal with server-side logic in the future versions of this library