B"H
Is there a way to return field names when using Tuples as return types for actions?
What I would like to do is skip creating DTOs for every single function in every single controller. When I have a complex system with many controllers each with many actions (functions). I often find that there are a handful of central DTOs. Then there are hundreds of slight variations of them. One for each function. I would like to stick with the handful of central classes and skip the extra classes. Replacing them with Tuples.
For example. I have a Customer class
public class Customer
{
public string Name{ get; set; }
public string Email{ get; set; }
}
with a many to many relation to store locations
public class StoreLocation
{
public string Name{ get; set; }
public string City{ get; set; }
public string State{ get; set; }
public string Focus{ get; set; }
}
I then have a function in a controller
[HttpGet("TopCustomersByState")]
public IEnumerable<(Customer customer, string state )> TopCustomersByState()
{
}
and a function
[HttpGet("TopCustomersByFocus")]
public IEnumerable<(Customer customer, string focus)> TopCustomersByState()
{
}
and a function
[HttpGet("CustomersAndTotalMoneySpent")]
public IEnumerable<(Customer customer, float moneySpent)> CustomersAndTotalMoneySpent()
{
}
These function are all accessed from Javascript in the browser expecting JSON.
Until now, I'd make a separate class for each return type. This quickly gets out of hand.The solution that I present above in my examples if it were to work would be perfect.The issue is that the JSON being returned is
{
"item1": {
},
"item2": null
}
instead of the property names customer, moneySpent, etc. as you'd expect.
Thank you
You don't need to put any return type of data at all if you want to make it generic. In any case it is converted to a json string. You can use an anonymous class for example
[HttpGet("CustomersAndTotalMoneySpent")]
public IActionResult CustomersAndTotalMoneySpent()
{
... your code
return Ok (new { Focus = focus, Customer = customer } );
//or
return Ok (new List<object> { { Focus = focus, Customer = customer} });
//or List<dynamic>
}
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'm listening to "push" notifications coming into my server. I've set up SubscriptionModel with all possible properties, and I can correctly iterate through the JSON body coming through, parse each Subscription, and modify the output before returning the list I created. However, I'd like to know how I might go about removing properties of SubscriptionModel when I don't need to return them at all; or removing them if they're null before responding back with List<SubscriptionModel> subscriptions.
namespace TextMessagingListener.Controllers
{
public class SubscriptionModel
{
public long push_id { get; set; }
public string request_id { get; set; }
public string subscription_id { get; set; }
public string message { get; set; }
public string status_code { get; set; }
public string error_message { get; set; }
}
[Route("api/[controller]")]
public class SubscriptionController : Controller
{
// PUT api/subscription
[HttpPut]
public List<SubscriptionModel> Put([FromBody] List<SubscriptionModel> model)
{
// Receive a report of whether your subscription(s) was successfully added or not.
List<SubscriptionModel> subscriptions = new List<SubscriptionModel>();
foreach (SubscriptionModel m in model)
{
m.message = "Push notification successfully received.";
subscriptions.Add(m);
}
return subscriptions;
}
}
}
The only solution I can think of is to create another object which will just be for returning information; and applying each subscriptions item I want to send on to that.
You can't. You'd need another class. A "light" version that contains just the properties. Or you could do an anonymous type, but that is difficult to work with. I agree with the other guy on your naming conventions though :).
I have a similar structure to the one below
Base class
public class BaseClass
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
public Guid Guid { get; set; }
public string Hometown { get; set; }
}
Derived Class
public class DerivedClass : BaseClass
{
public List<DerivedClassDataItem> Data { get; set; }
}
Data class
public class DerivedClassDataItem
{
public string Datum1 { get; set; }
public string Datum2 { get; set; }
public string Datum3 { get; set; }
public string Datum4 { get; set; }
public int Datum5 { get; set; }
public DateTime Datum6 { get; set; }
}
What is the best practice to return specific set of info from the DerivedClass?
a potential set could be:
Name, Address, Guid and then a Data list that only contains Datum1 and Datum4
I could see anonymousTypes, Tuples or another set of class(es), all to be valid approaches.
My concern about creating new set of classs for the set returned is that the class(s) structure will be similar to the structure of the three mentioned above except it will have fewer selected members, which to me, does not sound ideal. (duplicate code and structure)
Using anonymousTypes was my initial solution to tackle this, something like
List<DerivedClass> list = new List<DerivedClass>();
var mySet = list.Select(d => new
{
Name = d.Name,
Address = d.Address,
.
.
.
.
.
Data = d.Data.Select(item => new
{
Datum1 = item.Datum1,
Datum4 = item.Datum4
})
});
but again, that was a headache for us to track through httpResponse and through out API calls.
Should I go with Tuple?
Any insights as to what is the best practice for doing this?
Edit
I am using this set of data to be a response returned by a API/GET call. I will send the set back using HttpRespose and then the framework will transform that into json
this is an actual method we have now
private void populateReturnFile()
{
var returnFileAnonymous = new
{
Vendor = this.Vendor,
OrganizationName = this.OrganizationName,
User = this.User,
Platform = this.Platform,
DictionaryType = this.DictionaryType,
UseCaseId = this.UseCaseId,
Data = this.Data.Select(d => new
{
MigrationTermId = d.MigrationTermId,
ImoLexicalCode = d.ImoLexicalCode
})
};
this.returnFile = returnFileAnonymous;
}
Then my GET will return the retunFile (this is a very simple method, i have remove irrelevant code)
[HttpGet]
public HttpResponseMessage Get(Guid migrationFileId)
{
ProblemList problemList = ProblemList.GetProblemList(migrationFileId);
return Request.CreateResponse(HttpStatusCode.OK, problemList.ReturnFile, new JsonMediaTypeFormatter());
}
If API calls is where you are using these classes, then I personally like to keep it simple and avoid complex inheritance hierarchy. Remember, simple code is good code.
I would make a separate class for each api request/response call. For very simple api calls (ajax requests for example) I like to use anonymous types, but for controllers that only handle API calls I like to create separate classes, organized in a nice folder structure.
Everyone has their "style" but as long as you strive for simplicity your code will be maintainable.
What are good strategies for rebuilding/enriching a nested or complex ViewModel?
A common way to rebuild a flat ViewModel is shown here
But building and rebuilding a nested ViewModel using that method is too complex.
Models
public class PersonInfo
{
public int Id { get; set; }
public string Name { get; set; }
public int Nationality { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public int AddressTypeID { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
}
public class AddressType
{
public int Id { get; set; }
public string Description { get; set; }
}
view models
public class PersonEditModel
{
public int Id { get; set; }
public string Name { get; set; } //read-only
public int Nationality { get; set; }
public List<AddressEditModel> Addresses { get; set; }
public List<SelectListItem> NationalitySelectList { get; set; } //read-only
}
public class AddressEditModel
{
public int AddressTypeId { get; set; }
public string AddressDescription { get; set; } //read-only
public string Country { get; set; }
public string PostalCode { get; set; }
public List<SelectListItem> CountrySelectList { get; set; } //read-only
}
actions
public ActionResult Update(int id)
{
var addressTypes = service.GetAddressTypes();
var person = service.GetPerson(id);
var personEditModel= Map<PersonEditModel>.From(person);
foreach(var addressType in addressTypes)
{
var address = person.Addresses.SingleOrDefault(i => i.AddressTypeId == addressType.Id)
if(address == null)
{
personEditModel.Addresses.Add(new AddressEditModel
{
AddressTypeId = addressType.Id
});
}
else
{
personEditModel.Addresses.Add(Map<AddressEditModel>.From(address));
}
}
EnrichViewModel(personEditModel, person, addressTypes); //populate read-only data such as SelectList
return Index(personEditModel);
}
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var person = service.GetPerson(editModel.Id);
var addressTypes = service.GetAddressTypes();
EnrichViewModel(editModel, person, addressTypes);
return View(editModel);
}
service.Save(...);
return RedirectToAction("Index");
}
//populate read-only data such as SelectList
private void EnrichViewModel(PersonEditModel personEditModel, Person person, IEnumerable<AddressType> addressTypes)
{
personEditModel.Name = person.Name;
personEditModel.NationalitySelectList = GetNationalitySelectList();
foreach(var addressEditModel in personEditModel.Addresses)
{
addressEditModel.Description = addressTypes.Where(i => i.Id = addressEditModel.AddressTypeId).Select(i => i.Description).FirstOrDefault();
addressEditModel.CountrySelectListItems = GetCountrySelectList(addressEditModel.AddressTypeId);
}
}
My code for building and rebuilding the ViewModels (PersonEditModel and AddressEditModel) is too ugly. How do I restructure my code to clean this mess?
One easy way is to always build a new view model instead of merging/rebuilding since MVC will overwrite the fields with the values in ModelState anyway
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var newEditModel = BuildPersonEditModel(editModel.Id);
return View(newEditModel);
}
but I'm not sure that this is a good idea. Is it? Are there other solutions besides AJAX?
I'm going to tackle your specific pain points one-by-one and I'll try to present my own experience and likely solutions along the way. I'm afraid there is no best answer here. You just have to pick the lesser of the evils.
Rebuilding Dropdownlists
They are a bitch! There is no escaping rebuilding them when you re-render the page. While HTML Forms are good at remembering the selected index (and they will happily restore it for you), you have to rebuild them. If you don't want to rebuild them, switch to Ajax.
Rebuilding Rest of View Model (even nested)
HTML forms are good at rebuilding the whole model for you, as long as you stick to inputs and hidden fields and other form elements (selects, textarea, etc).
There is no avoiding posting back the data if you don't want to rebuild them, but in this case you need to ask yourself - which one is more efficient - posting back few extra bytes or making another query to fetch the missing pieces?
If you don't want to post back the readonly fields, but still want the model binder to work, you can exclude the properties via [Bind(Exclude="Name,SomeOtherProperty")] on the view model class. In this case, you probably need to set them again before sending them back to browser.
// excluding specific props. note that you can also "Include" instead of "Exclude".
[Bind(Exclude="Name,NationalitySelectList")]
public class PersonEditModel
{
...
If you exclude those properties, you don't have to resort to hidden fields and posting them back - as the model binder will simply ignore them and you still will get the values you need populated back.
Personally, I use Edit Models which contain just post-able data instead of Bind magic. Apart from avoiding magic string like you need with Bind, they give me the benefits of strong typing and a clearer intent. I use my own mapper classes to do the mapping but you can use something like Automapper to manage the mapping for you as well.
Another idea may be to cache the initial ViewModel in Session till a successful POST is made. That way, you do not have to rebuild it from grounds up. You just merge the initial one with the submitted one in case of validation errors.
I fight these same battles every time I work with Forms and finally, I've started to just suck it up and go fully AJAX for anything that's not a simple name-value collection type form. Besides being headache free, it also leads to better UX.
P.S. The link you posted is essentially doing the same thing that you're doing - just that its using a mapper framework to map properties between domain and view model.
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 };
}