Retrieve values from SQL database - EF - sql

I'm trying to figure out how to pull values from a SQL database and display this in a razor view.
I have the following class using Entity Framework (I believe)
public class EventLog
{
[Key]
public int Id { get; set; }
public int EventId { get; set; }
public int MaxDelegates { get; set; }
public string Code { get; set; }
public DateTime End { get; set; }
public string Title { get; set; }
}
And I want to map title to DBTitle in the following model:
public class CourseDetailVM : CourseDetailSummaryVM
{
public EventLog DBTitle { get; set; }
}
I then want to see this in the following view:
#using TSW.Web.Helpers
#model TSW.Web.ViewModels.CourseDetailVM
#{
Layout = "~/Views/_Master.cshtml";
}
#Model.DBTitle.Title;
I have the following controller already in place (sorry for the length I plan to reduce this down):
public class CourseDetailController : BaseRenderController<CourseDetailPageDT>
{
private readonly ISitePageFactory _pageFactory = null;
private readonly IEventService _eventService = null;
public CourseDetailController(IEventService eventService, ISitePageFactory pageFactory)
{
_pageFactory = pageFactory;
_eventService = eventService;
}
public async Task<ActionResult> CourseDetail()
{
var homepage = _pageFactory.GetCurrentHomepage();
var model = Mapper.Map<CourseDetailVM>(CurrentContent);
model.Email = homepage.ContactEmail;
model.PhoneNumber = homepage.HeaderPhoneNumber;
model.InnerPageHeader.ShowHeading = true;
model.InnerPageHeader.Title = model.PageTitle;
if (model.Categories.Count == 1)
{
var categoryTagId = model.Categories.First().Id;
var contentTypeAlias = DocumentTypeHelper.GetDocumentTypeAlias<CourseListingPageDT>();
var courseCategoryPage = Umbraco.TypedContentAtXPath($"//{contentTypeAlias}")
.FirstOrDefault(x => x.GetPropertyValue<int>(Constants.DocumentTypes.CourseListingPage.Category) == categoryTagId);
if (courseCategoryPage != null)
{
model.InnerPageHeader.BackLink = Mapper.Map<LinkItem>(courseCategoryPage.Id);
}
}
try
{
model.Events = await _eventService.GetEventsForCourse(CurrentContent.AdministrateId);
}
catch (Exception ex)
{
model.Events = new StaticPagedList<Event>(Enumerable.Empty<Event>(), 1, 1, 0);
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
if (CurrentContent.Graphic != 0)
{
model.InnerPageHeader.Graphic = Mapper.Map<CtaItem>(CurrentContent.Graphic);
}
return View(model);
}
}
I've tried every suggestion I can google to add the mapping in the controlling but can't get my head around this simple function of pulling the value from a SQL database into the razor view.
Could anyone help me out?

Related

Unable to save off DB changes using Entity Framework

I am pretty new to the EF world and am trying to understand why I am unable to save off changes to my phone record. I realize that I need to modify my entity but it doesn't seem to make a difference. I have tried using context.xxxx but I am getting an error that context is not defined. What is the using for context? If I change the phone type for the same user I want to update the record with the new info.
Phone definition:
namespace NewSMS.DATA.EF
{
using System;
using System.Collections.Generic;
public partial class Phone
{
public int Phone_ID { get; set; }
public string Phone_Number { get; set; }
public int User_PK1 { get; set; }
public int LU_Phone_Type_PK1 { get; set; }
public Nullable<bool> Available { get; set; }
public virtual LU_Phone_Type LU_Phone_Type { get; set; }
public virtual User User { get; set; }
}
}
Code:
if (phnMems != null && phnMems.Count > 0)
{
var smsPhn = new Phone { };
foreach (var item in phnMems)
{
smsPhn.Phone_Number = item.Phone_Number;
smsPhn.LU_Phone_Type_PK1 = item.Phone_Type_ID;
smsPhn.User_PK1 = obj.User_ID;
Phone phoneInfo = db.Phone.FirstOrDefault(n => n.User_PK1 == obj.User_ID);
if (phoneInfo == null)
{
db.Phone.Add(smsPhn);
db.SaveChanges();
}
else
{
smsPhn.Phone_ID = phoneInfo.Phone_ID; //keep userID
db.Entry(phoneInfo).State = EntityState.Modified;
phoneInfo = smsPhn;
db.SaveChanges();
}
}
} //End IF
smsPhn.Phone_ID = phoneInfo.Phone_ID; //keep userID
db.Entry(phoneInfo).State = EntityState.Modified;
In above code, you are assigning phone_ID in smsPhn object but updating phoneInfo object.
Just a Suggestion:
Move this declaration
var smsPhn = new Phone { };
inside foreach loop and call savechanges outside the loop.

getting 400 error on webapi call blazorserver

i am trying to setup a blazor server app, calling a webapi.
I keep getting a 400 error returned, when I call the API.
I have 3 Projects, projectserver and projectapi. projectserver is where the Blazor app sits and Project API is where the API sits.
I don't know if the apicall can find the API as it does not hit any breakpoints in the API section, I am totally confused, as if it cannot find the API then it should return a 404 or other error and not 400 ?
thank you for your efforts.
this is my code,
Projectserver, this is where I post the Register Model to the API
public string message { get; set; }
public RegisterModel r = new RegisterModel();
private async Task Create(MouseEventArgs e)
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(r);
var client = clientfactory.CreateClient("ServerApi");
var result = await client.PostAsJsonAsync("/Account/Register",json); // check the Startup file and check base address for the Full route.
message = result.StatusCode.ToString();
}
}
the ClientFactory returns the base address of what is defined in startup.cs
services.AddHttpClient("ServerApi", client => client.BaseAddress = new Uri("https://localhost:44302/"));
the API is Projectserver and defined as follows.
[Route("[controller]")]
[ApiController]
public class AccountContoller : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly SecurityOptions _securityOptions;
private readonly JwtIssuerOptions _jwtOptions;
// GET: api/<Account>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<Account>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<Account>
[HttpPost]
public void Post([FromBody] string value)
{
}
// POST api/<Account>
[HttpPost("Register")]
public async Task<ActionResult<RegisterResult>> Register(RegisterModel model)
{
RegisterResult r = new RegisterResult();
var Exisits = await _context.Users.Where(r => r.EmailAddress == model.Email).FirstOrDefaultAsync();
if(Exisits != null)
{
r.Sucsess = false;
r.ErrorMessage = "Email - Already Exisits";
return r;
}
else
{
try
{
User newuser = new User();
newuser.CreatedDateTime = DateTime.UtcNow;
newuser.UserID = Guid.NewGuid();
newuser.MobileNumber = model.MobileNumber;
newuser.Password = model.Password;
newuser.FirstName = model.FirstName;
newuser.Surname = model.LastName;
_context.Users.Add(newuser);
await _context.SaveChangesAsync();
r.Sucsess = true;
return r;
}
catch(Exception e)
{
r.Sucsess = false;
r.ErrorMessage = e.ToString();
return r;
}
}
}
the Model classes are defined as Serializable
[Serializable]
public class RegisterResult
{
public bool Sucsess { get; set; }
public string ErrorMessage { get; set; }
}
[Serializable]
public class RegisterModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string RoleID { get; set; }
public string EntityID { get; set; }
public string MobileNumber { get; set; }
}
Can you please modify your code as below and give it a try:-
var serializedBody = JsonConvert.SerializeObject(r);
var jsonRequestBodyContent = new StringContent(serializedBody, Encoding.UTF8,"application/json");
var client = clientfactory.CreateClient("ServerApi");
var result = await client.PostAsync("/Account/Register",jsonRequestBodyContent);

How to override the ef core to write data automatically or log to file?

I am using asp.net core + ef core. I can add delete get or update data to the database.
But can it be true that when I do these operations it can auto log to file or database the userinfo and datetime?
If I am updating data the data will auto update the LastModifiedDateTime and ModifyUserId property.
or log it to file?
for example:
If I update the database
await _context.SaveChangesAsync(cancellationToken);
I can do this:
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedBy = _currentUserService.UserId;
entry.Entity.Created = _dateTime.Now;
break;
case EntityState.Modified:
entry.Entity.LastModifiedBy = _currentUserService.UserId;
entry.Entity.LastModified = _dateTime.Now;
break;
}
}
return base.SaveChangesAsync(cancellationToken);
}
no need I specify the property but override it to log it automatically?
how to set the property of each entity?
Then I can know when and who do the update?
Create new base model class. Every models must have at least one or more columns that are same such as ID, CreatedBy, etc. Then link base model with all your models.
BaseModel.cs
public class BaseModel
{
public int ID { get; set; }
public DateTime? Created { get; set; }
public string CreatedBy { get; set; }
public DateTime? LastModified { get; set; }
public string LastModifiedBy { get; set; }
}
User.cs
public class User : BaseModel
{
public string Name { get; set; }
public int Age {get; set; }
}
YourDbContext.cs
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseModel && (x.State == EntityState.Added || x.State == EntityState.Modified));
DateTime dt = DateTime.Now;
foreach (var entity in entities)
{
if (entity.State == EntityState.Added)
{
((BaseModel)entity.Entity).CreatedBy = _currentUserService.UserId;
((BaseModel)entity.Entity).Created = dt;
}
else if (entity.State == EntityState.Modified)
{
((BaseModel)entity.Entity).ModifiedBy = _currentUserService.UserId;
((BaseModel)entity.Entity).LastModified = dt;
}
}
return base.SaveChangesAsync(cancellationToken);
}

New records inserted in foreign key table when inserting in parent table

I am new to Asp.net MVC and working on a simple blog application (Asp.Net MVC5, EF6) for learning.
I am using repository pattern for the solution architecture with EF Code first migration, Ninject for DI. On the client side, I am using jQuery Grid for Admin to manage Posts, Categories and Tags.
- Blog.Model: Post.cs, Category.cs, Tags.cs
public class Post
{
[Required(ErrorMessage = "Id is required")]
public int Id { get; set; }
[Required(ErrorMessage = "Title is required")]
[StringLength(500, ErrorMessage = "Title cannot be more than 500 characters long")]
public string Title { get; set; }
[Required(ErrorMessage = "Short description is required")]
public string ShortDescription { get; set; }
[Required(ErrorMessage = "Description is required")]
public string Description { get; set; }
public bool Published { get; set; }
[Required(ErrorMessage = "PostedOn date is required")]
public DateTime PostedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
[ForeignKey("Category")]
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual IList<Tag> Tags { get; set; }
}
public class Category
{
[Key]
public int CategoryId { get; set; }
[Required(ErrorMessage = "Category Name is required")]
[StringLength(500,ErrorMessage = "Category name length cannot exceed 500")]
public string Name { get; set; }
[Required(ErrorMessage = "Category Name is required")]
[StringLength(500, ErrorMessage = "Category name length cannot exceed 500")]
public string Description { get; set; }
[JsonIgnore]
public virtual IList<Post> Posts { get; set; }
}
public class Tag
{
public int Id { get; set; }
[Required(ErrorMessage = "Name is required")]
[StringLength(500, ErrorMessage = "Name length should not exceed 500 characters")]
public string Name { get; set; }
public string Description { get; set; }
[JsonIgnore]
public IList<Post> Posts { get; set; }
}
- Blog.Repository: BlogRepository, IBlogRepository, BlogContext
public interface IBlogRepository
{
int SavePost(Post post);
//Other methods...
}
public class BlogRepository : BlogContext, IBlogRepository
{
public BlogContext _db;
public BlogRepository(BlogContext db)
{
_db = db;
}
public int SavePost(Post post)
{
_db.Posts.Add(post);
_db.SaveChanges();
return post.Id;
}
//Other implementations...
}
public class BlogContext : DbContext, IDisposedTracker
{
public BlogContext() : base("BlogDbConnection") { }
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Category> Categories { get; set; }
public bool IsDisposed { get; set; }
protected override void Dispose(bool disposing)
{
IsDisposed = true;
base.Dispose(disposing);
}
- Blog.Web: AdminController.cs, NinjectWebCommon.cs
AdminController sends/consumes data in Json format.
public class AdminController : Controller
{
private readonly IBlogRepository _blogRepository;
public AdminController(IBlogRepository blogRepository)
{
_blogRepository = blogRepository;
}
//POST: /Admin/CreatePost
[HttpPost, ValidateInput(false)]
public ContentResult CreatePost([ModelBinder(typeof(PostModelBinder))] Post model)
{
string json;
ModelState.Clear();
if (TryValidateModel(model))
{
var id = _blogRepository.SavePost(model);
json = JsonConvert.SerializeObject(
new
{
id = id,
success = true,
message = "Post saved successfully."
});
}
else
{
json = JsonConvert.SerializeObject(
new
{
id = 0,
success = false,
message = "Post not saved."
});
}
return Content(json, "application/json");
}
}
public static class NinjectWebCommon
{
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<BlogContext>().ToSelf(); //This isn't helping either
kernel.Bind<IBlogRepository>().To<BlogRepository>();
}
}
I am using Custom Model Binding because I was getting validation exception while saving post since list of Categories and Tags received from grid do not map to actual objects in the application model. Therefore in the custom model binding, I am populating Post object with actual objects received from grid. This Post object is Sent to controller which Save to database using DbContext and Repository.
public class PostModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var post = (Post)base.BindModel(controllerContext, bindingContext);
**var blogRepository = new BlogRepository(new BlogContext());**//I think here I need to inject the dependency for BlogContext, but don't know how to do that.
if (post.Category != null)
{
post.Category = blogRepository._db.Categories.AsNoTracking().Single(c => c.CategoryId == post.Category.CategoryId);
}
var tags = bindingContext.ValueProvider.GetValue("Tags").AttemptedValue.Split(',');
if (tags.Length > 0)
{
post.Tags = new List<Tag>();
foreach (var tag in tags)
{
var id = int.Parse(tag.Trim());
post.Tags.Add(blogRepository._db.Tags.AsNoTracking().Single(t => t.Id == id));
}
}
if (bindingContext.ValueProvider.GetValue("oper").AttemptedValue.Equals("edit"))
post.ModifiedOn = DateTime.UtcNow;
else
post.PostedOn = DateTime.UtcNow;
return post;
}
}
Issue: When the Post is saved, data context inserts new rows for Category and Tags in their respective tables. The newly created post refers to new Category (Id:22) under Foreign key column.
Post:
Category:
Tag:
I think the reason for this is that when entity is saved it is attached to a different ObjectContext and I need to attach it to current context but do not know how? I found similar question asked before but there isn't an accepted answer to that. Any help would be greatly appreciated.
I was able to resolve above issue by attaching category and tags value to objectcontext manually, which indicates EF the changes it needs to make. This way it doesn't create new entries in Category and Tag's parent tables.
public int SavePost(Post post)
{
//attach tags to db context for Tags to tell EF
//that these tags already exist in database
foreach (var t in post.Tags)
{
_db.Tags.Attach(t);
}
//tell EF that Category already exists in Category table
_db.Entry(post.Category).State = EntityState.Modified;
_db.Posts.Add(post);
_db.SaveChanges();
return post.Id;
}
public void EditPost(Post post)
{
if (post == null) return;
//get current post from database
var dbPost = _db.Posts.Include(p => p.Tags).SingleOrDefault(p => p.Id == post.Id);
//get new list of tags
var newTags = post.Tags.Select(tag => new Tag() { Id = tag.Id, Name = tag.Name, Description = tag.Description }).ToList();
if (dbPost != null)
{
//get category from its parent table and assign to db post
dbPost.Category = _db.Categories.Find(post.Category.CategoryId); ;
//set scalar properties
_db.Entry(dbPost).CurrentValues.SetValues(post);
//remove tags from post in database
foreach (var t in dbPost.Tags.ToList())
{
if (!newTags.Contains(t))
{
dbPost.Tags.Remove(t);
}
}
//add tags to post in database
foreach (var t in newTags)
{
if (dbPost.Tags.All(p => p.Id != t.Id))
{
var tagInDb = _db.Tags.Find(t.Id);
if (tagInDb != null)
{
dbPost.Tags.Add(tagInDb);
}
}
}
}
//save changes
_db.SaveChanges();
}

DropDownList with possible nested DropDownList in MVC4

I have a set of questions the user can choose from and some of those questions have a secondary list of options to choose from. My goal is to have a drop down list and if you pick one of the options that has items in its SecondaryChoiceList then a second list would appear below the initial dropdown and all of this would be strongly typed and bound to the model upon submission.
I can get the initial list to appear by saying:
#Html.DropDownListFor( x => x.SelectedChoiceId, new SelectList(Model.Choices, "Id", "Name"))
But that has no hooks to the secondary list and I am completely lost as to how I would tie that secondary list back to the model that is returned when I submit the form.
Here's my view model:
public class ExampleViewModel
{
public List<Choice> ChoiceList { get; set; }
public int SelectedChoiceId { get; set; }
public int SelectedAffiliateId { get; set; }
}
Here is what a Choice looks like:
public class Choice
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<SecondaryChoice> SecondaryChoiceList { get; set; }
public Choice()
{
SecondaryChoiceList = new List<SecondaryChoice>();
}
}
And here is my SecondaryChoice object:
public class EligibleAffiliate
{
public int Id { get; set; }
public int EligibilityChoiceId { get; set; }
public string Name { get; set; }
}
If there is anything that I can clear up let me know.
I have tried to keep it as simple as possible.
So, a sample model is given below:
namespace StackOverflow.Models
{
public class Choice
{
public int Id { get; set; }
public string Name { get; set; }
public Choice()
{
Id = 0;
}
public Choice(int id, string name)
{
Id = id;
Name = name;
}
}
}
namespace StackOverflow.Models
{
public class ExampleViewModel
{
public List<Choice> PrimaryChoiceList { get; set; }
public List<Choice> SecondaryChoiceList { get; set; }
public int SelectedChoiceId { get; set; }
public int SelectedAffiliateId { get; set; }
public ExampleViewModel()
{
SelectedChoiceId = 0;
SelectedAffiliateId = 0;
PrimaryChoiceList = new List<Choice>()
{
new Choice(1, "How are you?"),
new Choice(2, "How is the weahter?"),
new Choice(3, "What have you been doing so far?"),
new Choice(4, "What's up man?"),
new Choice(5, "Any news?"),
new Choice(5, "Bla bla bla")
};
SecondaryChoiceList = new List<Choice>()
{
new Choice(1, "How are you dear?"),
new Choice(2, "How is the weahter?"),
new Choice(3, "What have you been doing so far dear?"),
new Choice(4, "What's up man?"),
new Choice(5, "Any romantic news?")
};
}
}
}
Sample controller:
namespace StackOverFlow.Controllers
{
public class SOController : Controller
{
public static ExampleViewModel evm = new ExampleViewModel();
public ActionResult Index()
{
return View(evm);
}
public ActionResult SetSelection(int id)
{
evm.SelectedChoiceId = id;
if (evm.PrimaryChoiceList.Count() > 0)
{
Choice selection = evm.PrimaryChoiceList.ElementAt(id-1);
Choice affiliate = (Choice)evm.SecondaryChoiceList.FirstOrDefault(x => x.Name == selection.Name);
if (affiliate != null)
{
return Content("show");
}
else
{
return Content("hide");
}
}
else
{
return Content("hide");
}
}
}
}
And the web page:
#using StackOverflow2.Models;
#model ExampleViewModel
<script src="#Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
#{
ViewBag.Title = "Stackoverflow Sample";
}
<h2>Index</h2>
<script type="text/javascript">
// Get the selection and make Ajax Request to the controller, action: SetSelection,
// which in turn may decide whetger you must show or hide the control
function updateSeconadryQuestion(id) {
var xmlhttp;
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else {// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
if (xmlhttp.responseText == 'show')
$('#SecondaryQuestionDropBoxId').show();
else
$('#SecondaryQuestionDropBoxId').hide();
}
}
xmlhttp.open("GET", "/SO/SetSelection?id=" + id, true);
xmlhttp.send();
}
</script>
#Html.DropDownListFor(x => x.SelectedChoiceId, new SelectList(Model.PrimaryChoiceList, "Id", "Name", "Value"), new { id = "PrimaryQuestionDropBoxId", onchange = "updateSeconadryQuestion(value);" })
<div id="SeconadryQuestionDivId">
#Html.DropDownListFor(x => x.SelectedAffiliateId, new SelectList(Model.SecondaryChoiceList, "Id", "Name"), new { id = "SecondaryQuestionDropBoxId" })
</div>