Bind /action/1,2,3 to List<int> - asp.net-mvc-2

In my API, I'd like to have routes like GET /api/v1/widgets/1,2,3 and GET /api/v1/widgets/best-widget,major-widget,bob-the-widget
public class WidgetsController : MyApiController
{
public ActionResult Show(IEnumerable<int> ids)
{
}
public ActionResult Show(IEnumerable<string> names)
{
}
}
I've got routes set up to get me to the action, but I can't figure out how to turn 1,2,3 into new List<int>(){1, 2, 3} and so on. Of course, I could just take a string and parse it in my action, but I'd like to avoid going that route.
One thing that came to mind was to put something in the OnActionExecuting method, but then I wasn't sure exactly what to put in there (I could hack something together, obviously, but I'm trying to write something reusable.)
The main questions I have are how to know whether I need to do anything at all (sometimes the ValueProviders upstream will have figured everything out), and how to handle figuring out the type to cast to (e.g., how do I know that in this case I need to go to a collection of ints, or a collection of strings, and then how do I do that?)
By the way, I had the idea of implementing a ValueProvider as well, but got lost on similar questions.

I can't figure out how to turn 1,2,3 into new List(){1, 2, 3} and so on.
To avoid polluting each controller action that needs to receive this parameter I would recommend a custom model binder:
public class IdsModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var result = base.BindModel(controllerContext, bindingContext);
var ids = bindingContext.ValueProvider.GetValue("ids");
if (ids != null)
{
return ids.AttemptedValue
.Split(',')
.Select(id => int.Parse(id))
.ToList();
}
return result;
}
}
and then register the model binder in Application_Start:
ModelBinders.Binders.Add(typeof(IEnumerable<int>), new IdsModelBinder());
and finally your controller action might look like this (and from what I can see in your question it already does look like this :-)):
public ActionResult Show(IEnumerable<int> ids)
{
...
}
and the custom model binder will take care for parsing the ids route token to the corresponding IEnumerable<int> value.
You could do the same with the IEnumerable<string> where you would simply remove the .Select clause in the corresponding model binder.

if your URL was
/api/v1/widgets/Show?names=best-widget&names=major-widget&names=bob-the-widget
This would bind neatly by itself :)
No need to override modelbinders in this case.
The querystring-variable names will bind to your Show-method_
public ActionResult Show(IEnumerable<string> names)
Hope this helps!

I'm relatively new to ASP.Net MVC and so I'm not sure if there is an easier way of doing this or not, however my approach would be to do something like the following:
public class WidgetsController : MyApiController
{
public ActionResult Show(string ids)
{
List<int> parsedIds = new List<int>();
foreach (var id in ids.Split(','))
{
parsedIds.Add(int.Parse(id));
}
return Show(parsedIds);
}
private ActionResult Show(List<int> ids);
}
You might also want to add some more sophisticated error handling for cases where the IDs entered can't be parsed, but thats the general approach I would use.

Related

EF Core load references of unknown entity

DISCLAIMER: Since we are all familiar with it, i will be using contoso university design to explain my question. Also, i am using EF core and .net core 2.0 on a mvc code first design.
I am developing a very generic RESTful API that works on any model. It has one method for each of create, read, update and delete operation in only one controller, the route of this is
[Route("/api/{resource}")]
Resource is the entity that the client wants to work with, for example if someone wants to get all Courses using the api he has to do a GET request on http://www.example.com/api/course/ or http://www.example.com/api/course/2 to get one by id and the following code will do the job.
[HttpGet("{id:int:min(1)?}")]
public IActionResult Read([FromRoute] string resource, [FromRoute] int? id)
{
//find resourse in models
IEntityType entityType = _context.Model
.GetEntityTypes()
.FirstOrDefault(x => x.Name.EndsWith($".{resource}", StringComparison.OrdinalIgnoreCase));
if (entityType == null) return NotFound(resource);
Type type = entityType.ClrType;
if (id == null)//select all from table
{
var entityRows = context.GetType().GetMethod("Set").MakeGenericMethod(type).Invoke(context, null);
if (entityRows == null)
return NoContent();
//TODO: load references (1)
return Ok(entityRows);
}
else //select by id
{
var entityRow = _context.Find(type, id);
if (entityRow == null)
return NoContent();
//TODO: load references (2)
return Ok(entityRows);
}
}
This small piece of code will do the magic with one small exception, intermediate collections will not be loaded. Given our example, the fetched course or courses will have no info for CourseInstructor (the intermediate collection in between Course and Person). I am trying to find a way to Eager load the navigation properties only if it is a collection; or by any other condition that will ensure that only many-to-many relationships are loaded.
For //TODO: load reference (2) i could use
_context.Entry(entityRow).Collection("CourseInsructor").Load();
On runtime if i could find all the navigation properties (filtered by spoken condition) and foreach of them i did Load(), i should get the desired result. My problem is when i get all (when id is null) the entityRows is type 'InternalDbSet' which is an unknown model.
So for the two TODOs i need some help on doing the following steps
1: find navigation properties of many-to-many relationships only
2: load them
Any suggestions?
In general, this seems like a very bad idea to me. While the CRUD stuff is going to be identical for most resources, there will be variances (as you've now run into). There's also something to be said for having a self-documenting API: with individual controllers, you know which resources can be accessed by nature of having a controller associated with that resource. With they way you're doing it, it's a complete black box. This also will of course effect any sort of actual generated API documentation. For example, if you were to include Swagger in your project, it would not be able to determine what you're doing here. Finally, you're now having to use reflection for everything, which will effect your performance.
What I would suggest instead is creating a base abstract controller and then creating a controller for each unique resource that inherits from that, for example:
public abstract class BaseController<TEntity> : Controller
where TEntity : class, new()
{
protected readonly MyContext _context;
public BaseController(MyContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
...
[HttpGet("create")]
public IActionResult Create()
{
var model = new TEntity();
return View(model);
}
[HttpPost("create")]
public async Task<IActionResult> Create(TEntity model)
{
if (ModelState.IsValid)
{
_context.Add(model);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(model);
}
...
}
I just wanted to give a quick example, but you'd build out all the rest of the CRUD methods in the same fashion, generically using TEntity. Then, for each actual resource, you simply do:
public class WidgetController : BaseController<Widget>
{
public WidgetController(MyContext context)
: base(context)
{
}
}
No duplication of code, but you've now got an actual real controller backing the resource, aiding both the innate and possibly explicit documentation of your API. And, no reflection anywhere.
Then, to solve problems like what you have here, you can add hooks to your base controller: essentially just virtual methods that are utilized in your base controller's CRUD actions and do nothing or just default things. However, you can then override these in your derived controllers to stub in additional functionality. For example, you can add something like:
public virtual IQueryable<TEntity> GetQueryable()
=> _context.Set<TEntity>();
Then, in your derived controller, you can do something like:
public class CourseController : BaseController<Course>
{
...
public override IQueryable<Course> GetQueryable()
=> base.GetQueryable().Include(x => x.CourseInstructors).ThenInclude(x => x.Instructor);
So, for example, you'd make your BaseController.Index action, perhaps, utilize GetQueryable() to get the list of entities to display. Simply by overriding this on the derived class, you can alter what happens based on the context of a particular type of resource.

EF Filtering a Child Table with Lazy Load

I'm using entity framework with POCOs and the repository pattern and am wondering if there is any way to filter a child list lazy load. Example:
class Person
{
public virtual Organisation organisation {set; get;}
}
class Organisation
{
public virtual ICollection<Product> products {set; get;}
}
class Product
{
public bool active {set; get;}
}
Currently I only have a person repository because I'm always starting from that point, so ideally I would like to do the following:
Person person = personRepo.GetById(Id);
var products = person.organisation.products;
And have it only load products where active = true from the database.
Is this possible and if so how?
EDIT My best guess would be either a filter can be added to the configuration of the entity. Or there might be a way to intercept/override the lazy load call and modify it. Obviously if I created an Organisation Repository I could manually load it as I please but I am trying to avoid that.
There's not a direct way to do this via lazy loading, but if you were willing to explicitly load the collection, you could follow whats in this blog, see the Applying filters when explicitly loading related entities section.
context.Entry(person)
.Collection(p => p.organisation.products)
.Query()
.Where(u => u.IsActive)
.Load();
You can do what Mark Oreta and luksan suggest while keeping all the query logic within the repository.
All you have to do is pass a Lazy<ICollection<Product>> into the organization constructor, and use the logic they provided. It will not evaluate until you access the value property of the lazy instance.
UPDATE
/*
First, here are your changes to the Organisation class:
Add a constructor dependency on the delegate to load the products to your
organization class. You will create this object in the repository method
and assign it to the Person.Organization property
*/
public class Organisation
{
private readonly Lazy<ICollection<Product>> lazyProducts;
public Organisation(Func<ICollection<Product>> loadProducts){
this.lazyProducts = new Lazy<ICollection<Product>>(loadProducts);
}
// The underlying lazy field will not invoke the load delegate until this property is accessed
public virtual ICollection<Product> Products { get { return this.lazyProducts.Value; } }
}
Now, in your repository method, when you construct the Person object you will assign the Organisation property with an Organisation object containing the lazy loading field.
So, without seeing your whole model, it will looks something like
public Person GetById(int id){
var person = context.People.Single(p => p.Id == id);
/* Now, I'm not sure about the cardinality of the person-organization or organisation
product relationships, but let's assume you have some way to access the PK of the
organization record from the Person and that the Product has a reference to
its Organisation. I may be misinterpreting your model, but hopefully you
will get the idea
*/
var organisationId = /* insert the aforementioned magic here */
Func<ICollection<Product>> loadProducts = () => context.Products.Where(product => product.IsActive && product.OrganisationId == organisationId).ToList();
person.Organisation = new Organisation( loadProducts );
return person;
}
By using this approach, the query for the products will not be loaded until you access the Products property on the Organisationinstance, and you can keep all your logic in the repository. There's a good chance that I made incorrect assumptions about your model (as the sample code is quite incomplete), but I think there is enough here for you to see how to use the pattern. Let me know if any of this is unclear.
This might be related:
Using CreateSourceQuery in CTP4 Code First
If you were to redefine your properties as ICollection<T> rather than IList<T> and enable change-tracking proxies, then you might be able to cast them to EntityCollection<T> and then call CreateSourceQuery() which would allow you to execute LINQ to Entities queries against them.
Example:
var productsCollection = (EntityCollection<Product>)person.organisation.products;
var productsQuery = productsCollection.CreateSourceQuery();
var activeProducts = products.Where(p => p.Active);
Is your repository using something like:
IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> expression)
If so you can do something like this:
var person = personRepo.Find(p => p.organisation.products.Any(e => e.active)).FirstOrDefault();
You could possibly use Query() method to achieve this. Something like:
context.Entry(person)
.Collection(p => p.organisation.products)
.Query()
.Where(pro=> pro.Active==true)
.Load();
Have a look at this page click here

Wicket - Wrapped collection Model "transformation"

I have a domain object which has a collection of primitive values, which represent the primary keys of another domain object ("Person").
I have a Wicket component that takes IModel<List<Person>>, and allows you to view, remove, and add Persons to the list.
I would like to write a wrapper which implements IModel<List<Person>>, but which is backed by a PropertyModel<List<Long>> from the original domain object.
View-only is easy (Scala syntax for brevity):
class PersonModel(wrappedModel: IModel[List[Long]]) extends LoadableDetachableModel[List[Person]] {
#SpringBean dao: PersonDao =_
def load: List[Person] = {
// Returns a collection of Persons for each id
wrappedModel.getObject().map { id: Long =>
dao.getPerson(id)
}
}
}
But how might I write this to allow for adding and removing from the original List of Longs?
Or is a Model not the best place to do this translation?
Thanks!
You can do something like this:
class PersonModel extends Model<List<Person>> {
private transient List<Person> cache;
private IModel<List<String>> idModel;
public PersonModel( IModel<List<String>> idModel ) {
this.idModel = idModel;
}
public List<Person> getObject() {
if ( cache == null ) {
cache = convertIdsToPersons( idModel.getObject() );
return cache;
}
public void setObject( List<Person> ob ) {
cache = null;
idModel.setObject( convertPersonsToIds( ob ) );
}
}
This isn't very good code but it shows the general idea. One thing you need to consider is how this whole thing will be serialised between requests, you might be better off extending LoadableDetachableModel instead.
Another thing is the cache: it's there to avoid having to convert the list every time getObject() is called within a request. You may or may not need it in practice (depends on a lot of factors, including the speed of the conversion), but if you use it, it means that if something else is modifying the underlying collection, the changes may not be picked up by this model.
I'm not quite sure I understand your question and I don't understand the syntax of Scala.
But, to remove an entity from a list, you can provide a link that simply removes it using your dao. You must be using a repeater to populate your Person list so each repeater entry will have its own Model which can be passed to the deletion link.
Take a look at this Wicket example that uses a link with a repeater to select a contact. You just need to adapt it to delete your Person instead of selecting it.
As for modifying the original list of Longs, you can use the ListView.removeLink() method to get a link component that removes an entry from the backing list.

Handling MVC2 variables with hyphens in their name

I'm working with some third-party software that creates querystring parameters with hyphens in their names. I was taking a look at this SO question and it seems like their solution is very close to what I need but I'm too ignorant to the underlying MVC stuff to figure out how to adapt this to do what I need. Ideally, I'd like to simply replace hyphens with underscores and that would be a good enough solution. If there's a better one, then I'm interested in hearing it.
An example of a URL I want to handle is this:
http://localhost/app/Person/List?First-Name=Bob&My-Age=3
with this Controller:
public ActionResult List(string First_Name, int My_Age)
{
{...}
}
To repeat, I cannot change the querystring being generated so I need to support it with my controller somehow. But how?
For reference, below is the custom RouteHandler that is being used to handle underscores in controller names and action names from the SO question I referenced above that we might be able to modify to accomplish what I want:
public class HyphenatedRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.RouteData.Values["controller"] = requestContext.RouteData.Values["controller"].ToString().Replace("-", "_");
requestContext.RouteData.Values["action"] = requestContext.RouteData.Values["action"].ToString().Replace("-", "_");
return base.GetHttpHandler(requestContext);
}
}
Have you tried [Bind(Prefix="First-name")]? It might work...
One way would be with a custom model binder. Another way would be with an action filter. Use the model binder if you want to do this on a specific type. Use the action filter if you want to do this on a specific action or controller. So for the latter method you could do something like:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var keys = filterContext.HttpContext.Request.QueryString.AllKeys.Where(k => k.Contains('-'));
foreach(var k in keys)
{
filterContext.ActionParameters.Add(
new KeyValuePair<string, object>(
k.Replace('-', '_'), filterContext.HttpContext.Request.QueryString[k]));
}
base.OnActionExecuting(filterContext);
}
I had the same problem. In the end rather than doing something too complex I just get the query string parameters using
string First_Name = Request["First-Name"];
You may want to check for NUlls incase the parameter is not there, but this sorted it out for me. You can also include an optional parameter for the ActionResult for test purposes etc..

Entity Framework and Entity Tracker Problems

If I run the following code it throws the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
public void Save(Category category)
{
using(var db = new NorthwindContext())
{
if(category.CategoryID == 0)
{
db.AddToCategorySet(category);
}
else
{
//category.RemoveTracker();
db.Attach(category);
}
db.SaveChanges();
}
}
The reason is of course that the category is sent from interface which we got from GetById method which already attached the EntityChangeTracker to the category object. I also tried to set the entity tracker to null but it did not update the category object.
protected void Btn_Update_Category_Click(object sender, EventArgs e)
{
_categoryRepository = new CategoryRepository();
int categoryId = Int32.Parse(txtCategoryId.Text);
var category = _categoryRepository.GetById(categoryId);
category.CategoryName = txtUpdateCategoryName.Text;
_categoryRepository.Save(category);
}
I'm still learning Entity Framework myself, but maybe I can help a little. When working with the Entity Framework, you need to be aware of how you're handling different contexts. It looks like you're trying to localize your context as much as possible by saying:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
...
}
}
... within your data access method. Did you do the same thing in your GetById method? If so, did you remember to detach the object you got back so that it could be attached later in a different context?
public Category GetById(int categoryId)
{
using (var db = new NorthwindContext())
{
Category category = (from c in db.Category where Category.ID == categoryId select c).First();
db.Detach(category);
}
}
That way when you call Attach it isn't trying to step on an already-attached context. Does that help?
As you pointed out in your comment, this poses a problem when you're trying to modify an item and then tell your database layer to save it, because once an item is detached from its context, it no longer keeps track of the changes that were made to it. There are a few ways I can think of to get around this problem, none of them perfect.
If your architecture supports it, you could expand the scope of your context enough that your Save method could use the same context that your GetById method uses. This helps to avoid the whole attach/detach problem entirely, but it might push your data layer a little closer to your business logic than you would like.
You can load a new instance of the item out of the new context based on its ID, set all of its properties based on the category that is passed in, and then save it. This costs two database round-trips for what should really only need one, and it isn't very maintainable.
You can dig into the context itself to mark the Category's properties as changed.
For example:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
db.Attach(category);
var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
stateEntry.SetModifiedProperty(propertyName);
}
db.SaveChanges();
}
}
This looks a little uglier, but should be more performant and maintainable overall. Plus, if you want, you could make it generic enough to throw into an extension method somewhere so you don't have to see or repeat the ugly code, but you still get the functionality out of it.