still pretty new to ASP.NET. I have two models, and two API controllers. I was hoping I could create all the methods for both controllers in my EFRepository, but it seems to only let me use objects from the first model I built. My question is, do I just create another repository for the second model/API? Or is it best practice to create a generic and use server-side services for my methods? Or is there something else I am missing in my thought process? Thanks!
Record Model
namespace Train.Models {
public class Record {
public int Id { get; set; }
public int Quantity { get; set; }
public DateTime DateCreated { get; set; }
public bool IsActive { get; set; }
public ApplicationUser User { get; set; }
public virtual ICollection<Cars> Cars { get; set; }
}
}
Cars Model
namespace Train.Models {
public class Cars {
public int Id { get; set; }
public string EmptyOrLoaded { get; set; }
public string CarType { get; set; }
//Hopper, flatbed, tank, gondola, etc.
public string ShippedBy { get; set; }
//UP(Union Pacific) or BNSF
public string RailcarNumber { get; set; }
//public virtual ApplicationUser ApplicationUser { get; set; }
public string UserId { get; set; }
public string RecordId { get; set; }
public virtual Record Record { get; set; }
}
}
API CarsController Method
public IHttpActionResult Post(Cars car) {
car.UserId = User.Identity.GetUserId();
if (!ModelState.IsValid) {
return BadRequest(this.ModelState);
}
_repo.SaveCar(car);
return Created("", car);
}
API Recordcontroler Method
public IHttpActionResult PostRecord(Record record) {
var UserId = this.User.Identity.GetUserId();
if (!ModelState.IsValid) {
return BadRequest(this.ModelState);
}
_repo.SaveRecord(record);
return Created("", record);
}
Save Car Method in EFRepository
public void SaveCar(Cars carToSave) {
if (carToSave.Id == 0) {
_db.Cars.Add(carToSave);
_db.SaveChanges();
} else {
var original = this.Find(carToSave.Id);
original.EmptyOrLoaded = carToSave.EmptyOrLoaded;
original.CarType = carToSave.CarType;
original.ShippedBy = carToSave.ShippedBy;
original.RailcarNumber = carToSave.RailcarNumber;
_db.SaveChanges();
}
Related
I am started using automapper in my simple project. I want to exactly following 'one-to-many-to-many' mapping :-
public class AppUser
{
public int Id { get; set; }
public string UniversityName { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Description { get; set; }
public int AppUserId { get; set; }
public AppUser AppUser { get; set; }
public ICollection<Postedpic> Postedpics { get; set; }
}
public class Postedpic
{
public int Id { get; set; }
public string Url { get; set; }
public string PublicId { get; set; }
public Post Post { get; set; }
public int PostId { get; set; }
}
Destination:
public class MemberDto
{
public int Id { get; set; }
public string UniversityName { get; set; }
public ICollection<PostDto> Posts { get; set; }
}
public class PostDto
{
public int Id { get; set; }
public string Description { get; set; }
public ICollection<PostedpicDto> Postedpics { get; set; }
}
public class PostedpicDto
{
public int Id { get; set; }
public string Url { get; set; }
public bool IsMain { get; set; }
}
For proceed with automapper I tried this:-
CreateMap<AppUser, MemberDto>()
.ForMember(dest=>dest.Posts,opt=>opt.MapFrom(src=>src.Posts.Select(x=>x.Description)))
.ForMember(dest=>dest.Posts,opt=>opt.MapFrom(src=>src.Posts.Include(x=>x.Postedpic).Select(x=>x.Postedpic.Url)))
.ForMember(dest=>dest.Posts,opt=>opt.MapFrom(src=>src.Posts.Include(x => x.Postedpic).Select(x=>x.Postedpic.PublicId)));
But I found this error:-
Also I don't understand my approach is right or wrong for proceeding automapper.
You have two issues here:
For the Include method, you need to import the right namespace:
using System.Data.Entity;
You then tell AutoMapper how to do its job. It's smart enough to map properties by name, so you don't need to do much here. You just need to tell it which types to map to:
CreateMap<Post, PostDto>();
CreateMap<PostedPic, PostedPicDto>();
CreateMap<AppUser, MemberDto>();
As for the context, it looks to me like you are using lazy loading and want to make sure the posts and pics are loaded. For this to work, you need to configure the original query with Include, prior to mapping. For instance:
var user = SomeContext.AppUsers
.Include(u => u.Posts)
.Include(u => u.Posts.Select(p => p.PostedPics))
.FirstOrDefault(u => u.Id == someId);
var userDto = mapper.Map<AppUser, MemberDto>(user);
Dont use automapper, or any other mapping framework. This is code smell.
Create an interface like:
public interface IMemberDtoCreator {
int GetId();
string GetUniversityName();
ICollection<PostDto> GetPosts();
}
public class MemberDto {
public int Id { get; set; }
public string UniversityName { get; set; }
public ICollection<PostDto> Posts { get; set; }
public MemberDto(IMemberDtoCreator creator) {
Id = creator.GetId();
UniversityName = creator.GetUniversityName();
Posts = creator.GetPosts();
}
}
public class AppUser : IMemberDtoCreator {
public int Id { get; set; }
public string UniversityName { get; set; }
public ICollection<Post> Posts { get; set; }
public int GetId() {
return Id;
}
public string GetUnivsersityName() {
return UniversityName;
}
public ICollection<Post> GetPosts(){
ICollection<PostDto> result = new ICollection<PostDto>();
foreach(Post post in Posts){
result.add(new PostDto(post)) // And repeat the interface implementation for PostDto and post, etc.
}
}
}
This is pseudo code, and I didn't check for compiling, but you get the idea, I am sure. This is better in every way imaginable.
(If you are worried about overwriting the implementation of your interfaces, simply write the implementations in a partial class. Then you can autogenerate the original "base" class forever, and never interrupt your interface implementations.
I have this classes in Code First asp.net
public class Account
{
[Key]
public int Id { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string AccountTitle { get; set; }
public Classification Classification { get; set; }
}
public class Classification
{
[Key]
public int Id { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string TitleClassification { get; set; }
public ICollection<Account> Accounts { get; set; }
}
public class ClassificationDto
{
public int Id { get; set; }
public string TitleClassification { get; set; }
}
In my Db Context
public DbSet<Account> Accounts { get; set; }
AccountingManager.FindAll is this
public IQueryable<T> FindAll()
{
return context.Set<T>().AsNoTracking().AsQueryable();
}
I am trying to get just the "Classification" which is just 3 but I am getting the "Account" that is associated with it too with this code:
[HttpGet]
[Route("get-classification")]
[Authorize]
public async Task<IActionResult> GetAccountClassification()
{
List<ClassificationDto> classificationList = new List<ClassificationDto>();
var accountingManager = new AccountingManager(context);
var list = accountingManager.FindAll();
classificationList = await list.Select(s => new ClassificationDto
{
Id = s.Classification.Id,
TitleClassification = s.Classification.TitleClassification,
}).ToListAsync();
return StatusCode(StatusCodes.Status200OK, classificationList);
}
This is how it is in my table
If you want to get all rows from Classification table,
add public DbSet<Classification> Classifications { get; set;} property to your DbContext class. And then implement similar to FindAll() method:
return context.Classifications.AsNoTracking().ToList()
Sql Tables Here
public partial class Users
{
public Users()
{
UsersRelationFollower = new HashSet<UsersRelation>();
UsersRelationFollowing = new HashSet<UsersRelation>();
Vote = new HashSet<Vote>();
VoteRating = new HashSet<VoteRating>();
}
public string Id { get; set; }
public string UserType { get; set; }
public string UserName { get; set; }
public string Mail { get; set; }
public string ImageUrl { get; set; }
public DateTime CreationDate { get; set; }
public DateTime? ModifyDate { get; set; }
public bool State { get; set; }
public virtual UserPasswords UserPasswords { get; set; }
public virtual CorporateProperty CorporateProperty { get; set; }
public virtual UserProperty UserProperty { get; set; }
public virtual ICollection<UsersRelation> UsersRelationFollower { get; set; }
public virtual ICollection<UsersRelation> UsersRelationFollowing { get; set; }
public virtual ICollection<Vote> Vote { get; set; }
public virtual ICollection<VoteRating> VoteRating { get; set; }
}
public partial class UserProperty
{
public string Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public DateTime BirthDay { get; set; }
public string Gender { get; set; }
public string Locale { get; set; }
public string PhoneNumber { get; set; }
public bool State { get; set; }
public virtual Users IdNavigation { get; set; }
}
public partial class CorporateProperty
{
public string Id { get; set; }
public string OrganisationName { get; set; }
public string Website { get; set; }
public bool State { get; set; }
public virtual Users IdNavigation { get; set; }
}
UserControllerClass
// GET: api/Users/5
[HttpGet("{id}")]
public async Task<IActionResult> GetUsers([FromRoute] string id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var users = await _context.Users.SingleOrDefaultAsync(m => m.Id == id);
if (users == null)
{
return NotFound();
}
return Ok(users);
}
My problem is exactly this; User information is coming but the password and property table information is not coming.
How to modify the following line solves my problem?
var users = await _context.Users.SingleOrDefaultAsync(m => m.Id == id);
based on your code this would also hydrate your CorporateProperty & UseProperty objects, etc.
var user = await _context.Users.Include(user => user.UserProperty).Include
(user => user.CorporateProperty).SingleOrDefaultAsync(user => user.Id == id);
lazy loading doesn't exist yet so you have Eager Loading to play with for now.
Surprised you didn't roll with Identity since this all of this would have been taken care for you especially Passwords... Hope you aren't rolling your own hash for that..
Just add in the custom class / collection objects you need.
you can also check out this link
https://learn.microsoft.com/en-us/ef/core/querying/related-data
this is how the controller looks like
public ActionResult Edit()
{
return View();
}
[HttpPost]
public ActionResult Edit(Car _car)
{
if (ModelState.IsValid)
{
entities.Entry(_car).State = EntityState.Modified;
entities.SaveChanges();
RedirectToAction("Stock");
}
return View(_car);
}
the page itself is pretty much the "Edit" template with just a different redirect at the bottom.
this is the Car class
public partial class Car
{
public Car()
{
this.Orders = new HashSet<Order>();
}
public int CarID { get; set; }
public string Manufacturer { get; set; }
public string Model { get; set; }
public decimal Daily_Cost { get; set; }
public decimal Late_Fee { get; set; }
public int Year { get; set; }
public string Gear { get; set; }
public int Mileage { get; set; }
public bool Available { get; set; }
public string Registration { get; set; }
public int Location { get; set; }
public string Picture { get; set; }
public virtual Branch Branch { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
I'm not exactly sure what's causing the ModelState to be invalid but when I look at the object at runtime the only thing I see that I think is wrong, is Branch is null.
Branch is in a relationship in the db with Location. Location is foreign key and Branch is primary key (in the DB it's BranchID but I don't think that's relevant)
Trying to figure out checkboxlists to implement in a few areas of our system. We have a table, tblJobs. This has a "AllocatedTo" property, which links to the UserID of "tblGlobalUsers". What we want is to be able to select multiple users, iterate through and insert a database record for each user selected to allocate the job to them.
I've tried following some other questions people asked on here, such as Asp.Net MVC4 Display CheckboxList and some other guides, but just getting nowhere with it unfortunately.
Will provide the code that anyone wants, or more information if needed.
Many thanks
Edit:
Have followed the link Stephen provided and attempted to update my viewmodel and controller as required. My viewmodel is now:
public class JobCreateViewModel
{
public string JobTitle { get; set; }
public string Description { get; set; }
public int InventoryID { get; set; }
public int ReportedBy { get; set; }
public int Priority { get; set; }
public int JobType { get; set; }
public int UserID { get; set; }
public string NetworkLogin { get; set; }
public bool IsSelected { get; set; }
public virtual tblGlobalUser GlobalUserAllocated { get; set; }
public virtual tblGlobalUser GlobalUserReportedBy { get; set; }
public virtual tblInventory InventoryHardware { get; set; }
public virtual tblJobType TypeJob { get; set; }
public virtual tblJobPriority JobsPriority { get; set; }
}
public class JobsAllocateViewModel
{
public JobsAllocateViewModel()
{
AllocatedTo = new List<JobCreateViewModel>();
}
public int UserID { get; set; }
public string NetworkLogin { get; set; }
public List<JobCreateViewModel> AllocatedTo { get; set; }
}
And the controller is:
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(JobCreateViewModel viewModel)
{
JobsAllocateViewModel model = new JobsAllocateViewModel();
if(ModelState.IsValid)
{
JobsContext.tblJobs.Add(new tblJob
{
JobTitle = viewModel.JobTitle,
JobDescription = viewModel.Description,
InventoryID = viewModel.InventoryHardware.InventoryID,
ReportedBy = viewModel.GlobalUserReportedBy.UserID,
AllocatedTo = model.AllocatedTo,
Priority = viewModel.JobsPriority.PriorityID,
JobType = viewModel.TypeJob.TypeID
});
JobsContext.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel);
}
Getting an error of "Cannot convert type systems.collections.generic.list to int" on AllocatedTo = model.AllocatedTo,