Using .Net Nore C# MVC
I have the following code:
My data:
public IEnumerable<string> GetData(string id)
{
return _context.TableData
.Where(r => r.ID == id)
.Select (r => r.Name).ToList();
}
My MVC controller:
public IActionResult GetDDL(string id)
{
var result = _repo.GetData(id);
//Above gives me
//[0] = "United States"
//[1] = "United Kingdom"
var json = JsonConvert.SerializeObject(result);
//Above json convert gives me ["United States","United Kingdom"]
return Json(json);
}
Above code does return me Json but I wanted to know how can I create new JSON such that it return me as below:
[{"Text":"United States","Text":"United Kingdom"}]
Basically, I want to bind this back to my dropdown which expects the "Text" property with the JSON array.
1. In the controller
public IActionResult GetDDL(string id)
{
var result = _repo.GetData(id);
var withProp = result.Select(x => new { Text = x });
var json = JsonConvert.SerializeObject(withProp);
return Json(json);
//Or shorter:
var result = _repo.GetData(id).Select(x => new { Text = x });
var json = JsonConvert.SerializeObject(result);
return Json(json);
}
2. In the repository
You cannot return an anonymous type so you should create a class that'll hold the data and use that to return from the repository:
public class Data
{
public string Text { get; set; }
}
public IEnumerable<Data> GetData(string id)
{
return _context.TableData
.Where(r => r.ID == id)
.Select (r => new Data { Text = r.Name });
}
Usage in the controller remains unchanged then:
public IActionResult GetDDL(string id)
{
var result = _repo.GetData(id);
var json = JsonConvert.SerializeObject(result);
return Json(json);
}
Related
how to retrieve partial object?
{
Id:123,
Name:"david",
Languages:[{b:"en"},{b:"ru"}]
}
public async Task<myObj> Get(long id, string lang=null)
{
FilterDefinition<myObj> filter = Builders<myObj>.Filter.Eq(s => s.Id, id)
& Builders<myObj>.Filter.ElemMatch(l => l.Languages, s => s.b== lang);
ProjectionDefinition<myObj> projection = Builders<Symptom>.Projection
.Include(d => d.Id)
.Include(d => d.Name)
.Include(d => d.Languages[-1]);
FindOptions<myObj> options = new FindOptions<myObj> { Projection = projection };
using (IAsyncCursor<myObj> cursor = await db.Collection.FindAsync(filter, options))
{
return cursor.SingleOrDefault();
}
}
if i call function get(123,"cn") i expect to get:
{
Id:123,
Name:"david",
Languages:null
}
instead of null.
how to fix the query to achieve my demand?
i think this will get the job done:
public async Task<myObj> Get(long id, string lang = null)
{
var res = await db.Collection.AsQueryable()
.Where(m =>
m.Id == id &&
m.Languages.Any(l => l.b == lang))
.SingleOrDefaultAsync();
return res ?? new myObj { _Id = id, Languages = null };
}
If you want to display the languages only when they match (and null if none match up), then try the following
public async Task<myObj> Get(long id, string lang = null)
{
FilterDefinition<myObj> filter = Builders<myObj>.Filter.Eq(s => s.Id, id)
var result = await collection.Find(filter).SingleOrDefaultAsync();
if (result != null)
result.Languages = result.Languages?.Where(lng => lng.b.Equals(lang)).ToList();
return result;
}
You will get your object that you want based on the ID.. then further it will return only those languages that match up with language that you are passing (null or otherwise).
It's working. I don't know what you mean by "instead of null".
One minor thing, that you would like to not include Languges, instead you projected the Languages to an array range with the [-1]. So it's just return last element of the array. The final code is:
> db.ItemWithLanguages.find()
{ "_id" : ObjectId("5dfb57c9692d22eefa6e0cfe"), "Id" : 123, "Name" : "david", "Languages" : [ { "B" : "en" }, { "B" : "cn" } ] }
internal class MyObj
{
public long Id { get; set; }
[BsonId]
[BsonElement("_id")]
public ObjectId MyId { get; set; }
public string Name { get; set; }
public List<Language> Languages { get; set; }
}
internal class Language
{
public string B { get; set; }
}
public static async Task<MyObj> Get(IMongoCollection<MyObj> collection, long id, string lang = null)
{
FilterDefinition<MyObj> filter = Builders<MyObj>.Filter.Eq(s => s.Id, id)
& Builders<MyObj>.Filter.ElemMatch(l => l.Languages, s => s.B == lang);
// excluding d.Languages by not including it.
// it makes Languages = null.
ProjectionDefinition<MyObj> projection = Builders<MyObj>.Projection
.Include(d => d.Id)
.Include(d => d.Name);
FindOptions<MyObj> options = new FindOptions<MyObj> { Projection = projection };
using (IAsyncCursor<MyObj> cursor = await collection.FindAsync(filter, options))
{
return cursor.SingleOrDefault();
}
}
...
string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("test");
var myObjs = db.GetCollection<MyObj>("ItemWithLanguages");
MyObj ret;
Task.Run(async () => { ret = await Get(myObjs, 123, "cn"); }).ConfigureAwait(false).GetAwaiter()
.GetResult();
The code generates all events of a specific producer, so that works.
But the problem is that when I return new ApiListModel< EventPerYearReport >() it goes to the class ApiListModel and then it says "status_code": 200, "data": null.
How do I generate, by executing the following URL: /api/dashboard/1, a value instead of "status_code": 200, "data": null?
DashboardController.cs
[HttpGet("{id}")]
public async Task<ApiListModel<EventPerYearReport>> GetEventPerYearReport(int id)
{
return await _dashboardService.GetEventPerYearReport(id);
}
DashboardService.cs
public async Task<ApiListModel<EventPerYearReport>> GetEventPerYearReport(int id)
{
var results = _dbContext.Events
.Where(p => p.ProducerId == id)
.GroupBy(e => e.DateStartEvent.ToString("yyyy"), (year, values) => new EventPerYear
{
year = year,
total = values.Count(),
}).ToList();
var report = new EventPerYearReport()
{
eventsPerYear = results
};
return new ApiListModel<EventPerYearReport>();
}
ApiListModel.cs
public class ApiListModel<T>
{
[JsonProperty(PropertyName = "status_code")]
public int StatusCode = 200;
[JsonProperty(PropertyName = "data")]
public List<T> Data;
}
EventPerYearReport.cs
public class EventPerYearReport
{
public List<EventPerYear> eventsPerYear { get; set; }
}
Your DashboardService.cs is returning new ApiListModel<EventPerYearReport>(); this will always have null data.
To fix this you need to use the following
public async Task<ApiListModel<EventPerYearReport>> GetEventPerYearReport(int id)
{
var results = _dbContext.Events
.Where(p => p.ProducerId == id)
.GroupBy(e => e.DateStartEvent.ToString("yyyy"), (year, values)
=> new EventPerYear
{
year = year,
total = values.Count(),
}).ToList();
return new ApiListModel<EventPerYearReport>()
{
Data = new EventPerYearRaport()
{
eventsPerYear = results
}
};
}
I'm able to return a list from controller below to view:
public ActionResult FillSegment(int siteid)
{
var segment = db.Segment.Where(c => c.SiteID == siteid).Select(x => new
{
SegmentID = x.SegmentID,
SegmentName = x.SegmentName
});
return Json(segment, JsonRequestBehavior.AllowGet);
}
But if i want to do the query operation in other classes, and i call the class from FillSegment Action, how it should return?
Controller:
public ActionResult FillSegment(int siteid)
{
SegmentHelper segmenthelper = new SegmentHelper();
return Json(segmenthelper.FillSegment(siteid), JsonRequestBehavior.AllowGet);
}
segmenthelper Class:
public List<string> FillSegment(int siteid)
{
using (DBConnection db = new DBConnection())
{
var segment = db.Segment.Where(c => c.SiteID == siteid).Select(x => new
{
SegmentID = x.SegmentID,
SegmentName = x.SegmentName
});
return segment.ToList(); <-- Cannot convert generic list to generic list?
}
}
In such cases it is probably better to introduce a ViewModel class and return it. You might want to use some mapping library.
public class SegmentViewModel
{
public string SegmentID { get; set; } //not sure if it of string type
public string SegmentName { get; set; }
}
public IEnumerable<SegmentViewModel> FillSegment(int siteid)
{
using (DBConnection db = new DBConnection())
{
return db.Segment.Where(c => c.SiteID == siteid).Select(x => new SegmentViewModel
{
SegmentID = x.SegmentID,
SegmentName = x.SegmentName
});
}
}
// you can also use JsonResult instead of ActionResult
public JsonResult FillSegment(int siteid)
{
SegmentHelper segmenthelper = new SegmentHelper();
return Json(segmenthelper.FillSegment(siteid), JsonRequestBehavior.AllowGet);
}
The Select statement will return you a sequence of anonymous objects with properties SegmentID and SegmentName and they cannot be converted to list of strings.
You had to change return type of FillSegment.
Im not 100% sure but i think this should work
Controller:
public ActionResult FillSegment(int siteid)
{
SegmentHelper segmenthelper = new SegmentHelper();
return Json(segmenthelper.FillSegment(siteid), JsonRequestBehavior.AllowGet);
}
Segmenthelper Class:
public list<Segment> FillSegment(int siteid)
{
using (DBConnection db = new DBConnection())
{
var segment = db.Segment.Where(c => c.SiteID == siteid).Select(x => new
{
SegmentID = x.SegmentID,
SegmentName = x.SegmentName
}).toList();
return segment;
}
}
In my MedicalProductController I am trying to convert a MedicalProduct (model class) named "product" into IEnumerable<MedicalProduct> so it can be passed to GetMedicalProductViewModelList, which will return an IEnumerable<MedicalProductViewModel> (viewModel class).
I am trying to keep SQL calls to a minimum. as per advice of other stackoverflow members.
But "product" fails to turn into IEnumerable<MedicalProduct> in my MedicalProductController Class
MedicalProductController
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();
// other CRUD code omitted for brevity.
// =============
// Edit HttpGet
// =============
public ActionResult Edit(int id = 0)
{
MedicalProduct product = _db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
var productList = product as IEnumerable<MedicalProduct>;
var viewModelList = GetMedicalProductViewModelList(productList);
return View(viewModelList);
}
// =============
// Edit HttpPost
// =============
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(MedicalProduct product)
{
if (ModelState.IsValid)
{
_db.Entry(product).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
var productList = product as IEnumerable<MedicalProduct>;
var viewModel = GetMedicalProductViewModelList(productList);
return View(viewModel);
}
// =====================
// Mapper Class Helper
// =====================
public IEnumerable<MedicalProductViewModel> GetMedicalProductViewModelList(IEnumerable<MedicalProduct> productList)
{
var brandList = _db.Brands.ToArray();
var mapper = new MedicalProductMapper();
return mapper.MapMedicalProductViewModel(productList, brandList);
}
And just in case it is helpful, here is the mapping class:
MedicalProductMapper
public class MedicalProductMapper
{
// =====================
// Model to ViewModel
// =====================
public IEnumerable<MedicalProductViewModel> MapMedicalProductViewModel(IEnumerable<MedicalProduct> medicalProductList, IEnumerable<Brand> brandList)
{
var brandSelectListItem = brandList.Select(b => new SelectListItem()
{
Text = b.Name,
Value = b.Name
});
var viewModelList = medicalProductList.Select(p => new MedicalProductViewModel()
{
BrandID = p.BrandID,
BrandName = brandList.SingleOrDefault(b => b.ID == p.BrandID).Name,
BrandSelectListItem = brandSelectListItem,
ID = p.ID,
Price = p.Price,
Name = p.Name
});
return viewModelList;
}
// =====================
// ViewModel to Model
// =====================
public MedicalProduct MapMedicalProduct(MedicalProductViewModel VM)
{
var model = new MedicalProduct()
{
Name = VM.Name,
Price = VM.Price,
BrandID = VM.BrandID
};
return model;
}
}
Instead of
var productList = product as IEnumerable<MedicalProduct>;
Try
var productList = new List<MedicalProduct> { product };
You can't cast to IEnumerable<MedicalProduct> if it's not an IEnumerable, what I did in this example is first create a new List of type MedicalProduct and then add the product to the List.
List is one of the types that implement IEnumerable
What is the correct way of returning JSON data via API controller in MVC4? I've heard that you need to use variable type as function, however I cannot do that because I can't use .Select(x => new { }) then.
What I do instead is to use dynamic like so
[HttpGet]
public dynamic List() // Not List<Item>
{
var items = _db.Items.OrderBy(x => x.ID).Select(x => new
{
ID = x.ID,
Title = x.Title,
Price = x.Price,
Category = new {
ID = x.Category.ID,
Name = x.Category.Name
}
});
return items;
}
Is this best way of doing this? I'm askin' because I just started with MVC4 and I don't want to pick up bad habits early :)
Built-in function Controller.Json (MSDN) can do what you want, i.e. assuming your code resides inside controller class:
[HttpGet]
public dynamic List() // Not List<Item>
{
var items = _db.Items.OrderBy(x => x.ID).Select(x => new
{
ID = x.ID,
Title = x.Title,
Price = x.Price,
Category = new {
ID = x.Category.ID,
Name = x.Category.Name
}
});
return Json(items, JsonRequestBehavior.AllowGet);
}
If you want to use it in GET requests, then you should use overload which accepts JsonRequestBehavior flags as parameter and specify JsonRequestBehavior.AllowGet for that parameter.
You don't need to use dynamic, the simple way is to return object for anonymous type:
[HttpGet]
public object List() // Not List<Item>
{
var items = _db.Items.OrderBy(x => x.ID).Select(x => new
{
ID = x.ID,
Title = x.Title,
Price = x.Price,
Category = new {
ID = x.Category.ID,
Name = x.Category.Name
}
});
return items;
}
Or, return HttpResponseMessage:
[HttpGet]
public HttpResponseMessage List() // Not List<Item>
{
var items = _db.Items.OrderBy(x => x.ID).Select(x => new
{
ID = x.ID,
Title = x.Title,
Price = x.Price,
Category = new {
ID = x.Category.ID,
Name = x.Category.Name
}
});
return Request.CreateResponse(HttpStatusCode.OK, items);
}