I'm coding an asp.net web api which will access data held on a mysql database. I am able to search for specific details through 'api/company/{company id number} which shows its working.
However what I'm wondering is whether there is a way to return a result based on {company name} rather than the primary key {company id}.
My reason for this is that the search query is more likely to be based on trying to find a company record by name rather than by company id.
Sorry if i sound like a noob, that's because I am.
I've tried changing (int id) to (string id) but that simply throws an error as its searching through the primary key field which is type int.
public class companyController : ApiController
{
private apollodbEF db = new apollodbEF();
// GET: api/company
public IQueryable<companydetail> Getcompanydetails()
{
return db.companydetails;
}
// GET: api/company/5
[ResponseType(typeof(companydetail))]
public IHttpActionResult Getcompanydetail(int id)
{
companydetail companydetail = db.companydetails.Find(id);
if (companydetail == null)
{
return NotFound();
}
return Ok(companydetail);
}
public class companyController : ApiController
{
private apollodbEF db = new apollodbEF();
// GET: api/company
public IQueryable<companydetail> Getcompanydetails()
{
return db.companydetails;
}
// GET: api/company/5
[ResponseType(typeof(companydetail))]
public IHttpActionResult Getcompanydetail(string id)
{
companydetail companydetail = db.companydetails.Find(id);
if (companydetail == null)
{
return NotFound();
}
return Ok(companydetail);
}
You can use a where statement
var company = db.companydetails.Where(c => c.Name == "My Company").FirstOrDefault();
Related
I have a problem saving the changes to Database Context. When i don't save i can see that the listing status is successfully changed , but when i try to save it I get an error which is saying : " There is already an open DataReader associated with this Connection which must be closed first." And i don't know where that comes from. When i try to do it asynchronous i get the same error.
AdministratorController.cs
[Route("/Admin")]
[ApiController]
public class AdministratorController : Controller
{
private readonly dbContext _dbContext;
public AdministratorController(dbContext dbContext)
{
_dbContext = dbContext;
}
///////////////////////////////////
/// //
/// Accept or Reject Listings //
/// //
//////////////////////////////////
[HttpPost]
[Route("acceptListing/{listingId}")]
[AllowAnonymous]
//[Authorize(Roles="Administrator")]
public ActionResult AcceptList([FromRoute]int listingId)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
if (listingId == null)
{
return NotFound("Listing not found.");
}
foreach (Listing listing in _dbContext.Listings)
{
Console.WriteLine(listing.Status);
if(listing.Id == listingId)
{
if(listing.Status == ListingStatus.Accepted)
{
return BadRequest("Listing already accepted.");
}
else
{
listing.Status = ListingStatus.Accepted;
Console.WriteLine(listing.Status);
_dbContext.SaveChanges();
}
return Ok();
}
}
return BadRequest("Couldn't find right listing.");
}
Rather than looping through all listings looking for the one with the Id you want, just filter and get.
Listing? listing = _dbContext.Listings.FirstOrDefault(l => l.Id == listingId);
if (listing is null)
{
return BadRequest("Couldn't find right listing.");
}
if(listing.Status == ListingStatus.Accepted)
{
return BadRequest("Listing already accepted.");
}
listing.Status = ListingStatus.Accepted;
Console.WriteLine(listing.Status);
_dbContext.SaveChanges();
return Ok();
The problem here is that you are iterating the data being fetched from database and in the same time you are trying to save something from the same context. Quick fix is to use ToList in foreach:
foreach (Listing listing in _dbContext.Listings.ToList())
{
// ..
}
But in general you should not fetch everything from the database to process only one item. Just write query that will filter everything on database side. Something along this lines (not tested):
var listing = _dbContext.Listings.FirstOrDefault(l => l.Id == listingId);
if (listing is null)
{
return NotFound();
}
if (listing.Status == ListingStatus.Accepted)
{
return BadRequest("Listing already accepted.");
}
else
{
listing.Status = ListingStatus.Accepted;
Console.WriteLine(listing.Status);
_dbContext.SaveChanges();
}
return Ok();
change the code like this :
var listings = _dbContext.Listings.Tolist();
foreach (Listing listing in listings)
{
Console.WriteLine(listing.Status);
if(listing.Id == listingId)
{
if(listing.Status == ListingStatus.Accepted)
{
return BadRequest("Listing already accepted.");
}
else
{
listing.Status = ListingStatus.Accepted;
Console.WriteLine(listing.Status);
_dbContext.Update(listing);
_dbContext.SaveChanges();
}
return Ok();
}
}
I want to be able to get the username of the logged in user in my C# ASP.net application so I can get the ID of that user. I then want to perform an edit so they can apply to be part of an appointment.
With my current code, I click the link and I just get a HTTP 400 error. I have debugged this and the ID is coming through as the correct value. Is there any reason that its not being attached to the url?
// GET:
public ActionResult VolunteerCeremony(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
string userName = string.Empty;
//if (System.Web.HttpContext.Current != null &&
// System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
//{
// System.Web.Security.MembershipUser usr = System.Web.Security.Membership.GetUser();
// if (usr != null)
// {
// userName = usr.UserName;
// }
//}
var getVolunteerId = (from u in db.Volunteers
where WebSecurity.CurrentUserName == u.Username
select u.VolunteerId).FirstOrDefault();
Volunteer volunteer = db.Volunteers
.Include(p => p.Appointments)
.Where(i => getVolunteerId == id)
.FirstOrDefault();
if (volunteer == null)
{
return HttpNotFound();
}
PopulateAssignedCeremonyData(volunteer);
return View(volunteer);
}
// POST: /Player/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult VolunteerCeremony(int? id, string[] selectedOptions)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var getVolunteerId = (from u in db.Volunteers
where WebSecurity.CurrentUserName == u.Username
select u.VolunteerId).FirstOrDefault();
var updateVolunteerWithCeremony = db.Volunteers
.Include(p => p.Appointments)
.Where(i => getVolunteerId == id)
.Single();
try
{
UpdateVolunteerCeremonies(selectedOptions, updateVolunteerWithCeremony);
db.Entry(updateVolunteerWithCeremony).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
PopulateAssignedCeremonyData(updateVolunteerWithCeremony);
return View(updateVolunteerWithCeremony);
}
EDIT This is where I'm calling the method
else if(Request.IsAuthenticated && HttpContext.Current.User.IsInRole("Volunteer"))
{
<li>#Html.ActionLink("Appointments", "Create", "Appointments")</li>
<li>#Html.ActionLink("Join Ceremony", "VolunteerCeremony", "Volunteers")</li>
}
EDIT 2 Here's my ViewModel that I used for the many to many relationship I have:
public class VolunteerCeremonyVM
{
public int AppointmentId { get; set; }
public string DetailsOfAppointment { get; set; }
public bool Assigned { get; set; }
public VolunteerCeremonyVM()
{
this.AppointmentId = AppointmentId;
this.DetailsOfAppointment = DetailsOfAppointment;
this.Assigned = Assigned;
}
}
Based on further clarification, you will want to update your ViewModel to include a VolunteerId. So it should look something like:
public class VolunteerCeremonyVM
{
public int AppointmentId { get; set; }
public string DetailsOfAppointment { get; set; }
public bool Assigned { get; set; }
public int VolunteerId { get; set; }
}
Notice that I removed the default constructor as it wasn't doing anything. When you construct the object you can use code like the following:
var model = new VolunteerCeremonyVM {
AppointmentId = 10,
VolunteerId = 50,
//etc
};
As this will set all of the properties after the basic object is constructed.
Then, you will want to pass this view model into the view that is generating your action links like so: return View(model); in whatever controller action you're using.
Inside the view, you will make use of this new information. Specifically, the Html.ActionLink method requires that any extra parameters are passed to it in a route dictionary. It uses this route dictionary to build the URL that is generated for the anchor tag. You can call Html.ActionLink with an anonymous object for its fourth parameter to specify the route dictionary. The id will be the name of the parameter and Model.VolunteerId will be the value of the current volunteer.
Change the Html.ActionLink call to the following to pass an id:
#Html.ActionLink("Join Ceremony",
"VolunteerCeremony",
"Volunteers",
new { id = Model.VolunteerId },
null)
This should fill in the id parameter of the ActionLink with the currently logged in user's userId which should generate the following link: /Volunteers/VolunteerCeremony/3 or whatever the ID actually is.
For the form side, you will also need a hidden field on the form so the correct id is sent along with the request. You will need to add a VolunteerId to the form ViewModel that you create when generating the form page. Once you've done that, inside the form itself, you will need to add the following:
Html.Hidden("id", Model.VolunteerId)
This creates an <input type="hidden" name="id" value="3" /> tag that will be sent with the form on post back to the server. Since the tag's name is id this will match with the id that's defined in your controller action.
I'm trying to update an entity and I've run into the following error:
InvalidOperationException: The instance of entity type 'BookLoan'
cannot be tracked because another instance of this type with the same
key is already being tracked. When adding new entities, for most key
types a unique temporary key value will be created if no key is set
(i.e. if the key property is assigned the default value for its type).
If you are explicitly setting key values for new entities, ensure they
do not collide with existing entities or temporary values generated
for other new entities. When attaching existing entities, ensure that
only one entity instance with a given key value is attached to the
context.
I've done a little research and from what I can tell I'm apparently trying to track an already tracked entity when I use _context.Update(bookloan); but I'm not really sure what to do.
What I'm trying to do is update an existing entity/record in my database. Here are the get and post controllers as I'm not sure what else to share.
Get
[HttpGet]
public async Task<IActionResult> Return(int? id)
{
if (id == null)
{
return NotFound();
}
if (isBookCheckedOut(id) == false)
{
//Not checked out
return RedirectToAction("Index");
}
else
{
var bookloan = (from book in _context.Books.Where(b => b.BookId == id)
join loan in _context.BookLoans.Where(x => !x.ReturnedOn.HasValue) on book.BookId equals loan.BookID into result
from loanWithDefault in result.DefaultIfEmpty()
select new BookReturnViewModel
{
BookLoanID = loanWithDefault.BookLoanID,
BookID = book.BookId,
Title = book.Title,
StudentID = loanWithDefault == null ? null : loanWithDefault.StudentID,
StudentFristName = loanWithDefault == null ? null : loanWithDefault.Student.FirstName,
StudentLastName = loanWithDefault == null ? null : loanWithDefault.Student.LastName,
//Fines
CheckedOutOn = loanWithDefault == null ? (DateTime?)null : loanWithDefault.CheckedOutOn,
IsAvailable = loanWithDefault == null,
AvailableOn = loanWithDefault == null ? (DateTime?)null : loanWithDefault.DueOn
}).FirstOrDefault();
if (bookloan == null)
{
return NotFound();
}
return View(bookloan);
}
}
Post:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Return(BookReturnViewModel model)
{
if (ModelState.IsValid && isBookCheckedOut(1) == true)
{
var bookloan = new BookLoan()
{
BookLoanID = model.BookLoanID,
BookID = model.BookID,
StudentID = model.StudentID,
CheckedOutOn = (DateTime)model.CheckedOutOn,
DueOn = (DateTime)model.AvailableOn,
ReturnedOn = DateTime.Now
};
try
{
_context.Update(bookloan);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
}
return RedirectToAction("Index");
}
else
{
}
return View();
}
Your context already includes the entity, so rather that creating a new one, get the existing one based on the ID of the entity and update its properties, then save it
if (ModelState.IsValid && isBookCheckedOut(1) == true)
{
// Get the existing entity
BookLoan bookLoan = db.BookLoans.Where(x => x.BookLoanID == model.BookLoanID).FirstOrDefault();
if (bookLoan != null)
{
bookLoan.BookID = model.BookID;
bookLoan.StudentID = model.StudentID;
.... // update other properties as required
_context.Update(bookloan);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
....
Side note: when returning the view, its good practice to pass back the model using return View(model); - your form controls will be correctly populated even if you don't (because they take the values from ModelState), but if you have any references to the model properties (e.g. <div>#Model.someProperty</div>) it would throw an exception.
I have stumbled upon a problem with Entity Framework this morning.
I have following code mapping a modified entity and saving it into database.
public Group Save(Group x)
{
using (var db = new HostContext())
{
db.Projects.Attach(x.Project);
if (x.ID != 0)
{
db.AttachableObjects.Attach(x);
var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
manager.ChangeObjectState(x, EntityState.Modified);
}
else
{
db.AttachableObjects.Add(x);
}
db.SaveChanges();
return x;
}
}
I call Save method with existing group as a parameter. Group contains one user I want to add as a member.
The method finishes successfully, however the relationship is not persisted in database.
Any help is very appreciated.
EDIT: These are my classes
class User : AttachableObject
{
...
private List<Group> memberof;
[DataMember]
[InverseProperty("Members")]
public List<Group> MemberOf
{
get { return memberof; }
set { memberof = value; }
}
...
}
class Group : AttachableObject
{
...
private List<User> members;
[DataMember]
[InverseProperty("MemberOf")]
public List<User> Members
{
get { return members; }
set { members = value; }
}
...
}
EDIT2: This is where the Save method is called
public Group AcceptInvite(int id)
{
var mapper = new InviteMapper();
var userMapper = new UserMapper();
var groupMapper = new GroupMapper();
var invite = mapper.Find(id);
if (invite != null)
{
var group = groupMapper.Find(invite.GroupID);
var user = userMapper.Find(invite.InviteeID);
group.Members.Add(user);
mapper.Delete(invite.ID);
return groupMapper.Save(group);
}
return null;
}
EDIT3: My mappers
public class GroupMapper
{
public Group Find(int id)
{
using (var db = new HostContext())
{
return db.AttachableObjects
.Include("Project")
.OfType<Group>().FirstOrDefault(x => x.ID == id);
}
}
}
The rest of the mappers is the same, only using their own tables.
You are not changing the relationship info of Project, you are only setting x to modified, relationship info must be changed explicitly.
So x.Project must have some property that points back to Group, you need to set it so the change is recorded.
I am guessing that x is resurrected via some deserialization process?
I would like to return an image that is dynamically created instead of retrieving from a database or from file. Let's say I have a simple ZipCode class. I would like to have a property that would return an image representation of the zipcode. The JSON returns "ZipAsImage":"System.Drawing.Bitmap".
Yes I am new to the programming in the web world. Yes I have looked for other examples but they seemed to only return an image. Thanks for any help.
public class ZipCode
{
public int ZipCodeId { get; set; }
[NotMapped]
public Image ZipAsImage
{
get
{
// Create a blank iamge for now to test.
return new Bitmap(50, 50);
}
set { }
}
}
My ApiController class
public class ZipCodesController : ApiController
{
private MyContext db = new MyContext();
// GET api/ZipCodesController
public IEnumerable<ZipCode> GetZipCodes()
{
return db.zipcodes.AsEnumerable();
}
// GET api/ZipCodesController/5
public ZipCode GetZipCode(int id)
{
ZipCode zipcode = db.ZipCodes.Find(id);
if (zipcode == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return zipcode;
}
...
My Controller class
public class ZipCodesController : Controller
{
private MyContext db = new MyContext();
//
// GET: /ZipCodes/
public ActionResult Index()
{
var zipcodes = db.ZipCodes.Include(z => z.State);
return View(zipcodes.ToList());
}
//
// GET: /ZipCodes/Details/5
public ActionResult Details(int id = 0)
{
ZipCode zipcode = db.ZipCodes.Find(id);
if (zipcode == null)
{
return HttpNotFound();
}
return View(zipcode);
}
...
I dont undersatnd the point where you are showing the MVC Contoller.Am i missing something there? However For Webapi (ApiController) You can re write your Method as follows. This should pass the enitre Object in one response.
public HttpResponseMessage GetZipCode(int id)
{
ZipCode zipcode = db.ZipCodes.Find(id);
if (zipcode == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound)
}
return Request.CreateResponse(HttpStatusCode.OK,zipcode);
}
Sending Binary Images. You need to set Straemcontent and content-type. I found a link that can help you with that Images with Webapi . Look through the entire thread . You might figure out quickly whats missing...