Attribute Routing in ASP.NET Core 1.0 - asp.net-mvc-routing

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();
}
}

Related

How to pass options from Startup.cs to DbContextOptions constructor function at ASP.NET Core 2.0

I am using ASP.NET Core 2.0
At Startup.cs I have
services.AddDbContext<MailDBServicesContext>(optionsSqLite =>
{
optionsSqLite.UseSqlite("Data Source=Mail.db");
});
I have created a model and a DbContext where DbContext is:
public class MailDBServicesContext : DbContext
{
public MailDBServicesContext(DbContextOptions<MailDBServicesContext> options)
: base(options)
{
}
public DbSet<MailCountSentErrorMails> DbSetMailCountSentErrorMails { get; set; }
}
from a Class helper I need to pass DbContextOptions and my question is how can I tell to use the options from the Startup.cs ConfigureServices method
using (var db = new MailDBServicesContext())
{
}
It should be enough to simply inject MailDBServicesContext into your controller or a service class, for example.
public class SomeDataService
{
private readonly MailDBServicesContext _dbContext;
public SomeDataService(MailDBServicesContext dbContext)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public async Task AddMailCounts()
{
_dbContext.DbSetMailCountSentErrorMails
.Add(new MailCountSentErrorMails { CountSentMails = 55 });
await _dbContext.SaveChangesAsync();
}
}
Other DB context configuration options are defined in Configuring a DbContext on MSDN.
Update
Make sure to register your service in DI, i.e. ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISomeDataService, SomeDataService>();
services.AddDbContext<MailDBServicesContext>(optionsSqLite =>
{
optionsSqLite.UseSqlite("Data Source=Mail.db");
});
services.AddMvc();
}
Then make a call to AddMailCounts() in your controller.
public class HomeController : Controller
{
private readonly ISomeDataService _dataService;
public HomeController(ISomeDataService dataService)
{
_dataService = dataService ?? throw new ArgumentNullException(nameof(dataService));
}
public IActionResult Index()
{
_dataService.AddMailCounts();
return View();
}
}
Now every time you load homepage, a record is inserted into DbSetMailCountSentErrorMails table.
You can find working solution on my GitHub.

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);

How to set a controller method not to be routed using attribute 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() { ... }

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(...);

Parameterless implementation for Ninject mappings

i am creating custom membership provider using ninject for binding to sql classes.
my class in looks like
public MyMembershipProvider(IUsersRepository userRepository)
{
this.userRepository = userRepository;
}
How can i create from this parameterless constructor?
kernel.Bind<IUsersRepository>().To<UsersRepository>();
kernel.Bind<MembershipProvider>().To<MyMembershipProvider>();
and then:
public class AccountController : Controller
{
private readonly MembershipProvider _membershipProvider;
public AccountController(MembershipProvider membershipProvider)
{
_membershipProvider = membershipProvider;
}
public ActionResult Foo()
{
// TODO: Use the membership provider to do some processing
return View();
}
}