Getting different result from an mvc action method - c#

I have a business layer that has some IEnumerable methods. I used those methods in my controller and get result after make methods result to .ToList().
Now there is a new requirement. I have to filter action result data per user access. When users call an action method, only one business layer method called, but I need different result per user. For example: user A can load all data but user B only access 10 rows data according by his permission.
Code:
BL:
public IEnumerable<Card> GetCardList(int Id)
{
return unitOfWork.GetCardList(Id);
}
Controller:
//...
CardBl.GetCardList(id).ToList();
//...
I tried to filter data in a CustomActionFilter class by overriding OnActionExecuting or OnResultExecuting methods, but I'm confused!
Actually I want to make this kind of filtering automate by attributes instead of adding where clause for every method.
I would appreciate if anyone suggest a solution.
Update:
My Model:
public class CardModels
{
public int Id { get; set; }
public string CardName { get; set; }
public string CardDescription { get; set; }
public int CityId { get; set; }
}
CityId is the property that I have to filter data based on.

Related

Controller accepting different models

I want to accept different model type from body based on query param value.
Example:
[HttpGet]
[Route("GetSystemdetails")]
public string Getdeatils([FromBody] SystemDetail sysdetails, string type)
{
//some code here
string details = getdetails(sysdetails);
}
// abc model
public class abc
{
public int UserID { get; set; }
public string Email { get; set; }
}
//xyz model
public class xyz
{
public int xyzid { get; set; }
public string systemval { get; set; }
public string snum { get; set; }
}
type abc and xyz will have it's own model. So based on type I receive in query param I wanted to pick the model and proceed.
Sample url:
localhost/GetSystemdetails/type=abc
localhost/GetSystemdetails/type=xyz
I thought of creating a new model SystemDetail which holds these two models(xyz and abc) and based on system pick them.
I wanted to know what are possible ways to achieve this kind of requirements without creating multiple methods in controller(I don't want to change the format of the URL).
That's not something that's supported out of the box. Your linked solution is probably the closest you'll get to that.
ASP.NET Core is not supposed to take values of the parameters into account when routing, except for validation.
There are several possible ways to do so
Having multiple model objects
As in the link you provided, you can declare multiple model objects. The site has given the example of
public class PostUserGCM
{
public User User { get; set; }
public GCM GCM { get; set; }
}
but you can use your own examples.
Base model
Your models can inherit from some base model. If you only need a single model at a time and they share some similarities, then you could just create a base model which the other two are inheriting from, be agnostic at implementation time and your use cases will mainly differ on instantiation inside the controller, while some service methods could handle other differences.

Entity Framework include only returning part of the database data

I am very new to asp.net and C# so bear with me. I am trying to return data from a database using the entity framework .include() method so that I can get the foreign key information from another table. However, what is being returned is only part of the data. It seems to be cut off before everything is returned.
"[{"id":11,"name":"Mr. Not-so-Nice","heroType":3,"heroTypeNavigation":{"id":3,"type":"Villian","heroes":["
Which gives me the error: SyntaxError: Unexpected end of JSON input.
Please seem below for the model classes and the GET section of the controller where this is being returned. If I remove the "include()" method it returns all the heroes from the main table just fine.
public partial class Hero
{
public int Id { get; set; }
public string Name { get; set; }
public int? HeroType { get; set; }
public virtual HeroTypes HeroTypeNavigation { get; set; }
}
{
public partial class HeroTypes
{
public HeroTypes()
{
Heroes = new HashSet<Hero>();
}
public int Id { get; set; }
public string Type { get; set; }
public virtual ICollection<Hero> Heroes { get; set; }
}
// GET: api/Heroes
[HttpGet]
public async Task<ActionResult<IEnumerable<Hero>>> GetHeroesTable()
{
return await _context.HeroesTable.Include(hero => hero.HeroTypeNavigation).ToListAsync();
}
Serializer recursion rules will be tripping this up. Basically as jonsca mentions, you have a circular reference between hero, and hero type. The serializer will start with the hero, then go to serialize the hero type which it will find the Hero's collection and expect to serialize, which each would reference a hero type, with collections of Heros.. The serializer bails when it sees this.
I would recommend avoiding passing back Entity classes to your view to avoid issues with EF and lazy loading. Serialization will iterate over properties, and this will trigger lazy loads. To avoid this, construct a view model for the details your view needs, flatten as necessary.
For example if you want to display a list of Heroes with their Type:
public class HeroViewModel
{
public int HeroId { get; set; }
public string Name { get; set; }
public string HeroType { get; set; }
}
to load:
var heroes = await _context.HeroesTable.Select(x => new HeroViewModel
{
HeroId = x.HeroId,
Name = x.Name,
HeroType = x.HeroType.Type
}).ToListAsync();
You can utilize Automapper for example to help translate entities to view models without that explicit code using ProjectTo<TEntity> which can work with EF's IQueryable implementation.
With larger realistic domains your client likely won't need everything in the object graph.
You won't expose more information than you need to. (I.e. visible via debugging tools)
You'll get a performance boost from not loading the entire graph or triggering
lazy load calls, and it's less data across the wire.
The last point is a rather important one as with complex object graphs, SQL can do a lot of the lifting resulting in a much more efficient query than loading "everything". Lazy hits to the database can easily add several seconds to each and every call from a client, and loading large graphs has a memory implication on the servers as well.

Best/easiest way to save data+relational data in Entity Framework

Consider this simple one to many relationship in Entity Framework. One organisation holds many products.
public class Product
{
public int Id { get; set; }
[StringLength(20)]
public string Title { get; set; }
[StringLength(300)]
public string Description { get; set; }
public float Price { get; set; }
public DateTime CreationDate { get; set; }
public virtual Organisation Organisation { get; set; }
public Product()
{
CreationDate = DateTime.Now;
}
}
public class Organisation
{
public int Id { get; set; }
[StringLength(20)]
public string Title { get; set; }
[StringLength(400)]
public string Description { get; set; }
public DateTime CreationDate { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
If I have a (post) api call, accepting raw data in the body (json formatted text), which allows the user to create an organisation and as many products as they want, in the same call. How do I correctly save these data into the database using Entity Framework?
As far as I know I am using lazy loading (due to virtual keywords when setting relations in the models), so shouldn't it handle relations automatically? If I save the data like in the controller action below (the controller action for the post call). Will it work? And if it doesn't, what is the appropriate/best practice method for saving a model that contains a list of another model using Entity Framework.
public IHttpActionResult CreateOrganisation(Organisation org)
{
db.Organisations.Add(org);
db.SaveChanges();
return Ok(org);
}
Really can't seem to find this in the docs?
Well, i didn´t have time to try it, but looking at your code i see that there are some suggestions that i may point to you if you are looking for the best practices even if these don´t anwser your question:
Make your Web API Controller using the Async Task pattern to avoid dead locks:
public async Task<IHttpActionResult> CreateOrganisation(Organisation org)
Make the caller method, the business logic of your ef, as the same pattern above. In this case use the SaveChangesAsync() method. Don´t forget to use also the keyword await when calling any method as the example above within your controller.
Make the use of the separation of concerns. Apply the BL out of your Controller class.
Apply the SOLID principle. Dependency of inversion, letter D, for example using the Microsfot Unity IOC container, make the classes loosely coupled using abstract classes, interfaces, so after that apply the dependency of injection.
Try to use a View Model to valide the model instead of the domain class generated by the datafirst model or the code first model by the EF.
Last but not least, strongly typed!!!!
I hope these suggestions would help you.
Since you asked for "best practices", I'll tell you about a technique I had to work with back when EF's handling of object graphs was a little, shall we say "iffy"?
First, each physically stored entity has a corresponding DTO object. Part of the ViewModel (MVVM-speak) was the necessary DTO object(s) for that View. There were additional fields added to communicate State (what the UI did) for each entity. It looked something like this:
public class ProductDTO {
// Same fields, mostly
public string Action { get; set; } // A, C, D
}
public class OrganizationDTO {
// Same fields except children
public ICollection<ProductDTO> Products { get; set; }
public string Action { get; set; }
}
Based on non-blank Action in the top level DTO, POST, PUT or DELETE (A,C,D). UI sets the states of each entity. A modification to children counts as a "change" to the parent.
Then in my CRUD methods on the backend ... Admittedly, this is the sledgehammer approach.
public class OrganizationRepository : whatever interfaces {
public void Add (OrganizationDTO newOrg) {
if (newOrg.Action != "A") // Why are you here?
throw an exception (bad request)
context.Organizations.Add(Map DTO to entity here);
foreach (var item in newOrg.Products) {
switch (item.Action)
case "A":
ProductRepository.Add(item,newOrg.Id);
break;
case "C":
ProductRepository.Update(item,newOrg.Id);
break;
case "D":
ProductRepository.Delete(item);
break;
}
context.SaveChanges();
}
public void Update (OrganizationDTO oldOrg) {
if (newOrg.Action != "C") // Why are you here?
throw an exception (bad request)
context.Organizations.Attach(Map DTO to entity here);
foreach (var item in newOrg.Products) {
switch (item.Action)
case "A":
ProductRepository.Add(item,newOrg.Id);
break;
case "C":
ProductRepository.Update(item,newOrg.Id);
break;
case "D":
ProductRepository.Delete(item);
break;
}
context.SaveChanges();
return Ok();
}
}
This is not working/tested code. Just rough off-the-cuff. Have to handle FKs on adds. Add your mapping technique. Handle collisions, Try/Catch, etc.
I think your code is not well self-explaining.
If I'm a developer that looks at the method you posted here, I would simply not understand that i have the possibility to send nested-data.
I would make an iteration over the Products collection of the object "org" and add them to their EF collection, then late add the Organisation to its own.
Then, obviously, SaveChanges().
This would clearly distinguish this method that accept nested object, from another ipothetical that doesn't and simply saves my new empty Organisation.

Mapping a User entity into a User DTO in .NET Core

I'm developing a web app that contains a User entity that is derived from .NET Core's IdentityUser. Lets suppose there is another entity called Comment which has a relation to a user (the user who posted the comment):
public class User : IdentityUser
{
public string SomeExtraField { get; set; }
}
public class Comment
{
//Owner (Creator) of the feedback
public User User { get; set; }
//body of the comment
public string Body { get; set; }
}
Now suppose I have an API endpoint that returns all of the comments in the system. If I query for all comments and include the User relation, when the object gets serialized, everything in the User class is serialized and sent to the client (including the users hashed password, etc). Obviously I don't want this. So I've created a CommentService layer that grabs the Comments from a CommentRepository. From my understanding, the service layer should do the job of mapping the raw Comment object into a Comment DTO, which only contains data that should be sent to the client. I've defined a comment and user DTO like this:
public class UserOutput
{
public string Id { get; set; }
public string SomeExtraField { get; set; }
}
public class CommentOutput
{
public UserOutput User { get; set; }
public string Body { get; set; }
}
Then in my service layer I have something like the following:
//Fetch all comments
var list = await _repository.ListAsync();
//Map comments to DTO
var result = list.Select(x => new CommentOutput
{
Body = x.Body,
User = new UserOutput
{
Id = x.User.Id,
SomeExtraField = x.User.SomeExtraField,
}
});
This all seems to work great. However I can foresee one problem. Lets say I have a large system with Comments, Posts, Likes, Private Messages, etc. I can map them all in a similar fashion above. Then one day I decide to add another field to the UserOutput DTO. Now I have to go through potentially hundreds of mapping code like the sample above to map the new field properly, and whats worse is the compiler wont tell me if I've missed anything. I would like to have a function somewhere that maps a User to a UserOutput but I don't know where it should go.
I've seen some suggestions to put a constructor to the DTO that does the mapping:
public class UserOutput
{
public UserOutput(User user)
{
Id = user.Id;
SomeExtraField = user.SomeExtraField
}
public string Id { get; set; }
public string SomeExtraField { get; set; }
}
but I've seen people against this because it tightly couples the DTO with the Entity. I've also seen suggestions of using Auto Mapper but is also seems an equal amount of people are against it.
Where should I place code that can perform these DTO->entity and entity->DTO mappings so I don't repeat myself all over the place?
Try to check out AutoMapper.
This library will help you to map the Entity Class into the ViewModel.
The way to use it is pretty straightforward.

MVC viewModel and paged results

I have a need to pass into a controller a viewmodel as per below.
[HttpPost]
public JsonResult GetSearchResultsJson(SearchCriteria searchCriteria)
{
}
SearchCriteria is defined as:
public class SearchCriteria
{
public SearchData searchData { get; set; }
public SearchMode searchMode { get; set; }
}
Where SearchMode is:
public class Searchmode
{
public int? mode { get; set; }
public int? pageNumber { get; set; }
public int? pageSize { get; set; }
}
And SearchData has 61 properties that define what items are to search for.
public class SearchData
{
public string name {get;set;}
....
public int age {get;set;}
}
I populate an object using jQuery and pass that to the controller. .Net converts this object into an object of type SearchCriteria. All is working, but when the PagedListPager control is rendered, how do i emulate the jQuery used to create the object?
At the moment I have the following code:
#Html.PagedListPager(Model.DocumentsPaged, pageNumber => Url.Action("GetSearchResultsJson", XXXXXXXXXXXXXXXXXXXXXXX),pLRO)
And do not know what to put in the bit marked as XXXXXXXXXXXXXXXXXXXXXXX.
Within jQuery, I can modify the pageNumber property of the SerachMode object and this does provide me with the correct page, but it is precisely this property that I need to update within the Html.PagedListPager helper.
As described in example here you can pass the page.
I really suggest you to clone the example code and play a bit with it.
Probably you have to add your search parameters as well, in case you lose them server-side.
Your method GetSearchResultsJson(SearchCriteria searchCriteria) is marked [HttpPost], so you can leave the URL parameters blank and just use Url.Action("GetSearchResultsJson"). The search parameters for a POST go in the body of the request instead of the URL.
Because the Html.PagedListPager method expects a 'page' parameter (assuming you are using the method from the PagedList.Mvc NuGet package), you may want to write your search function like this:
GetSearchResultsJson(int page, int SearchCriteria searchCriteria)

Categories