Removing properties of an object in List<> - c#

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 :).

Related

Using Tuple field names in JSON responses

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>
}

Custom error objects for .Net Core 3 web api

I am currently developing a web api in .NET Core 3. I currently have the following model for my error response object:
public class ErrorRo
{
public string Message { get; set; }
public int StatusCode { get; set; }
public string Endpoint { get; set; }
public string Parameters { get; set; }
public string IpAddress { get; set; }
}
This is a mandated response I need to implement, management has pushed this. It allows more verbose error messages for people hitting our API so that they know what went wrong.
At the moment I am currently populating this object manually in the methods themselves. Is there a way where I can overwrite the response methods. I.e. can I override the BadRequest of IActionResult to automatically populate these fields?
Thanks!
You can use result filters for this purpose. Add a filter which repalces result before sending it back
Model
public class CustomErroModel
{
public string Message { get; set; }
public int StatusCode { get; set; }
public string Endpoint { get; set; }
public string Parameters { get; set; }
public string IpAddress { get; set; }
}
Filter
public class BadRequestCustomErrorFilterAttribute : ResultFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
//todo: check for BadRequestObjectResult if anything is returned for bad request
if (context.Result is BadRequestResult)
{
var result = new CustomErroModel
{
StatusCode = 200, //you status code
Endpoint = context.HttpContext.Request.GetDisplayUrl(),
Message = "some message",
IpAddress = context.HttpContext.Connection.RemoteIpAddress.ToString(), //find better implementation in case of proxy
//this returns only parameters that controller expects but not those are not defined in model
Parameters = string.Join(", ", context.ModelState.Select(v => $"{v.Key}={v.Value.AttemptedValue}"))
};
context.Result = new OkObjectResult(result); // or any other ObjectResult
}
}
}
Then apply filter per action or globally
[BadRequestCustomErrorFilter]
public IActionResult SomeAction(SomeModel model)
or
services
.AddMvc(options =>
{
options.Filters.Add<BadRequestCustomErrorFilterAttribute>();
//...
}
Well it depends on the scenario, but one possible approach could be to use a middleware using a similar strategy like the one described in this question, so that you complete the response with extra information.

How do you construct an Entity class to fetch another Entity from repository?

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

Rebuilding a nested ViewModel on !ModelState.IsValid

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.

Removing an element from an array in MongoDB

I am new to learning how to use MongoDB and am stuck pretty early on, so hoping someone can help me out with a simple example.
I can successfully connect to a Mongo server and create a collection and create objects to put in it. I am doing all of this through c# and the c# driver.
I have the following custom objects defined in my code.
public class Feature
{
public string FeatureName { get; set; }
public ObjectId Id { get; set; }
public List<Scenario> Scenarios { get; set; }
}
public class Scenario
{
private string _notes;
public string ScenarioName { get; set; }
public List<string> Givens { get; set; }
public List<string> Whens { get; set; }
public List<string> Thens { get; set; }
public string Explanation { get; set; }
public string Notes { get; set; }
}
As you can see, the Feature object contains a property which is a list of scenarios.
In my Mongo collection I have a Feature object that contains 3 scenarios. What I want to do in C# is write a method that can remove a specific scenario from a feature:
User provides a feature and scenario name to a method
Method checks the feature, looking through the list within it, for a scenario where the scenario.scenarioname matches the scenario name passed in to the method
If it exists, then remove the scenario from the feature and update Mongo and return a bool of true. If it doesn't exist return false
I am sure that this is probably going to be obvious when I see an example, but I have tried and get stuck trying to work through the List property looking for a property on a sub object.
I hope that all makes sense??!?
Thanks in advance.
P
Resolved it myself...
public bool DeleteScenario(string featureName, string scenarioName)
{
var collection = GetCollection<Feature>();
var query = Query.EQ("FeatureName", featureName);
var resultingFeature = collection.Find(query).SetLimit(1).FirstOrDefault();
if (resultingFeature == null)
{
return false;
}
// we have found our feature and it exists.
foreach (var scenario in resultingFeature.Scenarios)
{
if (scenario.ScenarioName == scenarioName)
{
resultingFeature.Scenarios.Remove(scenario);
collection.Save(resultingFeature);
return true;
}
}
return true;
}

Categories