How to set a controller method not to be routed using attribute routing - asp.net-mvc-routing

I have applied a route attribute at the controller level, but I want to exclude an action from being routed. Not overriding but excluding the route completely. How can this be achieved?
Let's say I have:
[RoutePrefix("promotions")]
[Route("{action=index}")]
public class ReviewsController : Controller
{
// eg.: /promotions
public ActionResult Index() { ... }
// eg.: /promotions/archive
public ActionResult Archive() { ... }
// eg.: /promotions/new
public ActionResult New() { ... }
// eg.: /promotions/edit/5
[Route("edit/{promoId:int}")]
public ActionResult Edit(int promoId) { ... }
public void Internal() { ... }
}
and I want Internal not to be routed.
I would have expected to find a [DoNotRoute] or [Ignore] attribute, but I didn't find anything like that.

Use the [NonAction] attribute:
[NonAction]
public void Internal() { ... }

Related

Attribute Routing in ASP.NET Core 1.0

Do I need to configure anything to use attribute routing in an ASP.NET Core 1.0 application?
The following doesn't seem to be working for me. I was expecting to hit this method when I go to localhost:132/accounts/welcome
public class AccountsController : Controller
{
[Route("welcome")]
public IActionResult DoSomething()
{
return View();
}
}
An alternative you can use is to apply a RoutePrefix or Route on your class. Then you won't have to repeat that part on the action attributes.
[Route("[controller]")]
public class AccountsController : Controller
{
[Route("welcome")]
public IActionResult DoSomething()
{
return View();
}
}
Looks like I needed to add the controller token in there
public class AccountsController : Controller
{
[Route("[controller]/welcome")]
public IActionResult DoSomething()
{
return View();
}
}

Windsor and DbContext per request - DbContext has been disposed

I have a method in HomeController, that I'm trying to access through URL, like this:
http://localhost/web/home/GetSmth
First time it works, but after refreshing page, I get this error:
The operation cannot be completed because the DbContext has been disposed.
As the title states, I'm trying to use Castle Windsor and DbContext per request.
public class Installer1 : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IController>()
.LifestyleTransient()
);
var connString = ConfigurationManager.ConnectionStrings["MainDbContext"].ConnectionString;
container.Register(Component.For<MainDbContext>().DependsOn(Property.ForKey("conn").Eq(connString)).LifeStyle.PerWebRequest);
container.Register(Component.For<ISomeService>().ImplementedBy<SomeService>());
}
}
HomeController looks like this:
public class HomeController : Controller
{
private ISomeService _someService;
public HomeController(ISomeService someService)
{
_someService = someService;
}
public ActionResult Index()
{
return View();
}
public JsonResult GetSmth()
{
var data = _someService.GetData().ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
}
You are registering ISomeService with the default lifecycle, which is singleton. Once it's created, it will keep using the same DbContext. Simplest solution is to change its lifecycle to per request or transient.
container.Register(Component.For<ISomeService>()
.ImplementedBy<SomeService>()
.LifeStyle.PerWebRequest);

What is the best way to move sql-linked methods of entities from a data context to entitiy class?

I like EntityFramework. Usually I create a service class (layout) to put there the logic of interaction with a database. It looks like this:
public class UserService
{
MyDbContext _context;
public UserService(MyDBContext context)
{
_context = context;
}
public void MoveUserToGroup(User user, Group group)) { ... }
}
And I use that so somewhere in my code:
userService.MoveUserToGroup(User user, Group group);
It's good, but I would like my classes to look like this:
public class User
{
// ...
public void AddTo(Group group) { ... }
}
public class Group
{
// ...
public void Add(User user) { ... }
}
And I want to use that so:
user.AddToGroup(group);
What is the best way to do it? DI? Extensions? How to keep database context across my classes?
You would usually map all the related navigational properties of an entity.
public class User
{
public virtual ICollection<Group> Groups { get; set; }
public void AddTo(Group group)
{
Groups.Add(group);
}
}

how to reference controller function from another controller

Trying to learn ASP MVC coming from Linux/LAMP background (in other words I'm a newb) ...
For some reason I can't seem to use a function defined in a controller in another controller.
Here's the function in my MessagesController.cs file:
public List<Message> GetMessagesById(string username)
{
return db.Messages.Where(p => p.user == username).ToList();
}
When I try to reference it:
using LemonadeTrader.Models;
using LemonadeTrader.Controllers; // added this to pull the Messages::getMesssagesById
...
ViewBag.messages = lemondb.Messages.GetMessagesById(Membership.GetUser().ProviderUserKey.ToString());
I get something along the lines of lemondb.Messages does not contain a method called GetMesssagesById.
How do I reference it?
You shouldn't be linking controller methods like this, not to mention that controllers shouldn't be performing data access directly. I would recommend you externalizing this function into a separate class/repository which could be used by both controllers.
Example:
public class MessagesRepository
{
public List<Message> GetMessagesById(string username)
{
return db.Messages.Where(p => p.user == username).ToList();
}
}
and then:
public class FooController: Controller
{
public ActionResult Index()
{
var db = new MessagesRepository()
ViewBag.messages = db.GetMessagesById(Membership.GetUser().ProviderUserKey.ToString());
return View();
}
}
public class BarController: Controller
{
public ActionResult Index()
{
var db = new MessagesRepository()
ViewBag.messages = db.GetMessagesById(Membership.GetUser().ProviderUserKey.ToString());
return View();
}
}
OK, that's the first step. This code could be improved by decoupling the controllers from the repository by introducing an abstraction for this repository:
public interface IMessagesRepository
{
List<Message> GetMessagesById(string username);
}
public class MessagesRepository: IMessagesRepository
{
public List<Message> GetMessagesById(string username)
{
return db.Messages.Where(p => p.user == username).ToList();
}
}
then you could use constructor injection for those controllers:
public class FooController: Controller
{
private readonly IMessagesRepository _repository;
public class FooController(IMessagesRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
ViewBag.messages = _repository.GetMessagesById(Membership.GetUser().ProviderUserKey.ToString());
return View();
}
}
public class BarController: Controller
{
private readonly IMessagesRepository _repository;
public class BarController(IMessagesRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
ViewBag.messages = _repository.GetMessagesById(Membership.GetUser().ProviderUserKey.ToString());
return View();
}
}
finally you would configure your DI framework to pass the corresponding implementation into those controllers.
I would also recommend you replacing this ViewBag with a strongly typed view model:
public class MyViewModel
{
public List<Message> Messages { get; set; }
}
and then:
public ActionResult Index()
{
var model = new MyViewModel
{
Messages = _repository.GetMessagesById(Membership.GetUser().ProviderUserKey.ToString())
};
return View(model);
}
Place GetMessageById (and all other methods needed for accessing messages) to separate class and use the class everywhere you need to get Message data.
MessageService service = new MessageService();
ViewBag.messages = service.GetMessagesById(...);

MEF Custom attributes and Lazy

I think I am losing my mind. :)
I've been struggling with this for two days now. The code looks right. But for some reason when I try to access the [ImportMany] field, it is null, or at least not returning any values.
It get the 3 parts in the catalog, but they don't get applied to the Lazy[] import I am defining.
Here's my code:
using System;
using System.Linq;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace MefTest
{
// Extension interface and metadata
public interface IUIExtension
{
void DoSomething();
}
public interface IUIExtensionDetails
{
string Name { get; }
string Uri { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class UIExtensionAttribute : ExportAttribute
{
public UIExtensionAttribute() : base(typeof(IUIExtensionDetails)) { }
public string Name { get; set; }
public string Uri { get; set; }
}
// Extensions
[UIExtension(Name="Test 01", Uri="http://www.yourmomma.com/")]
public class Test1Extension : IUIExtension
{
public void DoSomething() { }
}
[UIExtension(Name = "Test 02", Uri = "http://www.yourdaddy.com/")]
public class Test2Extension : IUIExtension
{
public void DoSomething() { }
}
[UIExtension(Name = "Test 03", Uri = "http://www.youruncle.com/")]
public class Test3Extension : IUIExtension
{
public void DoSomething() { }
}
// Main program
public class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
[ImportMany]
public Lazy<IUIExtension, IUIExtensionDetails>[] Senders { get; set; }
public void Run()
{
Compose();
}
public void Compose()
{
var catalog = new AssemblyCatalog(
System.Reflection.Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
// This is always 3
Console.WriteLine(
(from g in container.Catalog.Parts select g).Count());
// This is always 0
Console.WriteLine(Senders.Length);
Console.ReadKey();
}
}
}
Your error is here:
public UIExtensionAttribute() : base(typeof(IUIExtensionDetails))
You should pass the contract type there, not the metadata type:
public UIExtensionAttribute() : base(typeof(IUIExtension))
(Also, in order to make sure that your custom export class has the right properties as expected by the import with metadata, I would make it implement the IUIExtensionDetails interface. But that is not mandatory.)
Your metadata attribute is defining the exports as typeof(IUIExtensionDetails) which is your metadata contract, not your actual extension. Change the custom attribute constructor to:
public UIExtensionAttribute() : base(typeof(IUIExtension)) { }