Why wont my changes persist to db after I click save.
Product Repository:
public class ProductRepository
{
NorthwindDataContext context = new NorthwindDataContext();
public IList<EditableProduct> All()
{
return (from product in context.Products
select new EditableProduct {
ProductID = product.ProductID,
ProductName = product.ProductName,
UnitPrice = product.UnitPrice.HasValue ? product.UnitPrice.Value : default(decimal),
UnitsInStock = product.UnitsInStock.HasValue ? product.UnitsInStock.Value : default(int),
Discontinued = product.Discontinued,
LastSupply = DateTime.Today
}).ToList();
}
public EditableProduct One(Func<EditableProduct, bool> predicate)
{
return All().Where(predicate).FirstOrDefault();
}
public void Update(EditableProduct product)
{
EditableProduct target = One(p => p.ProductID == product.ProductID);
if (target != null)
{
target.ProductName = product.ProductName;
target.UnitPrice = product.UnitPrice;
target.UnitsInStock = product.UnitsInStock;
target.Discontinued = product.Discontinued;
target.LastSupply = product.LastSupply;
}
}
public void Insert(EditableProduct product)
{
product.ProductID = All().OrderByDescending(p => p.ProductID).First().ProductID + 1;
All().Insert(0, product);
}
public void Delete(EditableProduct product)
{
EditableProduct target = One(p => p.ProductID == product.ProductID);
if (target != null)
{
All().Remove(target);
}
}
}
Controller:
public partial class GridController : Controller
{
ProductRepository productRepository = new ProductRepository();
public ActionResult EditingBatch()
{
return View();
}
[HttpPost]
[GridAction]
public ActionResult _SelectBatchEditing()
{
return View(new GridModel(productRepository.All()));
}
[HttpPost]
[GridAction]
public ActionResult _SaveBatchEditing([Bind(Prefix = "inserted")]IEnumerable<EditableProduct> insertProducts,
[Bind(Prefix = "updated")]IEnumerable<EditableProduct> updatedProducts,
[Bind(Prefix = "deleted")]IEnumerable<EditableProduct> deletedProducts)
{
if (insertProducts != null)
{
foreach (var product in insertProducts)
{
productRepository.Insert(product);
}
}
if (updatedProducts != null)
{
foreach (var product in updatedProducts)
{
var target = productRepository.One(p => p.ProductID == product.ProductID);
if (target != null)
{
target.ProductName = product.ProductName;
target.UnitPrice = product.UnitPrice;
target.UnitsInStock = product.UnitsInStock;
target.LastSupply = product.LastSupply;
target.Discontinued = product.Discontinued;
productRepository.Update(target);
}
}
}
if (deletedProducts != null)
{
foreach (var product in deletedProducts)
{
productRepository.Delete(product);
}
}
return View(new GridModel(productRepository.All()));
}
}
You seem to be using the code from our sample application which indeed does not update the database. It merely updates the data in memory so every single user browsing the demos sees only his changes.
To make it work you need to update the database. The actual implementation depends on the framework you are using for data access. For example for Linq To SQL you should use the SubmitChanges method. You can check this code library project which updates the underlying database.
Related
I have a batch of users defined with that model.
I need to iterate inside Manager nesting while manager is not null and create Departments entities with parents relation. I have an example of code what I already have, so I post it here.
What I already have:
public class AdUserModel
{
public string UserName { get; set; }
public AdUserModel Manager { get; set; }
}
...
List<UserDepartment> userDepartmentsToAdd = new List<UserDepartment>();
List<Department> newDeps = new List<Department>();
RecurseDepartments(adUser);
var userDepartment = new UserDepartment
{
User = user,
PositionName = adUser.PositionName,
Department = newDeps.FirstOrDefault(x => x.Name == adUser.DepartmentName),
IsHeadUser = user.SubordinateUsers?.Any() ?? false
};
userDepartmentsToAdd.Add(userDepartment);
void RecurseDepartments(AdUserModel model)
{
var department = new Department();
var existDep = newDeps.FirstOrDefault(x => x.Name ==
model.DepartmentName);
if (existDep == null)
{
department.Name = model.DepartmentName;
}
if (model.Manager is not null)
{
if (newDeps.FirstOrDefault(x => x.Name == model.Manager.DepartmentName) == null)
{
var parentDepartment = new Department
{
Name = model.Manager.DepartmentName
};
department.ParentDepartment = existDep ?? department;
if (existDep == null)
{
newDeps.Add(department);
}
newDeps.Add(parentDepartment);
}
if (model.Manager.DepartmentName != model.DepartmentName)
{
RecurseDepartments(model.Manager);
}
}
}
Thanks for any help in advance, being stuck here for some reason.
Error 1 Inconsistent accessibility: parameter type 'WingtipToys.Logic.ShoppingCartUpdate[]' is less accessible than method 'WingtipToys.Logic.ShoppingCartActions.UpdateShoppingCartDatabase(string, WingtipToys.Logic.ShoppingCartUpdate[])' C:\Users\Toshiba\documents\visual studio 2013\Projects\WingtipToys\WingtipToys\Logic\ShoppingCartActions.cs 93 21 WingtipToys
I don't understand the error please help.
The code where error is showing is in the first line of the given code UpdateShoppingCartDatabase.
namespace WingtipToys.Logic
{
public class ShoppingCartActions : IDisposable
{
public string ShoppingCartId { get; set; }
private ProductContext _db = new ProductContext();
public const string CartSessionKey = "CartId";
public void AddToCart(int id)
{
// Retrieve the product from the database.
ShoppingCartId = GetCartId();
var cartItem = _db.ShoppingCartItems.SingleOrDefault(
c => c.CartId == ShoppingCartId && c.ProductId == id);
if (cartItem == null)
{
// Create a new cart item if no cart item exists.
cartItem = new CartItem
{
ItemId = Guid.NewGuid().ToString(),
ProductId = id,
CartId = ShoppingCartId,
Product = _db.Products.SingleOrDefault
(p => p.ProductID == id),
Quantity = 1,
DateCreated = DateTime.Now
};
_db.ShoppingCartItems.Add(cartItem);
}
else
{
// If the item does exist in the cart,
// then add one to the quantity.
cartItem.Quantity++;
}
_db.SaveChanges();
}
public void Dispose()
{
if (_db != null)
{
_db.Dispose();
_db = null;
}
}
public string GetCartId()
{
if (HttpContext.Current.Session[CartSessionKey] == null)
{
if (!string.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name))
{
HttpContext.Current.Session[CartSessionKey] = HttpContext.Current.User.Identity.Name;
}
else
{
Guid tempCartId = Guid.NewGuid();
HttpContext.Current.Session[CartSessionKey] = tempCartId.ToString();
}
}
return HttpContext.Current.Session[CartSessionKey].ToString();
}
public List<CartItem> GetCartItems()
{
ShoppingCartId = GetCartId();
return _db.ShoppingCartItems.Where(
c => c.CartId == ShoppingCartId).ToList();
}
public decimal GetTotal()
{
ShoppingCartId = GetCartId();
// Multiply product price by quantity of that product to get
// the current price for each of those products in the cart.
// Sum all product price totals to get the cart total.
decimal? total = decimal.Zero;
total = (decimal?)(from cartItems in _db.ShoppingCartItems
where cartItems.CartId == ShoppingCartId
select (int?)cartItems.Quantity * cartItems.Product.UnitPrice).Sum();
return total ?? decimal.Zero;
}
public ShoppingCartActions GetCart(HttpContext context)
{
using (var cart = new ShoppingCartActions())
{
cart.ShoppingCartId = cart.GetCartId();
return cart;
}
}
public void UpdateShoppingCartDatabase(String cardId, ShoppingCartUpdate []
CartItemUpdates)
{
using (var db = new WingtipToys.Models.ProductContext())
{
try
{
int CartItemCount = CartItemUpdates.Count();
List<CartItem> myCart = GetCartItems();
foreach (var cartItem in myCart)
{
//Iterate through all rows within shopping cart list
for(int i = 0; i < CartItemCount; i++)
{
if (cartItem.Product.ProductID == CartItemUpdates[i].ProductId)
{
if (CartItemUpdates[i].PurchaseQuantity < 1 ||
CartItemUpdates[i].RemoveItem == true)
{
RemoveItem(CartId, cartItem.ProductId);
}
else
{
UpdateItem(CartId, cartItem.ProductId,
CartItemUpdates[i].PurchaseQuantity);
}
}
}
}
}
I think you'll need to provide more information to get a definite answer. Like what a ShoppingCartUpdate is and how its defined.
As the error message suggests, you have a difference in accessibility. This could mean that your ShoppingCartUpdate is declared internal (example) so it can't be used in a public method as something outside of the assembly is allowed to call UpdateShoppingCartDatabase, but not allowed to access ShoppingCartUpdate as it's marked internal (again example, need more information)
I am overriding the SaveChanges() method so that I can use the ChangeTracker to get the modified properties of an entity. I need to return a Dictionary<string, string> of the modified properties so that in my controller I can call the Audit Service. So far my SaveChanges() methods looks like:
public override int SaveChanges()
{
var changeInfo = ChangeTracker.Entries()
.Where(t => t.State == EntityState.Modified)
.Select(t => new {
Original = t.OriginalValues.PropertyNames.ToDictionary(pn => pn, pn => t.OriginalValues[pn]),
Current = t.CurrentValues.PropertyNames.ToDictionary(pn => pn, pn => t.CurrentValues[pn])
});
Dictionary<string, string> modifiedProperties = new Dictionary<string, string>();
foreach (var item in changeInfo)
{
foreach (var origValue in item.Original)
{
var currValue = item.Current[origValue.Key];
if ((origValue.Value != null && currValue != null) && !origValue.Value.Equals(currValue))
{
modifiedProperties.Add(origValue.Key, string.Format("Old Value: {0}, New Value: {1}", origValue.Value, currValue));
}
}
}
return base.SaveChanges();
}
Is there a way to access the modifiedProperties dictionary in my controller so I can pass that to my service?
Controller:
if (validator.IsValid())
{
_workRequestRepo.Save(workRequest);
_auditService.Log(UserId, modelId, "Work Order", "Edit", modifiedProperties);
}
You don't have to return the modified properties, you can tackle your audit procedures inside the SaveChanges method. This is an example:
public MyContainer(IUserProvider userProvider) {
_userProvider = userProvider;
}
public override int SaveChanges() {
var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));
if (entities.Any()) {
User currentUser = _userProvider.GetCurrent();
if (currentUser == null)
throw new Exception("Current user is undefined.");
DateTime time = DateTime.Now;
foreach (var entity in entities) {
BaseEntity baseEntity = (BaseEntity)entity.Entity;
if (entity.State == EntityState.Added) {
baseEntity.Created = time;
baseEntity.CreatedBy = currentUser;
}
baseEntity.Modified = time;
baseEntity.ModifiedBy = currentUser;
// get and store the changed properties of the entity here
// ....
var changeInfo = entities.Select(t => new { Original = t.OriginalValues.PropertyNames.ToDictionary(pn => pn, pn => originalValues[pn]), Current = t.CurrentValues.PropertyNames.ToDictionary(pn => pn, pn => t.CurrentValues[pn]);
}
}
return base.SaveChanges();
}
Using IOC I would imagine you have something like:
(This is assume Auditing and not Revisioning)
Presentation:
public PersonController
{
private IPersonBL _personBL;
public PersonController(IPersonBL personBL)
{
_personBL = personBL
}
public ActionResult SavePerson(PersonVM model)
{
// if ModelState etc etc
var person = Mapper.Map<Person>(model);
_personBL.Save(person)
}
}
Business Layer
public PersonBL : IPersonBL
{
private IAuditService _auditService;
private IPersonRepo _personRepo;
public PersonBL(IAuditService auditService,
IPersonRepo personRepo)
{
_auditService = auditService;
_personRepo = personRepo;
}
public void Save(Person person)
{
PersonDTO personDTO = Mapper.Map<PersonDTO>(person);
var result = _personRepo.Save(personDTO);
if (result.Count > 0)
{
_auditService.Audit(result);
}
}
}
Data Layer
public PersonDL : IPersonDL
{
private DbContext _context;
public PersonDL(DbContext dbContext)
{
_context = dbContext;
}
public IDictionary<string, string> Save(PersonDTO person)
{
var result = new Dictionary<string, string>()
_context.Persons.Add(person);
var saveCount = _context.SaveChanges();
if (saveCount > 0)
{
// Do Object Tracking
// Populate result;
}
return result;
}
}
I have trouble with the DHTMLX scheduler specifically around recurring events.
I have tried to follow the documentation found here http://blog.scheduler-net.com/post/recurring-events-calendar-view-asp-net.aspx. However can't seem to get it working.
I can create the basic scheduler without any issues. The issue I now have is that any event that gets created won't save to the DB. This is what I have so far.
Model:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[DHXJson(Alias = "id")]
public int Id { get; set; }
[DHXJson(Alias = "text")]
public string Description { get; set; }
[DHXJson(Alias = "start_date")]
public DateTime StartDate { get; set; }
[DHXJson(Alias = "end_date")]
public DateTime EndDate { get; set; }
[DHXJson(Alias="event_length")]
public int event_length { get; set; }
[DHXJson(Alias = "rec_type")]
public string rec_type { get; set; }
[DHXJson(Alias = "event_pid")]
public int event_pid { get; set; }
Controller:
public ActionResult Save(int? id, FormCollection actionValues)
{
var action = new DataAction(actionValues);
ApplicationDbContext data = new ApplicationDbContext();
try
{
var changedEvent = (Appointment)DHXEventsHelper.Bind(typeof(Appointment), actionValues);
//operations with recurring events require some additional handling
bool isFinished = deleteRelated(action, changedEvent, data);
if (!isFinished)
{
switch (action.Type)
{
case DataActionTypes.Insert:
data.Appointment.Add(changedEvent);
if (changedEvent.rec_type == "none")//delete one event from the serie
action.Type = DataActionTypes.Delete;
break;
case DataActionTypes.Delete:
changedEvent = data.Appointment.SingleOrDefault(ev => ev.Id == action.SourceId);
data.Appointment.Remove(changedEvent);
break;
default:// "update"
var eventToUpdate = data.Appointment.SingleOrDefault(ev => ev.Id == action.SourceId);
DHXEventsHelper.Update(eventToUpdate, changedEvent, new List<string>() { "id" });
break;
}
}
data.SaveChanges();
action.TargetId = changedEvent.Id;
}
catch
{
action.Type = DataActionTypes.Error;
}
return (new AjaxSaveResponse(action));
}
protected bool deleteRelated(DataAction action, Appointment changedEvent, ApplicationDbContext context)
{
bool finished = false;
if ((action.Type == DataActionTypes.Delete || action.Type == DataActionTypes.Update) && !string.IsNullOrEmpty(changedEvent.rec_type))
{
// context.Recurrings.DeleteAllOnSubmit(from ev in context.Recurrings where ev.event_pid == changedEvent.id select ev);
}
if (action.Type == DataActionTypes.Delete && (changedEvent.event_pid != 0 && changedEvent.event_pid != null))
{
// Recurring changed = (from ev in context.Recurrings where ev.id == action.TargetId select ev).Single();
// changed.rec_type = "none";
finished = true;
}
return finished;
}
Any help or ideas?
Try changing the change save method return value to "ContentResult". Also Look into your ApplicationDbContext and see if you can pull some hard-coded db values from that table when your index loads. Here is a copy of mine. I had the same problems until I used linq to classes to create the model/EF and my context is based on that. I was having the same issue when I created my own "light-weight" interface because I didn't want to use EF.
public ContentResult Save(int? id, FormCollection actionValues)
{
var action = new DataAction(actionValues);
var context = new SchedulerDataContext();
Int64 source_id = Int64.Parse(actionValues["id"]);
try
{
var changedDelEvent = (Delivery)DHXEventsHelper.Bind(typeof(Delivery), actionValues);
var changedRecEvent = (Recurring)DHXEventsHelper.Bind(typeof(Recurring), actionValues);
//operations with recurring events require some additional handling
bool isFinished = deleteRelated(action, changedRecEvent, context);
if (!isFinished)
{
switch (action.Type)
{
case DataActionTypes.Insert:
context.Recurrings.InsertOnSubmit(changedRecEvent);
context.SubmitChanges();
break;
case DataActionTypes.Delete:
changedRecEvent = context.Recurrings.SingleOrDefault(d => d.id == source_id);
if (changedRecEvent != null)
{
context.Recurrings.DeleteOnSubmit(changedRecEvent);
}
context.SubmitChanges();
break;
default:// "update"
var eventToUpdate = context.Deliveries.SingleOrDefault(d => d.DeliveryID == source_id);
DHXEventsHelper.Update(eventToUpdate, changedRecEvent, new List<string> { "id" });
if (eventToUpdate != null && eventToUpdate.RouteID != changedRecEvent.id)
{
var routeToUpdate = context.Routes.SingleOrDefault(d => d.RouteID == changedRecEvent.id);
eventToUpdate.Route = routeToUpdate;
}
context.SubmitChanges();
break;
}
action.TargetId = changedRecEvent.id;
}
}
catch
{
action.Type = DataActionTypes.Error;
}
return (new AjaxSaveResponse(action));
}
The recurring extension (dhtmlxscheduler_recurring.js) doesn't recognize the DHXJson Alias annotations that you are using on your entity class properties (extremely frustrating). Therefore, you must name your entity class columns/properties exactly how the dhtmlxscheduler_recurring.js is expecting them, even though the base scheduler API gives you the option for custom naming using the DHXJson alias annotations.
In my MedicalProductController I am trying to convert a MedicalProduct (model class) named "product" into IEnumerable<MedicalProduct> so it can be passed to GetMedicalProductViewModelList, which will return an IEnumerable<MedicalProductViewModel> (viewModel class).
I am trying to keep SQL calls to a minimum. as per advice of other stackoverflow members.
But "product" fails to turn into IEnumerable<MedicalProduct> in my MedicalProductController Class
MedicalProductController
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();
// other CRUD code omitted for brevity.
// =============
// Edit HttpGet
// =============
public ActionResult Edit(int id = 0)
{
MedicalProduct product = _db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
var productList = product as IEnumerable<MedicalProduct>;
var viewModelList = GetMedicalProductViewModelList(productList);
return View(viewModelList);
}
// =============
// Edit HttpPost
// =============
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(MedicalProduct product)
{
if (ModelState.IsValid)
{
_db.Entry(product).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
var productList = product as IEnumerable<MedicalProduct>;
var viewModel = GetMedicalProductViewModelList(productList);
return View(viewModel);
}
// =====================
// Mapper Class Helper
// =====================
public IEnumerable<MedicalProductViewModel> GetMedicalProductViewModelList(IEnumerable<MedicalProduct> productList)
{
var brandList = _db.Brands.ToArray();
var mapper = new MedicalProductMapper();
return mapper.MapMedicalProductViewModel(productList, brandList);
}
And just in case it is helpful, here is the mapping class:
MedicalProductMapper
public class MedicalProductMapper
{
// =====================
// Model to ViewModel
// =====================
public IEnumerable<MedicalProductViewModel> MapMedicalProductViewModel(IEnumerable<MedicalProduct> medicalProductList, IEnumerable<Brand> brandList)
{
var brandSelectListItem = brandList.Select(b => new SelectListItem()
{
Text = b.Name,
Value = b.Name
});
var viewModelList = medicalProductList.Select(p => new MedicalProductViewModel()
{
BrandID = p.BrandID,
BrandName = brandList.SingleOrDefault(b => b.ID == p.BrandID).Name,
BrandSelectListItem = brandSelectListItem,
ID = p.ID,
Price = p.Price,
Name = p.Name
});
return viewModelList;
}
// =====================
// ViewModel to Model
// =====================
public MedicalProduct MapMedicalProduct(MedicalProductViewModel VM)
{
var model = new MedicalProduct()
{
Name = VM.Name,
Price = VM.Price,
BrandID = VM.BrandID
};
return model;
}
}
Instead of
var productList = product as IEnumerable<MedicalProduct>;
Try
var productList = new List<MedicalProduct> { product };
You can't cast to IEnumerable<MedicalProduct> if it's not an IEnumerable, what I did in this example is first create a new List of type MedicalProduct and then add the product to the List.
List is one of the types that implement IEnumerable