This is my Controllers Edit Method in Httpost
[HttpPost]
public ActionResult Edit(Employee employee)
{
if (!ModelState.IsValid)
{
var department = db.Departments.ToList();
var viewModel = new EmployeeViewModel
{
Departments = department,
Employees = db.Employees.ToList()
};
return View("Index", viewModel);
}
db.Entry(employee).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
this method creates new data instead of updating data.
The problem is you're trying to update Employee instance directly based from action parameter without seeking any matching record first, hence EF "unaware" if the entity should be modified and treat its state as EntityState.Added in this line:
db.Entry(employee).State = EntityState.Modified;
What you should do is using SingleOrDefault() or FirstOrDefault() to fetch matching record based from Id property, update all required properties then attach Employee instance into Employees entity and set its state as EntityState.Modified:
[HttpPost]
public ActionResult Edit(Employee employee)
{
if (!ModelState.IsValid)
{
var department = db.Departments.ToList();
var viewModel = new EmployeeViewModel
{
Departments = department,
Employees = db.Employees.ToList()
};
return View("Index", viewModel);
}
// find employee with matching ID first
var selectedEmployee = db.Employees.SingleOrDefault(x => x.Id == employee.Id);
// update all properties
selectedEmployee.Name = employee.Name;
// other properties here
// attach the context
db.Employees.Attach(selectedEmployee);
// set modified state and save all changes
db.Entry(selectedEmployee).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
Related issues:
Why does Entity Framework reinsert existing objects into my database?
Updating the Record with EntityState.Modified but its inserting the new row rather than update
Related
I need to obtain a dataset or array of the results provided by a Generic Inquiry inside of BLC / graph logic.
I've been given the following as an example, but this obviously only returns a count.
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> Test;
[PXButton]
[PXUIField]
protected void test()
{
var dataGraph = PXGenericInqGrph.CreateInstance("GI000010");
if (dataGraph != null)
{
var count = dataGraph.Views["Results"].SelectMulti().Count;
}
}
}
When I use an index in the returned variable, I don't get anything that resembles a row of the actual data in the GI, for example:
dataGraph.Views["Results"].SelectMulti()[0]
This doesn't return the actual data. I've tried the several methods / properties provided by the dataGraph.Views["Results"] object, but none give me the results I need.
Is there a method or property that simply returns a dataset or array of the actual results of the Generic Inquiry?
The following custom button I developed for Non-Stock Items will call the specified Generic Inquiry, update its parameter value and retrieve all result rows and corresponding field results without needing to know the specific DAC.
public class NonStockItemMaintExtension : PXGraphExtension<NonStockItemMaint>
{
#region Buttons
public PXAction<InventoryItem> RunSummary;
[PXButton]
[PXUIField(DisplayName = "Run Summary")]
public virtual IEnumerable runSummary(PXAdapter adapter)
{
InventoryItem item = Base.Item.Current;
GIDesign design = PXSelectReadonly<GIDesign, Where<GIDesign.name, Equal<Required<GIDesign.name>>>>.Select(this.Base, "ItemTrans");
if (design != null)
{
PXLongOperation.StartOperation(this.Base, delegate ()
{
//Creates Generic Inquiry Graph for the specified inquiry
PXGenericInqGrph graph = PXGenericInqGrph.CreateInstance(design.DesignID.Value);
if (design != null)
{
//Sets parameter value
graph.Caches[typeof(GenericFilter)].SetValueExt(graph.Filter.Current, "Item", item.InventoryCD);
//Loops through each returned result row of Generic Inquiry
foreach (GenericResult resultRow in graph.Views["Results"].SelectMulti())
{
//Loops through objects returned from one - not an object per field
foreach (string key in resultRow.Values.Keys)
{
//Loops through list of each object and the fields we need values from for each data key
foreach (GIResult resultMap in PXSelectReadonly<GIResult, Where<GIResult.designID, Equal<Required<GIResult.designID>>, And<GIResult.objectName, Equal<Required<GIResult.objectName>>>>>.Select(graph, new object[] { design.DesignID.Value, key }))
{
//retrieves field value from data object specified
var result = graph.Caches[resultRow.Values[key].GetType()].GetValue(resultRow.Values[key], resultMap.Field);
}
}
}
}
});
}
return adapter.Get();
}
#endregion
}
Tables referenced in Generic Inquiry :
Data fields to retrieve from the inquiry :
Parameters being populated :
Value being retrieved :
This will give you a list of the results. Each list element will contain the records involved with 1 row of the GI result (each joined table). Only those fields included in the GI will have values I believe.
public PXAction<SOOrder> Test;
[PXButton]
[PXUIField]
protected void test()
{
// Using "Invoiced Items" (GI000008) GI in 2017R2
var dataGraph = PXGenericInqGrph.CreateInstance("GI000008");
if (dataGraph == null)
{
return;
}
var resultList = dataGraph.Results.Select().FirstTableItems.ToList();
foreach (GenericResult genericResult in resultList)
{
// Note: not all values are pulled into the DAC, only those used in the query
var arInvoice = GetDac<PX.Objects.AR.ARInvoice>(genericResult);
var arTran = GetDac<PX.Objects.AR.ARTran>(genericResult);
var customer = GetDac<PX.Objects.AR.Customer>(genericResult);
var customerClass = GetDac<PX.Objects.AR.Customer>(genericResult);
var address = GetDac<PX.Objects.AR.Customer>(genericResult);
var bAccount = GetDac<PX.Objects.CR.BAccount>(genericResult);
var inventoryItem = GetDac<PX.Objects.IN.InventoryItem>(genericResult);
var formulaValues = genericResult.Values.Last();
}
}
protected virtual T GetDac<T>(GenericResult genericResult) where T : class, PX.Data.IBqlTable
{
// Example:
//var customer = (PX.Objects.AR.Customer)genericResult.Values["Customer"]
return (T)(genericResult?.Values != null &&
genericResult.Values.ContainsKey(typeof(T).Name)
? genericResult.Values[typeof(T).Name]
: null);
}
// I am using code like below
Document doc = client.CreateDocumentQuery<Document>(collectionLink)
.Where(r => r.Id == "doc id")
.AsEnumerable()
.SingleOrDefault();
doc.SetPropertyValue("MyProperty1", "updated value");
Document updated = await client.ReplaceDocumentAsync(doc);
Want to remove "MyProperty2" from the document. How to do this?
It seems that you'd like to update MyProperty1 property and remove MyProperty2 property from your document, the following sample code is for your reference.
private async Task updateDoc()
{
string EndpointUri = "xxxxx";
string PrimaryKey = "xxxxx";
DocumentClient client;
client = new DocumentClient(new Uri(EndpointUri), PrimaryKey);
Document doc = client.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri("testdb", "testcoll"))
.Where(r => r.Id == "doc5")
.AsEnumerable()
.SingleOrDefault();
//dynamically cast doc back to your MyPoco
MyPoco poco = (dynamic)doc;
//Update MyProperty1 of the poco object
poco.MyProperty1 = "updated value";
//replace document
Document updateddoc = await client.ReplaceDocumentAsync(doc.SelfLink, poco);
Console.WriteLine(updateddoc);
}
public class MyPoco
{
public string id { get; set; }
public string MyProperty1 { get; set; }
}
My document:
Updated:
Edit:
this would remove "MyProperty3" and "MyProperty4" as well.
As you mentioned, setting property with null would be also a approach.
Document doc = client.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri("testdb", "testcoll"))
.Where(r => r.Id == "doc5")
.AsEnumerable()
.SingleOrDefault();
doc.SetPropertyValue("MyProperty2", null);
//replace document
Document updateddoc = await client.ReplaceDocumentAsync(doc.SelfLink, doc);
Thanks for replying. You got it what I was asking for.
I did not want to use MyPoco as using Microsoft.Azure.Document is the most flexible. Also, using this approach would also remove any MyProperty3 if it exists with MyPoco.
Using Document class only, following worked:
eachDoc.SetPropertyValue("MyProperty2", null);
I'm using mvc5, and everything about user account management I do with UserManager. It works good with roles, claims, etc. But I didn't find how to delete user with UserManager. Is there a way to delete user with UserManager? I can create Database context with dbset and then delete it from this context, but I don't want create dbcontext, userclass, etc. for one delete method.
I had issues with the above answer, though I was able to work out what's wrong. I kept getting a cascading error. Basically the user was being deleted without the role being deleted. DeleteAsync was not doing that for me (I have the latest build of Identity Framework). Ended up passing both the userid and role into my code, deleting the user from the role, then deleting the user. Seems to work fine.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Delete(string id, string role)
{
// Check for for both ID and Role and exit if not found
if (id == null || role == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
// Look for user in the UserStore
var user = UserManager.Users.SingleOrDefault(u => u.Id == id);
// If not found, exit
if (user == null)
{
return HttpNotFound();
}
// Remove user from role first!
var remFromRole = await UserManager.RemoveFromRoleAsync(id, role);
// If successful
if (remFromRole.Succeeded)
{
// Remove user from UserStore
var results = await UserManager.DeleteAsync(user);
// If successful
if (results.Succeeded)
{
// Redirect to Users page
return RedirectToAction("Index", "Users", new {area = "Dashboard"});
}
else
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
else
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
Delete was not supported in UserManager in 1.0, but its supported in the upcoming 2.0 release, and in the current 2.0 nightly builds if you want to preview the changes early.
Using the updated asp.net identity I have the following code:
public UserManagerController()
: this(new UserManager<User>(new UserStore<User>(new ApplicationDbContext())))
{
}
public UserManagerController(UserManager<User> userManager)
{
UserManager = userManager;
}
public UserManager<User> UserManager { get; private set; }
public async Task<ActionResult> Delete (string id)
{
var userContext = new ApplicationDbContext();
var user = UserManager.Users.SingleOrDefault(u => u.Id == id);
var userStore = new UserStore<User>(userContext);
await UserManager.DeleteAsync(user);
// var userManager = new UserManager<User>(userStore);
// await userManager.DeleteAsync(user);
return RedirectToAction("Index");
}
This one now deletes the user. It is also no need to delete from UserRoles table as that is taken care of by UserManager.DeleteAsync(user).
Hope this helps a few. I spent some time figuring out why I got some errors.
Trond
everyone,
I'm having a problem that does not appear in the output parameter list on the right side under "Look for" underneath "Local Values", I do not understand the problem or reason for not appear, since in terms of input parameters's okay.
protected override void Execute(CodeActivityContext executionContext)
{
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
//Create the context
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
tracingService.Trace("Creating Account");
Account entity = new Account();
entity.Name = AccountName.Get<string>(executionContext);
Guid entityId = service.Create(entity);
string a = entity.Name;
AccountNameTest.Set(executionContext, a);
tracingService.Trace("Account created with Id {0}", entityId.ToString());
tracingService.Trace("Create a task for the account");
Task newTask = new Task();
newTask.Subject = TaskSubject.Get<string>(executionContext);
newTask.RegardingObjectId = new EntityReference(Account.EntityLogicalName, entityId);
Guid taskId = service.Create(newTask);
tracingService.Trace("Task has been created");
tracingService.Trace("Retrieve the task using QueryByAttribute");
QueryByAttribute query = new QueryByAttribute();
query.Attributes.AddRange(new string[] { "regardingobjectid" });
query.ColumnSet = new ColumnSet(new string[] { "subject" });
query.EntityName = Task.EntityLogicalName;
query.Values.AddRange(new object[] { entityId });
tracingService.Trace("Executing the Query for entity {0}", query.EntityName);
//Execute using a request to test the OOB (XRM) message contracts
RetrieveMultipleRequest request = new RetrieveMultipleRequest();
request.Query = query;
Collection<Entity> entityList = ((RetrieveMultipleResponse)service.Execute(request)).EntityCollection.Entities;
//Execute a request from the CRM message assembly
tracingService.Trace("Executing a WhoAmIRequest");
service.Execute(new WhoAmIRequest());
if (1 != entityList.Count)
{
tracingService.Trace("The entity list was too long");
throw new InvalidPluginExecutionException("Query did not execute correctly");
}
else
{
tracingService.Trace("Casting the Task from RetrieveMultiple to strong type");
Task retrievedTask = (Task)entityList[0];
if (retrievedTask.ActivityId != taskId)
{
throw new InvalidPluginExecutionException("Incorrect task was retrieved");
}
tracingService.Trace("Retrieving the entity from IOrganizationService");
//Retrieve the task using Retrieve
retrievedTask = (Task)service.Retrieve(Task.EntityLogicalName, retrievedTask.Id, new ColumnSet("subject"));
if (!string.Equals(newTask.Subject, retrievedTask.Subject, StringComparison.Ordinal))
{
throw new InvalidPluginExecutionException("Task's subject did not get retrieved correctly");
}
//Update the task
retrievedTask.Subject = UpdatedTaskSubject.Get<string>(executionContext);
service.Update(retrievedTask);
}
}
//
[Input("Name conta")]
[Default("testv01")]
public InArgument<string> AccountName { get; set; }
[Input("Task")]
[Default("testv01")]
public InArgument<string> TaskSubject { get; set; }
[Input("Update task")]
[Default("testUPDATED:v01}")]
public InArgument<string> UpdatedTaskSubject { get; set; }
[Output("Account ID Guid")]
[Default("testUPDATED:v01")]
public OutArgument<string> AccountNameTest { get; set; }
Ok, problem solved, just restart IIS to assume the fields, or through version change. The problem was the update of the plugin, this also happens with workflows. According to CRM 4.0 i realized this situation does not happen in CRM 4.0.
Even though this Question is already answered I wanted to share two Cases where this solution didn't work (even in a recent Version of CRM):
Case 1
Having choosen Input-Parameter-Names that contained german Umlauts (äöüß).
IIS-restart did not help.
Choosing Names without Umlauts solved the Problem for me.
Case 2
We recently also had a case where a normal In-Argument did not show up, even after restarting the entire Maschine CRM was running on. The Solution was not to obvious:
Open PluginRegistrationTool from SDK
Choose the Assembly that contains your CWA
Choose your CWA
Hit the Save-Button in the Properties-Tab of your CWA
We are migrating our SProc based solution over to ORMLite, and so far has been pretty painless. Today I wrote the following method:
public AppUser GetAppUserByUserID(int app_user_id)
{
var dbFactory = new OrmLiteConnectionFactory(this.ConnectionString, SqlServerOrmLiteDialectProvider.Instance);
AppUser item = null;
var rh = new RedisHelper();
var id= CacheIDHelper.GetAppUserID( app_user_id );
item = rh.Get<AppUser>(id);
if (item == null)
{
try
{
using (var db = dbFactory.OpenDbConnection())
{
item = db.Single<AppUser>("application_user_id={0}", app_user_id);
rh.Set<AppUser>(item, id);
}
}
catch (Exception ex)
{
APLog.error(ex, "Error retrieving user!");
}
}
return item;
}
I have remove some of the extraneous fields, but they are basically:
[Alias("application_user")]
public class AppUser : APBaseObject
{
[Alias("application_user_id")]
[AutoIncrement]
public int? UserID
{
get;
set;
}
[Alias("application_user_guid")]
public string UserGUID
{
get;
set;
}
//MORE FIELDS here.
}
The challenge is that they only field that is populate is the ID field, AND I already know that ID because I am passing it into the method.
I did get the last SQL called and ran that against the DB directly and all of the fields were being referenced correctly.
I stepped through the code in the debugger, and everything came back correctly, except that the only field returned was the ID.
Thoughts?
I had a similar issue which was caused by my class methods not mapping to the db properly. My exact issue was caused by a nullable int field in the db and the class method was defined as an 'int' instead of 'int?'.
Perhaps you have a similar issue?