Im New to ASP.NET MVC.
im just learning MVC and i am stuck in a situation where i want to update data in database using Ajax and EF.
I am using code first approach.
I have two projects in my solution. First is The Web MVC project named as Gem, The other i have entities in it with project name Gem.Domain
I have this entity named Category with file name Category.cs
namespace Gem.Domain
{
public class Category
{
public virtual int CategoryID { get; set; }
public virtual int ParentCategory { get; set; }
public virtual string Name { get; set; }
[MaxLength]
public virtual string Description {get;set; }
public virtual ICollection<Product> Products { get; set; }
}
}
with datasource file
namespace Gem.Domain
{
public interface IStoreDataSource
{
IQueryable<Product> Products { get; }
IQueryable<Category> Categories { get; }
}
}
Now in other Project Named Web
I have Area Registered with Name Admin which contains some controllers, but to be specific CategoriesController.cs reside in it.
And i have this method in this CategoriesController
public string UpdateCategoryName_DT(Category category)
{
return "Just A Test";
}
Finally coming to view.
I want to use ajax on a popup that appears on datatables.
Ajax request works fine.. and request do generates to correct method.
Here is my ajax code.
//Category Name Update Using Ajax.
$('#datatable').on('click', '.editable-submit', function () {
var rowID = $(this).parents('tr').find('td:eq(0)').text();
var updatedCategoryName = $(this).parents('div').siblings('div.editable-input').find('input').val();
var postData = {
ID: rowID,
CategoryName: updatedCategoryName
};
//For Ajax Request.
$.ajax({
type:"POST",
data:JSON.stringify(postData),
url:"#Url.Action("UpdateCategoryName_DT", "Categories", new { area = "Admin" })",
success: function (output) {
console.log(output);
}
});
});
Below is generated Ajax Request screen cap, using firebug for showing post info.
Main Question:
I want to know how to get the posted values in the controller through this ajax request and update the category name in database on base of posted values.
e-g i am getting ID of row and New Category name in ajax post, and i want to update the record in category table in database using entity framework.
How to do it, and what is the right method as i am using ajax.
I have tried youtube and tutorials but i am not understanding it.
I have DBContext with name of StoreDb which resides in Gem Project and Infrastructure Folder.
namespace Gem.Infrastructure
{
public class StoreDb : DbContext, IStoreDataSource
{
public StoreDb() : base("GemStoreConnection")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
IQueryable<Product> IStoreDataSource.Products
{
get
{
return Products;
}
}
IQueryable<Category> IStoreDataSource.Categories
{
get
{
return Categories;
}
}
}
}
plus i am using structuremap.mvc5 dependency resolution.
namespace Gem.DependencyResolution {
using Domain;
using Infrastructure;
using StructureMap.Configuration.DSL;
using StructureMap.Graph;
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
For<IStoreDataSource>().Use<StoreDb>();
}
#endregion
}
}
i am new but i have did this setup using tutorial for what i have understood so far. but there is nothing related to ajax so i need little help with ajax.
-=-=-=-=-=-=-=-=-=-=
Update:
Used a debugger, i think i am getting null, values are posting fine but i am getting null in controller ?
=-=-=-=-=-=-=-=-=--=-=-=-=-
Update 2:
i removed the JSON.stringify() and changed the Posted Data to this
var postData = {
CategoryID: rowID,
Name: updatedCategoryName
};
As now it matches to schema, so its working now..
but on other hand it also exposes my DB Schema. What if i want to post Ajax with Different ValuesNames, other than the database column names, what to do in that case?
Based on your comments, I think you are not much familiar with HTTP concept.
The simpliest way to read the object from ajax request (or any other POST request) is to update your Category model property names to match the once in json request (you can keep the first letter upper in c#, the rest has to be the same). Right now, your model has CategoryID and CategoryName, but in the json request, you are sending ID and Name parameters. Then, you need to add [FromBody] attribute to you action:
public string UpdateCategoryName_DT([FromBody]Category category)
{
return "Just A Test";
}
The attribute tells the framework, that it should parse the json from body of the request and creates an instance of Category object. Then you will not get null in as seen in your updated question.
UPDATE
You can have different names in json request and in database. You just need to use JsonProperty attribute on the property.
public class Category
{
[JsonProperty(PropertyName = "yourDesiredNameInJsonRequest")]
public virtual int CategoryID { get; set; }
public virtual int ParentCategory { get; set; }
[JsonProperty(PropertyName = "yourAnotherDesiredNameInJsonRequest")]
public virtual string Name { get; set; }
[MaxLength]
public virtual string Description {get;set; }
public virtual ICollection<Product> Products { get; set; }
}
To clarify the code above - the attribute will tell Json.Net to deserialize property "yourDesiredNameInJsonRequest" in your json and save it to Category.CategoryID field. It is just mapping.
Related
I am developing a dashboard in react which calls backend API to fetch all recipes from the database. So the search criteria would be huge. Its required to pass many filter attributes into the backend to get the correct recipes.
As an example below I have defined a class for Search Parameters
public class SearchParams
{
public string TemplateName { get; set; } = "";
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public String CreatedBy { get; set; } = "";
public Guid Id { get; set; }
}
So the GET method is required to handle whatever the parameters provided fetch the corresponding recipes from the DB accordingly.
But since GET requests doesnt support accepting parameters as OBJECT (SOrry if I am wrong) I thought about trying with POST. But that feels a little confused to use POST for a search functionality.
So with GET method do I need to define with all the parameters like this
[HttpGet]
public IEnumerable<Recipes> Get(string TemplateName,DateTime DateFrom....)
{
return new string[] { "value1", "value2" };
}
Or any best approach for this?
Please note, my real search criteria include many attributes other than the properties in my class definition above.
nothing prevents you from using SearchParams as an input parameters
[HttpGet]
public IEnumerable<Recipes> Search(SearchParams par)
the only problem is that Get doesn't include a body, so all data should be included in a query string
.../search?TemplateName=TemplateName&CreatedBy=....
I've an ASP.Net MVC 4 application that I'm porting to ASP.Net Core 3.0 MVC.
I'm trying to port this method
[HttpPost]
public ActionResult DoSave(
[Bind(Prefix = "new")]IEnumerable<C_Data> newItems,
[Bind(Prefix = "updated")]IEnumerable<C_Data> updatedItems,
[Bind(Prefix = "deleted")]IEnumerable<C_Data> deletedItems))
{
}
In the post AJAX (in JavaScript from the web browser) I'm sending the values as JSON like this
{
"new[0].Id":3,
"new[0].SID":"00000000-0000-0000-0000-000000000000",
"new[0].Name":"asd"
}
Here's the C_Data class
public class C_Data
{
[Key]
public int Id { get; set; }
public Guid SID { get; set; }
[Required]
[MaxLength(40)]
public string Name { get; set; }
}
But the three parameters are empty when this action is executed.
Here's the error I get in the ModelState
"The JSON value could not be converted to C_Data"
Anyone please can tell me how to port this method?
Thank you.
PD: This action is in an MVC controller not an API controller.
Here's a link that should help.
It looks like you should be able to use C_Data object, put it in an array, and stringify it in the AJAX call, receive an IEnumerable.
For Asp.Net Core, there are two ways to bind the model, ModelBinding and JsonInputFormatter. For sending request with json, it will use JsonInputFormatter and Bind will not work.
In general, I would suggest you try option below:
Controller Action
[HttpPost]
public ActionResult DoSave([FromBody]ItemModel itemModel)
{
return Ok("Worked");
}
Model
public class ItemModel
{
public IEnumerable<C_Data> NewItems { get; set; }
public IEnumerable<C_Data> UpdatedItems { get; set; }
public IEnumerable<C_Data> DeletedItems { get; set; }
}
Request Json
{
"newItems":[{
"Id":3,
"SID":"00000000-0000-0000-0000-000000000000",
"Name":"asd"
}]
}
This is a C# Question, using .NET framework built on Asp.NET Boilerplate.
Again, to re-emphasis the question being asked is "HOW...", so if an answer that was provided was a url link or a descriptive explanation on how something was supposed to be done, i would very much appreciate it. (Dont answer questions on how to tie shoelace by showing a picture of a tied shoe, nor do you answer "how to fish" by showing a recording of someone fishing...)
Since the question is pretty basic (i don't need to rephrase/repeat the header again), i'll give an example.
If i have a Forum service, and i create a class to load a Thread. Inside that thread class should be some sort of collection, array, list, or even a dbset of Post that is pulled on construct.
[Table("Thread", Schema = "dbo")]
public class ThreadModel
{
[Key]
public long Id { get; set; }
public string Title { get; set; }
//Idea 1
//Value should automatically be pulled and cached the moment class connects to database
public Post[] Posts { get; set; }
//Idea 2
//Post has a constructor to return all post that matches a thread id. While new tag keeps the return value constantly refreshed.
public Post[] Posts { get { return new Post(this.Id) } }
//Idea 3
//Not sure how collection is supposed to work. Does it automatically just pull or will i need to make a method to request?
public virtual ICollection<Post> Posts { get; set; }
//Example constructor
//When connected to database key-value pairs that match database labels will automatically get stored in class
protected ThreadModel()
{
//Idea 1-A
//Should be a value of null or empty if database yields no results
Posts = new Post();
}
public ThreadModel(int threadid) : this()
{
//Idea 1-A
Id = threadid;
//new Post => returns all posts in db
//Posts default value is all post in db
Posts = Posts.Select(post => post.threadid == this.id)
//Idea 3-A
Posts = Posts.Get(post => post.threadid == this.id)
//Idea 4
Posts = new Posts().GetThread(threadid);
}
}
Side questions
If all entities are created by inheriting Entity then at what point am i exposed to EntityFramework and DbContext?
I love this example here, submitted by a user as they attempt to connect ABP to their database. But their example doesn't show parent/child resources. I'm unable to find the guide they used to create that, and how it relates back to using ABP to fetch EntityFramework's DbContext example
How does this work? I'm unable to find instructions or explanation for this? (What am i to enter into google to get answers on these mechanics?)
[Table("AbpItems")]
public class Item : Entity
{
[ForeignKey("PostId")]
public Post Post { get; set; }
public int PostId { get; set; }
}
How does this integrate into/with abp's EntityFramework?
Where am i supposed to be creating my Database Table/Class? The project follows the Core.csproj, Application.csproj, and EntityFramework.csproj assembly layout. But it seems like every example is creating the classes at different stages or locations of the solution.
use GetAllIncluding. See https://github.com/aspnetboilerplate/aspnetboilerplate/issues/2617
Here's a complete solution ;
namespace EbicogluSoftware.Forum.Threads
{
[Table("Threads")]
public class Thread : FullAuditedEntity
{
[Required]
[StringLength(500)]
public virtual string Title { get; set; }
[Required]
[StringLength(2000)]
public virtual string Text { get; set; }
public virtual List<Post> Posts { get; set; }
public Thread()
{
Posts = new List<Post>();
}
}
[Table("Posts")]
public class Post : FullAuditedEntity
{
[Required]
[StringLength(2000)]
public virtual string Text { get; set; }
}
public class ThreadDto
{
public string Title { get; set; }
public string Text { get; set; }
public List<PostDto> Posts { get; set; }
public ThreadDto()
{
Posts = new List<PostDto>();
}
}
public class PostDto
{
public string Text { get; set; }
}
public class ThreadAppService : IApplicationService
{
private readonly IRepository<Thread> _threadRepository;
public ThreadAppService(IRepository<Thread> threadRepository)
{
_threadRepository = threadRepository;
}
public async Task<List<TenantListDto>> GetThreads()
{
var threads = await _threadRepository.GetAllIncluding(x => x.Posts).ToListAsync();
return threads.MapTo<List<TenantListDto>>();
}
}
}
Where am i supposed to be creating my Database Table/Class?
You can create them in YourProject.Core.proj
I have a view model and models similar to this
public class SupplierViewModel
{
public Supplier Supplier { get; set; }
//Select Lists and other non model properties
}
And two models
public class Supplier
{
public string Name { get; set; }
public Contact PrimaryContact { get; set; }
public List<Contact> SecondaryContacts { get; set; }
}
public class Contact
{
public string Name { get; set; }
}
But in my view the fields get prefixed with the class name so when I send it to the Web API controller it is in the following form
{
Supplier.Name: "test",
Supplier.PrimaryContact.Name: "test",
Supplier.SecondaryContacts: [
{ Name: "test" }
]
}
When I send it to my controller
[System.Web.Http.Route("Suppliers/{idSupplier?}")]
public HttpResponseMessage SuppliersAddOrEdit(Supplier Supplier, int idSupplier = 0)
It obviously doesn't deserialize because of the prefixes, currently I'm reformatting it before I send the request like this
{
Name: "test",
PrimaryContact: {Name: "test"},
SecondaryContacts: [
{
Name: "test"
}
]
}
then it binds OK, but I'm pretty sure that when I was sending data to an ActionController it knew even without specifying Bind[(Prefix)] that for example
PrimaryContact.Name: "test"
Would go into the class PrimaryContact. How do I achieve the same result in a Web API controller?
Edit: Based on Jon Susiak's answer I'd like to clarify further
If instead I use a Controller instead of a ApiController my model as it is would bind just fine sending the data in JSON with prefixes, is there a way to achieve the same thing in an ApiController?
In your view you are initially sending a SupplierViewModel however on the POST of the form you are expecting a Supplier object.
You can do one of two things:
a) Change the POST model to SupplierViewModel
b) Change the initial model to Supplier and put the additional properties and Lists in the ViewBag
I am starting to use breezejs for a project with a Web API 2.1 backend. I have an entity called Country that has a foreign key/navigation property to an entity called Continent.
I want to use the countries as lookup values but I also need their relationship to continents so I would like to fetch that info as well.
public class Country
{
public string Iso { get; set; }
public string Name { get; set; }
public virtual Continent Continent { get; set; }
}
I also have a FK field called continentIso but I don't use it in code.
Currently the backend controller looks like:
[HttpGet]
public object Lookups() {
var countries = _breezeRepository.Get<Country>().Include(it=>it.continent);
//more lookups in here
return new { countries };
}
As per the breeze samples I am returning an anonymous object of entities (I have a couple more but removed them from the above to avoid confusion).
On the front end side I have a lookup repository (demonstrated by John Papa's Building Apps with Angular and Breeze - Part 2):
function setLookups() {
this.lookupCachedData = {
countries: this._getAllLocal(entityNames.country, 'name'),
};
}
Problem is that although the sent JSON contains values for the continents, the countries object does not contain a value or a navigation property for them.
I have also tried bringing the continents as a separate lookup and try joining them through breeze metadata extension as I do for connecting lookups with entities but to no avail.
I also have a FK field called continentIso but I don't use it in code.
Probably that's the problem as explained here.
I would try the followings:
Make sure you have the Continent FK explicitly defined in your domain model. Eg.:
public class Country
{
public string Iso { get; set; }
public string Name { get; set; }
public string ContinentIso { get; set; }
public virtual Continent Continent { get; set; }
}
Also, in your controller, return not only the list of countries, but also the list of continents; breeze would make the binding. (not sure that the Include your have there is necessary).
[HttpGet]
public object Lookups() {
var countries = _breezeRepository.Get<Country>();
var countinents = _breezeRepository.Get<Continent>();
//more lookups in here
return new { countries, continents };
}