I'm designing an ASP.Net Webapi 2 solution which can possibly receive one of the forms of a request message from the [Frombody] as below.
How should I design the concrete class that will bind to the incoming request message? Any suggestion would be much appreciated.
<?xml version="1.0" encoding="utf-8"?>
<RequestMessage>
<Shapes>
<RectangleInfo></RectangleInfo>
</Shapes>
</RequestMessage>
or
<RequestMessage>
<Shapes>
<CicleInfo></CicleInfo>
</Shapes>
</RequestMessage>
or
<RequestMessage>
<Shapes>
<SquareInfo></SquareInfo>
</Shapes>
</RequestMessage>
Consider using the ModelBinder interface and return an IShape of the correct type: RectangleShape, CicleShape or SquareShape. Your controller method will look something like this when you get done:
public Task<HttpResponseMessage> GetShape(
[ModelBinder] IShape shape = null) { ... }
Your binder will look something like
using System;
using System.Net;
using System.Web.Http.Controllers;
using System.Web.Http.ModelBinding;
/// <summary>Web API ModelBinder for Shape.</summary>
/// <example>
/// At service startup include:
/// config.Services.Insert(typeof(ModelBinderProvider), 0, new SimpleModelBinderProvider(typeof(IShape), new ShapeBinder()));
/// <br/>
/// On controller methods that need a IShape
/// public async Task<HttpResponseMessage> SomeMethod([ModelBinder] IShape shape)
/// </example>
public class ShapeBinder : IModelBinder
{
/// <summary>Create a Shape.</summary>
/// <returns>true if a valid shape . false otherwise.</returns>
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(IShape))
{
return CreateShape(actionContext, bindingContext);
}
return false;
}
Your class heirarchy will be something like
public abstract class Shape : IShape { .. properties for all shapes ... }
public class SquareShape : RectangleShape { ... special case rectangle syntactic sugar that makes it a square ... }
public class RectangleShape : Shape { ... rectangle properties }
public class CicleShape : Shape { ... cicle properties }
and you might even some day add
public class CircleShape : EllipseShape { ... circle special case of ellipse properties ... }
public class EllipseShape : Shape { ... ellipse properties ... }
Perhaps a different answer than you were thinking but you said Any suggestion and this will probably serve you well.
Related
I have a Page which consist of AddPage.xaml and AddPage.xaml.cs. I want to create a generic class AddPage which extends from PhoneApplicationPage to outsource some repetitive code like Save or Cancel.
If I change the base class from PhoneApplicationPage to my new generic class, I get this error: Partial declarations of 'AddPage' must not specify different base classes.
To accomplish this you need to do the following.
First, create your base class
public class SaveCancelPhoneApplicationPage : PhoneApplicationPage
{
protected void Save() { ... }
protected void Cancel() { ... }
}
Then, your AddPage needs to be modified to inherit from the base class. The main places this is needed is within the code (AddPage.xaml.cs) AND within the xaml
Code:
public partial class AddPage : SaveCancelPhoneApplicationPage { ... }
Xaml:
<local:SaveCancelPhoneApplicationPage
x:Class="MyPhone.Namespace.AddPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyPhone.Namespace"
<!-- other xaml elements -->
</local:SaveCancelPhoneApplicationPage>
UPDATE: Info added based on comments
If you need to have generic like functionality and you must use the Page to do this (rather than a ViewModel) then you can still do this using generic methods
public abstract class SaveCancelPhoneApplicationPage : PhoneApplicationPage
{
protected override void OnNavigatedTo(blaa,blaa)
{
var obj = CreateMyObject();
obj.DoStuff();
}
// You should know what your objects are,
// don't make it usable by every phone dev out there
protected MyBaseObject MyObject { get; set; }
protected T GetMyObject<T>() where T : MyBaseObject
{
return MyObject as T;
}
}
public class AddPage : SaveCancelPhoneApplicationPage
{
public AddPage()
{
MyObject = new MyAddObject();
}
}
In order to outsource some functions you just declare some add class which does the common work. Having another page doesn't do that work.
public class Add
{
public bool SaveContent(string filename, string content)
{
....//some content
return true;
}
public string ViewContent(string filename)
{
string content="";
.....
return content;
}
}
Add this part of code where you thought it is redundant.
Add obj=new Add();
obj.SaveContent("myfile.txt","Hello.This is my content.");
string content("myfile.txt");
Tell me if this is what you intend or not.
Is it possible to pass an ViewModel object to WebApi controller action instead of separate params?
Instead of using:
public class ContactsController : ApiController
{
public IEnumerable<Contact> GetContacts(string p1, string p2)
{
// some logic
}
}
I would like to use:
public class ContactsController : ApiController
{
public IEnumerable<Contact> GetContacts(TestVM testVM)
{
// some logic
}
}
public class TestVM
{
public string P1 { get; set; }
public string P2 { get; set; }
}
This doesn't seem to work for me. When I call /api/contacts/?P1=aaa&P2=bbb the testVM object doesn't get populated (null).
Also, I would like the TestVM to have valdiation attribtues defined and use ModelState.IsValid in my API controller.
Unless told otherwise WebApi will deserialise complex models using the content/body of the request. To tell WebApi to use the Url to construct the model you need to specify the [FromUri] attribute:
public IEnumerable<Contact> GetContacts([FromUri]TestVM testVM)
{
// some logic
}
I know it's kind of late to post another answer but I thought it could be useful for anyone who uses .net core as a web API service
public IEnumerable<Contact> GetContacts([FromQuery]TestVM testVM)
I am trying to update an entity with a FK relationship in EntityFramework 4.3 Code First.
I try to attach to the related entites by calling: Entry(item).State = EntityState.Unchanged
I get the following exception: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
I do not update these items nor have an id property for them on my main entity.
Is it possible to know which entities are attached or not ?
Thanks in advance,
Radu
You can find the answer here.
public bool Exists<T>(T entity) where T : class
{
return this.Set<T>().Local.Any(e => e == entity);
}
Place that code into your context or you can turn it into an extension like so.
public static bool Exists<TContext, TEntity>(this TContext context, TEntity entity)
where TContext : DbContext
where TEntity : class
{
return context.Set<TEntity>().Local.Any(e => e == entity);
}
You can use this method:
/// <summary>
/// Determines whether the specified entity key is attached.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="key">The key.</param>
/// <returns>
/// <c>true</c> if the specified context is attached; otherwise, <c>false</c>.
/// </returns>
internal static bool IsAttached(this ObjectContext context, EntityKey key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
ObjectStateEntry entry;
if (context.ObjectStateManager.TryGetObjectStateEntry(key, out entry))
{
return (entry.State != EntityState.Detached);
}
return false;
}
For example:
if (!_objectContext.IsAttached(entity.EntityKey))
{
_objectContext.Attach(entity);
}
If you have arrived here from an EF Core Lazy Loading scenario in which Navigation properties were filled in a data layer via DbSet<>.Include() clause(s) while the Entity was attached to a DbContext and then that Entity was detached and passed up to a business layer, consider adding something like this to your DbContext.OnConfiguring(DbContextOptionsBuilder optionsBuilder) method:
optionsBuilder.ConfigureWarnings(warn => warn.Ignore(CoreEventId.LazyLoadOnDisposedContextWarning));
The error will be ignored and the values that were originally Include()d will be returned.
I use this extension method since I needed to check tracking based on values, not based on instance
internal static class DBExtensions
{
internal static bool IsAttached<TEntity>(this DbSet<TEntity> dbSet, Func<TEntity, bool> condition) where TEntity : class
{
return dbSet.Local.Any(condition);
}
}
Usage:
if (!context.Items.IsAttached(y => y.ItemId == item.ItemId))
{
context.Items.Attach(item);
}
I'm struggling with getting a custom attribute / filter working with ninject, constructor injection on the ASP.NET Web API.
Here's a few snippets to give some context...
//controller
[ApiAuthorise]
public IEnumerable<Thing> Get()
// Attribute definition with no body
public class ApiAuthoriseAttribute : FilterAttribute {}
// Custom Filter definition
public class ApiAuthoriseFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{ throw new NotImplementedException(); }
}
//Ninject module for my API authorisation
public class ApiAuthoriseModule : NinjectModule
{
public override void Load()
{
this.BindFilter<ApiAuthoriseFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<ApiAuthoriseAttribute>()
}}
//The registerServices(IKernel kernel) method in NinjectMVC3.cs
kernel.Load(new ApiAuthoriseModule());
That's literally all the code I have concerning this filter and attribute.
From what I understand I don't have to explicitly add the filter to the global filter collection as ninject takes care of that, is that correct?
If I place a constructor inside my attribute and throw an exception from within there I can see that the attribute is firing.
My suspicion is something I'm doing wrong within the Ninject side of things but after spending an afternoon reading others examples that appear to be identical to mine I'm know asking for help :)
TIA
There are different classes that you need to work with in Web API, not the standard System.Web.Mvc.FilterAttribute and System.Web.Mvc.IAuthorizationFilter that are used in normal controllers:
public class ApiAuthoriseAttribute : System.Web.Http.Filters.FilterAttribute
{
}
public class ApiAuthoriseFilter : System.Web.Http.Filters.IAuthorizationFilter
{
public System.Threading.Tasks.Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken, Func<System.Threading.Tasks.Task<HttpResponseMessage>> continuation)
{
throw new NotImplementedException();
}
public bool AllowMultiple
{
get { return false; }
}
}
Then you will obviously have to modify Ninject and the filter binding syntax (BindFilter extension method) to be able to register this new classes. Or wait for Ninject.MVC4 which will include this functionality.
I'm writing a custom ErrorHandler attribute for my MVC project. I would like to inject an implementation of EventViewerLogger into that attribute.
I'm using Ninject 2.2 and it works fine for other features, such as injection repositories and aggregate services through controller constructors.
I understand that I can't inject an implementation of some class into attribute through constructor, therefore I have to inject it into the attribute's property.
Interface is below:
namespace Foo.WebUI.Infrastructure
{
public interface ILogger
{
void Log(Exception e);
}
}
Event viewer logger implementation
namespace Foo.WebUI.Infrastructure
{
/// <summary>
/// Logs exceptions into the Windows Event Viewer
/// </summary>
public class EventViewerLogger: ILogger
{
private EventViewerLogger _logger = null;
EventViewerLogger()
{
_logger = new EventViewerLogger();
}
public void Log(Exception e)
{
_logger.Log(e);
}
}
}
Below is code for error handler:
namespace Foo.WebUI.Handlers
{
/// <summary>
/// Custom error handler with an interface to log exceptions
/// </summary>
public class CustomHandleErrorAttribute: HandleErrorAttribute
{
[Inject]
public ILogger Logger { get; set; }
// Default constructor
public CustomHandleErrorAttribute():base() { }
public override void OnException(ExceptionContext filterContext)
{
Logger.Log(filterContext.Exception);
base.OnException(filterContext);
}
}
}
In global.asax I register the handler and Ninject.
protected void Application_Start()
{
IKernel kernel = new StandardKernel(new NinjectInfrastructureModule());
}
Finally, I have a custom filter provider
namespace Foo.WebUI.Infrastructure
{
public class NinjectFilterProvider: FilterAttributeFilterProvider
{
private readonly IKernel kernel;
public NinjectFilterProvider(IKernel kernel)
{
this.kernel = kernel;
}
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
// Iterate through all the filters and use Ninject kernel to serve concrete implementations
foreach (var filter in filters)
{
kernel.Inject(filter.Instance);
}
return filters;
}
}
}
When I start the application I get the following exception:
Activation path:
2) Injection of dependency ILogger into property Logger of type CustomHandleErrorAttribute
1) Request for CustomHandleErrorAttribute
Suggestions:
1) Ensure that the implementation type has a public constructor.
2) If you have implemented the Singleton pattern, use a binding with InSingletonScope() instead.
Source Error:
Line 27: foreach (var filter in filters)
Line 28: {
Line 29: kernel.Inject(filter.Instance);
Line 30: }
Spent a day on this, learnt a lot about dependecy injection which is great, but what am I doing wrong here?
Ninject.Web.Mvc has this functionality built in called "BindFilter" which lets you map an attribute (that takes some or no constructor args) to a filter (which has its constructor args injected). Additionally, you can use it to copy values from the attribute and inject them as constructor args to the filter if you want. It also lets you change scope on your filters to be per action or per controller etc so that they actually get re-instantiated (normal action filters don't get re-instantiated per request).
Here's an example of how I've used it to do a UoW action filter.