Using MVC Orchard services file to allow the user to enter a new record in the 'HoursWorkedToday' table, allowing the user to enter how many hours they have worked that day.
//HoursWorkedService
public class HoursWorkedService : IHoursWorkedService
{
private IRepository<HoursWorkedPerDay> _repository = null;
public HoursWorkedService(IRepository<HoursWorkedPerDay> Repository)
{
_repository = Repository;
}
public Models.HoursWorkedPerDay Create(HoursWorkedPerDay newRecordForHoursWorked)
{
_repository.Create(newRecordForHoursWorked);
return newRecordForHoursWorked;
}
//IHoursWorkedService
public interface IHoursWorkedService : IDependency
{
HoursWorkedPerDay Create(HoursWorkedPerDay newRecordForHoursWorked);
}
//controller
[HttpPost, ActionName("HoursWorkedPerDay")]
public ActionResult HoursWorkedPerDayPOST(int userId, int hours)
{
DateTime TodaysDate = DateTime.UtcNow;
HoursWorkedPerDay HoursPerDay = new HoursWorkedPerDay();
HoursPerDay.Date = TodaysDate;
HoursPerDay.WorkerRecord_Id = userId;
HoursPerDay.HoursWorked = hours;
_hoursWorked.Create(HoursPerDay);
_notifier.Add(NotifyType.Information, T("Thank you, Hours Added."));
return RedirectToAction("Index");
}
but after the post i get the 'website can not display'
LOGS show:
Cannot insert the value NULL into column 'Id', table 'Orchard.Timekeeper.dbo.Timekeeper_HoursWorkedPerDay'; column does not allow nulls. INSERT fails
In my migration file i have 'id' set to .ContentPartRecord(), which i assumed created the next record automatically when the service was used
Do not use ContentPartRecord on something that is not a content part record. Instead, add the Id column yourself as identity so that it gets auto-incremented.
If it is meant as a content part record, never use it without a content item. In particular, never use the repository directly on part records, and go through the content manager instead.
Related
I'm trying to pull out of database objects where current user Id is in list of those objects.
My model:
public class Procedure
{
...
public IList<User> Lawyers{ get; set; }
...
}
And in controller:
[HttpGet]
public async Task<IActionResult> MyProcedures()
{
var user = await userManager.GetUserAsync(User);
var procedures = context.Procedures.Where(x => x.Lawyers.Contains(user));
return View(procedures);
}
This only selects one object.
EDIT:
Problem is in my User class, it takes only one Id from Procedure and that is why its showing my only one (last added). Thanks for help guys!
The way you wrote will work if you have icomparable or something implemented that can tell that a lawyer is “the” user if user id matches.
Without that you would need to do lawyers.Any(x=> x.UserId == user.UserId)
You are missing something, which user ??
Either pass something into your function signature or get it from HTTPContext you need to get the user you want to find
If you are using ASP core its bit different from the old ways
First this:
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
And now..
// make sure you can access/inject it if you want
// private.. _httpContextAccessor
// then in your action
[HttpGet]
public async Task<IActionResult> MyProcedures()
{
// again make sure you can access the context _httpContextAccessor
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
var procedures = context.Procedures.Where(x => x.Lawyers.Contains(userId).FirstOrDefault());
//... fill in whatever logic you want..
return View(parnice);
}
Update 2 based on question/comments:
Do this in two Steps
Step 1: Get the Current User (with claims or HTTPContext as shown below), for e.g. System.Security.Claims.ClaimsPrincipal currentUser = this.User;
Step 2: Using the user, find all the related Lawyers etc. context.Procedures.Where(x => x.Lawyers.Contains(userId)
Make sure to register HttpContextAccessor in your startup... double check this.. to register in your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
In the original Core version, I have to double check if it changed now, assuming your code is inside an MVC controller:
public class YourController : Microsoft.AspNetCore.Mvc.Controller
Now, since you have the Controller base class, you can get the IClaimsPrincipal from the User property
System.Security.Claims.ClaimsPrincipal currentUser = this.User;
You can check the claims directly (without a round trip to the database):
var userId = _userManager.GetUserId(User); // Get user id:
To your second comment, you can get either the UserId or UserName
// user's userId
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)
// user's userName
var userName = User.FindFirstValue(ClaimTypes.Name)
A nice reference for you hope it helps :)
I've connected to my database using Entity Framework and am building my first MVC app for use in a web page.
I can get the controller to populate public strings in my models with no problem... the issue I'm having is that I can't figure out how to filter responses from my database.
I expect to have only one item returned which I will display in the view with #Model.BusinessUnit
Here's my Model Class for the database table:
public partial class TBL_Wholesale_UWS_BusinessUnits
{
public int PrimaryID { get; set; }
public string BusinessUnit { get; set; }
public string Status { get; set; }
}
Here's what I have in my controller:
public ActionResult test(int PrimaryID)
{
var testing = new TBL_Wholesale_UWS_BusinessUnits();
// maybe putting new is the wrong thing to do as that would be wiping the class? IDK
return View(testing);
}
As you can see, the PrimaryID is passed to the controller via the querystring and this is recognised without issue, but I'm at a loss as to where to add the filter, I assumed it would be something like...
var testing = TBL_Wholesale_UWS_BusinessUnits.Where(TBL_Wholesale_UWS_BusinessUnits.PrimaryID = PrimaryID);`
but Visual Studio is telling me in no uncertain terms that this this wrong.
Had this been classic asp I would have just made a record set and used the where clause in SQL, but as this is built with the Entity Framework to do my connecting I don't really know where to start.
Any help would be greatly appreciated.
If you are only trying to return that one specific object to the view.. then you need to find that int PrimaryID in the database and retrieve that specific record.
What you are doing is simply creating a new instance of the TBL_Wholesale_UWS_BusinessUnits class which is empty.
Try this:
public ActionResult test(int PrimaryID)
{
var testing = db.TableName.Find(PrimaryID);
// db = Whatever the variable holding your connection string is.. maybe DbContext
// TableName = Whatever table in your database that holds the record you want
// This will return the specific object that you are looking for
return View(testing);
}
I hope this helps!
I am trying to update a specific part with following code:
var nationalPart = _contentManager.Get<NationalPart>(Id);
nationalPart.Name = part.Name;
i have already done this.but now it is not working.even i call contentManager.Flush() explicitly, but still no luck. can anyone help me with this?
thanks in advance.
EDIT: i found where my problem originates from!
when i make a JSON request to update a contentPart.the in memory version is updating but it is not reflecting the result to db.now we are one step closer to answer.but what is preventing the session from updating the db?
EDIT2:
this is code for my handler :
public NationalPartHandler(IRepository<NationalPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
this is code for Controller Action:
[GridAction(EnableCustomBinding = true)]
public JsonResult UpdateNational(NationalViewModel Model, GridCommand Command)
{
if (!ModelState.IsValid)
return new JsonResult { Data = "error" };
var NationalPart = _contentmanager.Get(Model.Id).As<NationalPart>();
NationalPart.Record = new NationalPartRecord();
NationalPart.Record.MapFrom<NationalPartRecord, NationalViewModel>(Model);
_soccerService.UpdateNational(NationalPart);
return NationalsListJson(Command);
}
and finally code for service:
public void UpdateNational(NationalPart part)
{
var nationalPart = _contentManager.Get<NationalPart>(part.Id);
nationalPart.Name = part.Name;
_contentManager.Flush();
}
Remove the line
NationalPart.Record = new NationalPartRecord();
The underlying record is automatically generated and is proxied so NHibernate can save the data to the database whenever you do any change to it. You don't want to replace that ever, otherwise no data will get saved.
UpdateNational is redundant and you can remove it - you already have part.Name (which if I understand correctly, should map to part.Record.Name) set during call to record.MapFrom.
So the correct version would look like this
[GridAction(EnableCustomBinding = true)]
public JsonResult UpdateNational(NationalViewModel Model, GridCommand Command)
{
if (!ModelState.IsValid)
return new JsonResult { Data = "error" };
var part = _contentmanager.Get(Model.Id).As<NationalPart>();
part.Record.MapFrom<NationalPartRecord, NationalViewModel>(Model);
return NationalsListJson(Command);
}
given your NationalPart looks similar to:
public NationalPart : ContentPart<NationalPartRecord>
{
public string Name {
get { return Record.Name; }
set { Record.Name = value; }
}
}
Summarizing - if you want to store some data in Orchard, all you need to do is to set a value of some property of a record, nothing more. Data will get persisted at the end of a request (or earlier, if you call IContentManager.Flush()).
I have the following C# model class:
public class Thingy
{
public ObjectId Id { get; set; }
public string Title { get; set; }
public DateTime TimeCreated { get; set; }
public string Content { get; set; }
public string UUID { get; set; }
}
and the following ASP.MVC controller action:
public ActionResult Create(Thingy thing)
{
var query = Query.EQ("UUID", thing.UUID);
var update = Update.Set("Title", thing.Title)
.Set("Content", thing.Content);
var t = _collection.Update(query, update, SafeMode.True);
if (t.UpdatedExisting == false)
{
thing.TimeCreated = DateTime.Now;
thing.UUID = System.Guid.NewGuid().ToString();
_collection.Insert(thing);
}
/*
var t = _collection.FindOne(query);
if (t == null)
{
thing.TimeCreated = DateTime.Now;
thing.UUID = System.Guid.NewGuid().ToString();
_collection.Insert(thing);
}
else
{
_collection.Update(query, update);
}
*/
return RedirectToAction("Index", "Home");
}
This method either does an update or insert. If it needs to do an insert, it must set the UUID and TimeCreated members. If it needs to do an update, it must leave UUID and TimeCreated alone, but must update the members Title and Content.
The code that's commented out works, but does not seem to be most efficient. When it calls FindOne, that is one trip to mongodb. Then if it goes to the else clause, it does another query and an update operation, so that's 2 more trips to mongodb.
What is a more efficient way to do what I'm trying to accomplish?
As mentioned in the linked SO answer, for upserts to work, you need to update the entire document, not just a few properties.
Personally I would separate the Create and Edit into separate MVC actions. SRP. Creating a Thingy has different considerations from updating it.
If you still want to do an upsert instead of separate insert/update calls, you will need to use the following code:
_collection.Update(
Query.EQ("UUID", thing.UUID),
Update.Replace(thing),
UpsertFlags.Upsert
);
The question now becomes, how do we ensure the thing has the appropriate values for both cases, ie insert as well as update.
My assumption is (based on your code model binding to a Thingy instance), your view is sending back all fields (including UUID and TimeCreated). Which implies, in case of an update, the view already has the values pre-populated for UUID and TimeCreated. So in the case of a Thingy being updated, the thing object has the latest values.
Now in case of an create, when the view is rendered, you could store DateTime.MinValue for the TimeCreated field. In your Create MVC action, you could check if TimeCreated is DateTime.MinValue, then set it to current time and also store a new value for UUID.
This way, in the case of a insert as well, the thing has the latest values. We can thus safely do an Upsert.
I take this approach when doing upserts for Mongo from the controller
public ActionResult Create(Thingy model)
{
var thing = _collection.FindOneAs<Thingy>(Query.EQ("UUID", model.UUID));
if(thing == null)
{
thing = new Thingy{
TimeCreated = DateTime.Now,
UUID = System.Guid.NewGuid().ToString(),
Id = ObjectId.GenerateNewId()
}
}
else
{
thing.Content = model.Content;
//other updates here
}
_collection.Save<Thingy>(thing);
return View();
}
I have a Model, which has a drop down list of reference data. The reference data is based on the current user. So, User A sees his records assigned to him, and User B can see different records. The point is, the reference data is based on the userId.
The userId is in the Session. Is there a way for the model to access a session variable? The creation of the SelectionList is built into the model. So, I can put the UserId as a parameter in the constructor - but I need that in all constructors for the model. Seems duplication of work. I'd prefer the model to be able to say, 'Ah, the current user is User1' but it's self.
Possible? Or do I have a design flaw?
How about something like this
public class WibbleModelBuilder
{
private int _userId;
private WibbleRepository _repo;
public WibbleModelBuilder(WibbleRepository wibbleRepository, int userId)
{
_repo=wibbleRepository;
_userId=userId;
}
public WibbleModel Build()
{
var model = new WibbleModel();
model.LookupList = _repo.GetLookupForUser(_userId);
return model;
}
}
Now you can create you WibbleModelBuilder in your controller and pass your repository and userid into the constructor. Your model is now just a very simple data object
public class WibbleModel
{
public IList<ReferenceData> LookupList { get; set;}
}