I'm using VS2013 and building a simple MVC5 app with EF6 (learning C#/MVC)
The app is simple. There's a table and an SP in an SQL database that serves up sequential job nos to users via web page. This SP will also be called by a another app so the biz logic is in the SP.
The SP takes a username and returns a JobNo (PK), which is derived as Max(JobNo) + 1
I'm using DB First (as I don't currently understand enough about migrations to production with code first and nearly everything I will write has to work with existing DB's and existing SP's)
I created the model from the DB using ADO.NET and chose the table and a few SP's. This created the following:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace JobNoServer.Models
{
using System;
using System.Collections.Generic;
public partial class JobNo
{
public int JobNo1 { get; set; }
public System.DateTime CreateDateTime { get; set; }
public string UserName { get; set; }
}
}
The problem I've got is that when I call the SP (user clicks "Get new Job No"), I only want to pass the username. The CreateDateTime will be current datetime (set in SP) and JobNo is determined in SP.
I tried removing the setter in class members:
public int JobNo1 { get; }
but then I get the error "must declare a body because it is not marked abstract or extern", but can't figure out how to fix this.
The other issue I have is that the controller created this Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "JobNo1,CreateDateTime,UserName")] JobNo jobNo)
{
if (ModelState.IsValid)
{
db.JobNo.Add(jobNo);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(jobNo);
}
But I can't pass a job no, as it doesn't exist until after Create is called. When I remove the JobNo from the add method, I get a message saying there is no overload that takes zero params. When i look at the definition of Add it's some kind of generic class and the create view is saying job no is mandatory
public class DbSet<TEntity> : DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable, IInternalSetAdapter where TEntity : class
{
Could someone point me in the right direction of how to have a create method that takes just the UserName, calls the SP and gets the return value?
There are lot's of way to do this let's look at a few options:
//add a post method to the JobNo class (makes controller logic dead simple)...
public partial class JobNo
{
public int JobNo1 { get; set; }
public System.DateTime CreateDateTime { get; set; }
public string UserName { get; set; }
public Task Post(){
using (var db = new My Entities){
//any issues found here are purely Data access related!
db.JobNo.Add(UserName);
await db.SaveChangesAsync();
}
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(JobNo vm)
{
if (ModelState.IsValid)
{
await vm.Post();
RedirectToAction("Index");
}
return View(vm);
}
But first you have to solve the problem of the SP wanting more than just 1 parameter right? Did you map the SP using DB first? Is JobNo class from the SP? There seems to be an "impedance mismatch"
By the looks of it you have your db mapped successfully, however if your db relies on Stored procedures to create PK instead of it's default functionality MVC can get a little confused, which is where you're having troubles.
Have you tried mapping the stored procedure to your context. Basically right click in your edmx diagram and 'update from database'. Instead of adding your tables(which you already did), you'll find the SP under the Stored Procedures and Functions section and add it. Done. Here's the MS how to...
If you map your stored procedure you can simply do something like the following
public JobNo myCreate( DateTime createDT, string uName)
{
int jobNo = 0;
using(Context db = new Context())
{
jobNo = db.yourSP(youParams probably uName, createDT);
}
return new JobNo() { JobNo1 = jobNo, CreateDateTime = createDT, UserName = uName};
}
Then you can reference the Create function in your question and just pass it the JobNo object you just created.
If all else fails and you can't seem to map your SP, remember you could always default to exectuting SQL directly against your context, and call your SP that way.
public JobNo myCreate( DateTime createDT, string uName)
{
int jobNo = 0;
using(Context db = new Context())
{
jobNo = db.Database.SqlQuery<int>("YourProcName #param1, #param2",
new SqlParameter("param1", createDT.toString()),
new SqlParameter("param2", uName));
}
return new JobNo() { JobNo1 = jobNo, CreateDateTime = createDT, UserName = uName};
}
Related
I am fairly new to c# and MVC but I am building an intranet app. Being on the internal network there is no need to sign in to use the app but I do have it connected to a database which has an 'Administration' table. In this table are the administrator's email addresses. I am also using System.DirectoryServices.AccountManagement and then UserPrincipal.Current.EmailAddress to get the users email address. What I would like to do is compare the UserPrincipal.Current.EmailAddress to the database table and if there is a match then set a boolean to TRUE that I can reference/call upon within my entire site.
I have a model matching the database tables and I can also query the database using a where statement to the value of UserPrincipal.Current.EmailAddress but only within a set method (ActionResult) and return the boolean value within a viewbag to that particular controller that is accessed by the related view only.
I would like to know what is best practice for setting up my site so that whichever page a users visits their email is compaired to the database and a boolean is set to true/false if they are/aren't in the database administrator table.
Edit: Would this be to create a base controller and then inherit it in all other controllers and within the base controller perform the database query - if so a little guidance would be greatly appricated
My current set up is an EmailEntityModel:
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
public partial class EmailEntities : DbContext
{
public EmailEntities()
: base("name=EmailEntities")
{
}
public virtual DbSet<Audience> Audiences { get; set; }
public virtual DbSet<CallToAction> CallToActions { get; set; }
public virtual DbSet<ColourScheme> ColourSchemes { get; set; }
public virtual DbSet<Email> Emails { get; set; }
public virtual DbSet<EmailType> EmailTypes { get; set; }
public virtual DbSet<Administrator> Administrators { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Then I have an email Controller:
public class EmailsController : Controller
{
private EmailEntities db = new EmailEntities();
public ActionResult Index()
{
return View(db.Emails.ToList());
}
Can I use the EmailEntities to query the Administator DBset within my controller but can I use this elsewhere?
If I understood your question correctly, you want to query the DB on every request and compare the current user's email against the admin email. If that's the case, then you have many options.
If it was me, I would keep the Admin email in a constant/static variable (so I don't have to make the trip to the DB on every request):
public static class StaticCache
{
// static constructor would run only once, the first it is used
// this value is maintained for the entire life-time of the application
static StaticCache()
{
using (var context = MyApplicationDbContext.Create())
{
// get your admin email for the DB
AdminEmail = context.Email.Where(/*some admin flag == true*/).FirstOrDefault();
}
}
public static string AdminEmail;
public static bool IsAdminUser(string curEmail)
{
return string.Equal(curEmail, AdminEmail, StringComparison.InvariantCultureIgnoreCase);
}
}
That's it. Now you can call StaticCache.IsAminUser() anywhere in your program (even in your view). All you need is to pass the current email to the method.
When using the OData Client (.NET) for v4 to create/post a new entity we are not getting the auto-generated (auto-incremented) ID/Key back from the service. When we create the new entity, we have no ID assigned (it is an 'int' so the value is '0'). After calling SaveChanges the result JSON response has the new auto-assigned id (e.g. '4662'). The issue is that the Entity on the client side still has '0' for its ID (it's not mapped back to the orig. entity).
I also opened the issue on GitHub: https://github.com/OData/odata.net/issues/775
Assemblies affected
Microsoft.Data.OData - v5.7.0
Microsoft.OData.Client - v6.15.0
Microsoft.OData.Core - v6.15.0
Microsoft.OData.Edm - v6.15.0
Reproduce steps
Save a new entity that will automatically have an ID assigned on the server-side (do not set this ID on the client-side)
After saving, check the ID property on your new entity (on the client-side)
Expected result
The newly created (and saved) entity will have the ID updated to match the server-side response (JSON) that came back to the client.
Actual result
JSON response form the server-side has the correct ID, but the Entity on the client-side is never updated with this new information.
In order to support your scenario, some requirements want to be met.
For a full reference, see my sample project at GitHub.
Given a model:
public class Item {
[Key] //define as key
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] //used by EF to auto generate Ids and mapping them back on inserts
public int Id { get; set; }
[Required(AllowEmptyStrings = false)]
public string Name { get; set; }
}
An EntityFramwork-DbContext:
public class SampleContext : DbContext {
public SampleContext()
: base("name=SampleContext") {
}
public DbSet<Person> Persons { get; set; }
public DbSet<Item> Items { get; set; }
}
A straight-forward OData-configuration:
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Person>("Persons");
builder.EntitySet<Item>("Items");
config.MapODataServiceRoute("ODataRoute", "odata", builder.GetEdmModel());
and a corresponding ODataController:
public class ItemsController : ODataController {
private readonly SampleContext _Db;
public ItemsController() {
_Db = new SampleContext();
}
...
[ResponseType(typeof(CreatedODataResult<Item>))]
public IHttpActionResult Post(Item p) {
if (!ModelState.IsValid)
return BadRequest(ModelState);
var inserted = _Db.Items.Add(p);
_Db.SaveChanges();
//return entity with any server side changes. This would also work for other DB-generated columns.
return Created(inserted);
}
}
Should produce the desired result:
public void AddingItemTransportsServerGeneratedIdBackToClientSideModel() {
var item = new Item() {
Name = Guid.NewGuid().ToString()
};
var container = new Container(new Uri("http://localhost/ODataAutoId/odata"));
container.AddToItems(item);
container.SaveChanges();
var actual = item.Id;
var unexpected = 0;
Assert.AreNotEqual(unexpected, actual);
}
Your actual implementation might vary or miss some of the requirements. In order to help in your particular scenarion, provide more details, as stated in my comment.
C# Web.Api Odata APplication
I’m implementing Odata V4 for the first time and would like to use my own custom class instead of the data class from the designer.
Here is what I did:
I created a Data project in Visual Studios and added my SQL Linq data table “Video”
I created the data context as follows:
public class VideoDataContext : DbContext
{
public VideoDataContext(): base("name=VideoData")
{
DbSet<VideoEf> Videos { get; set; }
}
And my custom class as follows:
[Serializable]
[DataContract]
public class VideoEf : Repository
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Isrc { get; set; }
}
And model builder:
public Microsoft.OData.Edm.IEdmModel GetEdmModel()
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
EntityTypeConfiguration<VideoEf> titleType = builder.EntityType<VideoEf>();
builder.EntitySet<VideoEf>("Video");
return builder.GetEdmModel();
}
And in my video controller:
public class VideoController : ODataController
{
VideoDataContext db = new VideoDataContext ();
[EnableQuery(PageSize = 20, MaxExpansionDepth = 5)]
public IHttpActionResult Get()
{
return Ok(db.Videos.AsQueryable());
}
When I make the call to get the video entities I keep getting a ” 406 Not Acceptable” error message
How can I ensure that the data returned from the database is mapped to my custom model ?
Is my model builder correct?
what could be causing the above error?
You don't need to return IQueryable because you have EnableQuery attribute, just return DbSet.
You also don't need any wcf attribute and EntityTypeConfiguration<VideoEf> titleType = builder.EntityType<VideoEf>();
Then it should just work.
Hope it helps.
Edit
My mistake for IQueryable, I also use it.
I can't seem to get the edit function of my view to work..i have a page that lists, a page that shows specific detail and on that page, i should be able to edit the information of the form..PROBLEM: when i run the application it says:No parameterless constructor defined for this object. What am i doing wrong...?
In the Home Controller i have:
Edit Functions:
[HttpGet]
public ViewResult EditSchoolDetails(int id)
{
var institution = _educationRepository.GetInstititionById(id);
var model = (Mapper.Map<Institution, InstitutionModel>(institution));
return View(model);
}
post
[HttpPost]
public ActionResult EditSchoolDetails( InstitutionModel institutionModel, int id)
{
if (ModelState.IsValid) {
//_get from repository and add to instituion
var institution = _educationRepository.GetInstititionById(institutionModel.Id);
// Map from the view model back to the domain model
var model = Mapper.Map<Institution, InstitutionModel>(institution);
//UpdateModel(model);
SaveChanges();
return RedirectToAction("ViewSchoolDetails", new {institutionModel = institutionModel, id = id});
}
return View(institutionModel);
}
InstitutionModel
public class InstitutionModel {
public InstitutionModel() {
NAABAccreditations = new List<AccreditationModel>();
}
public int Id { get; set; }
public string Name { get; set; }
public bool IsNAAB { get { return NAABAccreditations.Any(); } }
public string Website { get; set; }
public AddressModel Address { get; set; }
public IEnumerable<AccreditationModel> NAABAccreditations { get; set; }
}
Does the Institution class have a parameterless constructor? If not, that will be the problem. You are passing an InstitutionModel to the the edit view, so the post action should probably take an InstitutionModel too, then you can map back to the original Institution model:
public ActionResult EditSchoolDetails(int id, InstitutionModel institutionModel)
{
if (ModelState.IsValid)
{
//add to database and save changes
Institution institutionEntity = _educationRepository.GetInstititionById(institution.Id);
// Map from the view model back to the domain model
Mapper.Map<InstitutionModel, Institution>(institutionModel, institutionEntity);
SaveChanges();
return RedirectToAction("ViewSchoolDetails",);
}
return View(institutionModel);
}
Notice also how it returns the view model back to the view if the model state isn't valid, otherwise you will lose all your form values!
Here's a similar question too which might help: ASP.NET MVC: No parameterless constructor defined for this object
Is it possible you need to pass a parameter to ViewSchoolDetails? I notice in the return statement you commented out that you were passing it an id, but in the return statement you're using, you're not passing in anything.
EDIT
This (from your comment below):
parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult ViewSchoolDetails(Int32)
...tells me you need to pass a parameter to ViewSchoolDetails
EDIT 2
I saw your edit, and would say this: if the method you are calling is
public ActionResult ViewSchoolDetails(InstitutionModel institutionModel, int id)
Then you MUST pass it an object of type InstitutionModel and an int as parameters or you will get an exception. Meaning, you need
RedirectToAction("ViewSchoolDetails", new {institutionModel = institutionModel, id = id});
Whenever i get this, i have forgotten to create a parameter-less constructor on my view-model. I always add one now just in case it's needed and i forget.
Does InstitutionModel have one?
I'm using the Fluent Validation framework in my ASP.net MVC 3 project. So far all of my validations have been very simple (make sure string is not empty, only a certain length, etc.) but now I need to verify that something exists in the database or not.
Should Fluent Validation be used in this case?
If the database validation should be done using Fluent Validation, then how do I handle dependencies? The validator classes are created automatically, and I would need to somehow pass it one of my repository instances in order to query my database.
An example of what I'm trying to validate might:
I have a dropdown list on my page with a list of selected items. I want to validate that the item they selected actually exists in the database before trying to save a new record.
Edit
Here is a code example of a regular validation in Fluent Validation framework:
[Validator(typeof(CreateProductViewModelValidator))]
public class CreateProductViewModel
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class CreateProductViewModelValidator : AbstractValidator<CreateProductViewModel>
{
public CreateProductViewModelValidator()
{
RuleFor(m => m.Name).NotEmpty();
}
}
Controller:
public ActionResult Create(CreateProductViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
var product = new Product { Name = model.Name, Price = model.Price };
repository.AddProduct(product);
return RedirectToAction("Index");
}
As you can see, I never create the Validator myself. This works because of the following line in Global.asax:
FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure();
The problem is that now I have a validator that needs to interact with my database using a repository, but since I'm not creating the validators I don't know how I would get that dependency passed in, other than hardcoding the concrete type.
Can't you just create your own validation method where in you would kick-off the database validation?
RuleFor(m => m.name)
.Must(BeInDatabase)
private static bool BeInDatabase(string name)
{
// Do database validation and return false if not valid
return false;
}
I'm using FluentValidation for DataBase validations. just pass the Validation class the session in the Ctor. and do the validation inside the action something like:
var validationResult = new ProdcutValidator(session).Validate(product);
Update: Based on your example I add my example...
public class CreateProductViewModel
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class CreateProductViewModelValidator : abstractValidator<CreateProductViewModel>
{
private readonly ISession _session;
public CreateProductViewModelValidator(ISession session)
{
_session = session;
RuleFor(m => m.Name).NotEmpty();
RuleFor(m => m.Code).Must(m, Code => _session<Product>.Get(Code) == null);
}
}
Controller:
public ActionResult Create(CreateProductViewModel model)
{
var validator = new CreateProductViewModelValidator();
var validationResult =validator.Validate(model);
if(!validationResult.IsValid)
{
// You will have to add the errors by hand to the ModelState's errors so the
// user will be able to know why the post didn't succeeded(It's better writing
// a global function(in your "base controller" That Derived From Controller)
// that migrate the validation result to the
// ModelState so you could use the ModelState Only.
return View(model);
}
var product = new Product { Name = model.Name, Price = model.Price };
repository.AddProduct(product);
return RedirectToAction("Index");
}
Second update:
If you insist using parameterless constructor you will have to use some Inversion Of control container, a static class that is something like the Factory of your objects.
use it like this:
public class CreateProductViewModelValidator : abstractValidator<CreateProductViewModel>
{
private readonly ISession _session;
public CreateProductViewModelValidator()
{
_session = IoC.Container.Reslove<ISession>();
RuleFor(m => m.Name).NotEmpty();
RuleFor(m => m.Code).Must(m, Code => _session<Product>.Get(Code) == null);
}
}
You can find many IoC containers, most famous are Windsor and Ninject,
You will need to register- instruct the container once to resolve all the ISession to return your's session object.
The other way this could work for you is using Constructor injection. While this method isn't as clear cut as using an IoC library, it may help if you have a static way of accessing or fetching your session.
public class CreateProductViewModelValidator
{
private ISession _session;
public CreateProductViewModelValidator()
:this(SessionFactory.GetCurrentSession()) //Or some other way of fetching the repository.
{
}
internal CreateProductViewModelValidator(ISession session)
{
this._session = session;
RuleFor(m => m.Name);//More validation here using ISession...
}
}
I have been spending quite a bit of time thinking about this exact same issue. I am using ninject to inject my repository into my web UI layer so that my web UI only accesses the database through an interface.
I am wanting to be able to validate things that access the database such as checking for duplicate names and hence my validation needs to access the injected repository. I think that the best way to do this is to just setup Fluent Validation via the manual method rather than the MVC integrated way. For Example:
Create your validation Class (can pass in repository Interface):
public class CategoryDataBaseValidation : AbstractValidator<CategoryViewModel>
{
private IRepository repository;
public CategoryDataBaseValidation (IRepository repoParam)
{
repository = repoParam;
RuleFor(Category => Category.Name).Must(NotHaveDuplicateName).WithMessage("Name already exists");
}
private bool NotHaveDuplicateName(string name)
{
List<Category> c = repository.Categories.ToList(); //Just showing that you can access DB here and do what you like.
return false;
}
}
}
Then in your controller you can just create an instance of above class and pass in the repository (that ninject would have injected in the controller constructor)
[HttpPost]
public ActionResult Create(CategoryViewModel _CategoryViewModel )
{
CategoryDataBaseValidation validator = new CategoryDataBaseValidation (repository);
ValidationResult results = validator.Validate(_CategoryViewModel );
if (results.IsValid == false)
{
foreach (var failure in results.Errors)
{
//output error
}
}
return View(category);
}
Both the above files can live in the Web UI project and you can then also just use the standard MVC DataAnnotations for client side validation.
Just thought that I would put this up for comment / help someone.