I'm writing an mvc 4 c# .net 4.5 website
I want to create a new company object and register a new user that is linked to that company.
My account model is:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string PhoneNumber { get; set; }
public bool MarketingEmailOptin { get; set; }
public bool isDisabled { get; set; }
public virtual Company CompanyICanEdit { get; set; }
}
If i call the following it adds the user fine but has null for the CompanyICanEdit field:
WebSecurity.CreateUserAndAccount(addCompanyViewModel.User.UserName,
addCompanyViewModel.User.Password,
propertyValues: new
{
FirstName = addCompanyViewModel.User.FirstName,
LastName = addCompanyViewModel.User.LastName,
EmailAddress = addCompanyViewModel.User.EmailAddress,
PhoneNumber = addCompanyViewModel.User.PhoneNumber,
MarketingEmailOptin = addCompanyViewModel.User.MarketingEmailOptin,
isDisabled = false
});
which i would expect as i am not assigning it anything.
i have tried adding (mycompany is a company object):
WebSecurity.CreateUserAndAccount(addCompanyViewModel.User.UserName,
addCompanyViewModel.User.Password,
propertyValues: new
{
FirstName = addCompanyViewModel.User.FirstName,
LastName = addCompanyViewModel.User.LastName,
EmailAddress = addCompanyViewModel.User.EmailAddress,
PhoneNumber = addCompanyViewModel.User.PhoneNumber,
MarketingEmailOptin = addCompanyViewModel.User.MarketingEmailOptin,
isDisabled = false,
CompanyICanEdit = mycompany
});
But i get an error saying it can't match the type.
How do i go about registering the user so that the CompanyICanEdit contains the CompanyId value of mycompany?
Any help will be appreciated. thanks
Never worked out how to do it in 1 go, got round it by the following in the end if anyone has the same problem.
//
// POST: /BusinessManager/ManageCompanies/Add
[HttpPost]
public ActionResult Add(AddCompanyViewModel addCompanyViewModel)
{
if (ModelState.IsValid)
{
// Create company and attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(addCompanyViewModel.User.UserName,
addCompanyViewModel.User.Password,
propertyValues: new
{
FirstName = addCompanyViewModel.User.FirstName,
LastName = addCompanyViewModel.User.LastName,
EmailAddress = addCompanyViewModel.User.EmailAddress,
PhoneNumber = addCompanyViewModel.User.PhoneNumber,
MarketingEmailOptin = addCompanyViewModel.User.MarketingEmailOptin,
isDisabled = false
});
db.Companies.Add(addCompanyViewModel.Company);
var newuser = db.UserProfiles.FirstOrDefault(u => u.UserName == addCompanyViewModel.User.UserName);
if (newuser != null)
{
newuser.CompanyICanEdit = addCompanyViewModel.Company;
db.Entry(newuser).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
ModelState.AddModelError("", "New user wasn't added");
}
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", Mywebsite.Controllers.AccountController.ErrorCodeToString(e.StatusCode));
}
}
return View(addCompanyViewModel);
}
Related
I have a Web API for OData services. I have a lot of table with many relations. Here is some of the table:
MSADDRESSCOUNTRY
public partial class MSADDRESSCOUNTRY
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2214:DoNotCallOverridableMethodsInConstructors")]
public MSADDRESSCOUNTRY()
{
this.MSADDRESSPROVINCEs = new HashSet<MSADDRESSPROVINCE>();
}
public int ID { get; set; }
public string CODE { get; set; }
public string COUNTRYNAME { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MSADDRESSPROVINCE> MSADDRESSPROVINCEs { get; set; }
}
MSADDRESSPROVINCE
public partial class MSADDRESSPROVINCE
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MSADDRESSPROVINCE()
{
this.MSADDRESSDISTRICTs = new HashSet<MSADDRESSDISTRICT>();
}
public int ID { get; set; }
public Nullable<int> COUNTRYID { get; set; }
public string PROVINCENAME { get; set; }
public virtual MSADDRESSCOUNTRY MSADDRESSCOUNTRY { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MSADDRESSDISTRICT> MSADDRESSDISTRICTs { get; set; }
}
MSADDRESSDISTRICT
public partial class MSADDRESSDISTRICT
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MSADDRESSDISTRICT()
{
this.MSADDRESSSUBDISTRICTs = new HashSet<MSADDRESSSUBDISTRICT>();
}
public int ID { get; set; }
public Nullable<int> PROVINCEID { get; set; }
public string DISTRICTNAME { get; set; }
public virtual MSADDRESSPROVINCE MSADDRESSPROVINCE { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MSADDRESSSUBDISTRICT> MSADDRESSSUBDISTRICTs { get; set; }
}
I create DTO object model for every table with the property is the same with Database object model.
I want the client can use $expand keyword to get child data and/or parent data.
For MSADDRESSCOUNTRY I need to write the code like this.
[EnableQuery(MaxExpansionDepth = 4)]
public IQueryable<MsAddressCountryObject> Get()
{
return db.MSADDRESSCOUNTRies.Select(c => new MsAddressCountryObject
{
ID = c.ID,
CODE = c.CODE,
COUNTRYNAME = c.COUNTRYNAME,
MSADDRESSPROVINCEs = c.MSADDRESSPROVINCEs.Select(data => new MsAddressProvinceObject()
{
ID = data.ID,
COUNTRYID = data.COUNTRYID,
PROVINCENAME = data.PROVINCENAME,
MSADDRESSCOUNTRY = new MsAddressCountryObject()
{
ID = data.MSADDRESSCOUNTRY.ID,
CODE = data.MSADDRESSCOUNTRY.CODE,
COUNTRYNAME = data.MSADDRESSCOUNTRY.COUNTRYNAME,
},
MSADDRESSDISTRICTs = data.MSADDRESSDISTRICTs.Select(dist => new MsAddressDistrictObject()
{
ID = dist.ID,
PROVINCEID = dist.PROVINCEID,
DISTRICTNAME = dist.DISTRICTNAME,
})
})
});
}
For MSADDRESSPROVINCE I need to write the code like this.
[EnableQuery(MaxExpansionDepth = 4)]
public IQueryable<MsAddressProvinceObject> Get()
{
return db.MSADDRESSPROVINCEs.Select(data => new MsAddressProvinceObject()
{
ID = data.ID,
COUNTRYID = data.COUNTRYID,
PROVINCENAME = data.PROVINCENAME,
MSADDRESSCOUNTRY = new MsAddressCountryObject()
{
ID = data.MSADDRESSCOUNTRY.ID,
CODE = data.MSADDRESSCOUNTRY.CODE,
COUNTRYNAME = data.MSADDRESSCOUNTRY.COUNTRYNAME,
},
MSADDRESSDISTRICTs = data.MSADDRESSDISTRICTs.Select(dist => new MsAddressDistrictObject()
{
ID = dist.ID,
PROVINCEID = dist.PROVINCEID,
DISTRICTNAME = dist.DISTRICTNAME
})
});
}
That code works fast. But if I add/change/remove column, I have to modify the controller manually, one by one for all controller. For example, if I want to add geological coordinate in MSADDRESSDISTRICT, I have to change the code in Country Controller, Province Controller and District Controller.
So I decide to create extension method like this.
public static MsAddressCountryObject ToDTO(this MSADDRESSCOUNTRY data)
{
return new MsAddressCountryObject()
{
ID = data.ID,
CODE = data.CODE,
COUNTRYNAME = data.COUNTRYNAME,
};
}
public static IQueryable<MsAddressCountryObject ToDTO(this IEnumerable<MSADDRESSCOUNTRY datas)
{
return datas.Select(country =
{
var obj = country?.ToDTO();
obj.MSADDRESSPROVINCEs = country.MSADDRESSPROVINCEs?.ToDTO();
return obj;
}).AsQueryable();
}
public static MsAddressProvinceObject ToDTO(this MSADDRESSPROVINCE data)
{
return new MsAddressProvinceObject()
{
ID = data.ID,
COUNTRYID = data.COUNTRYID,
PROVINCENAME = data.PROVINCENAME,
MSADDRESSCOUNTRY = data.MSADDRESSCOUNTRY?.ToDTO()
};
}
public static IQueryable<MsAddressProvinceObject ToDTO(this IEnumerable<MSADDRESSPROVINCE datas)
{
return datas.Select(province =
{
var obj = province?.ToDTO();
obj.MSADDRESSDISTRICTs = province.MSADDRESSDISTRICTs.ToDTO();
return obj;
}).AsQueryable();
}
public static MsAddressDistrictObject ToDTO(this MSADDRESSDISTRICT data)
{
return new MsAddressDistrictObject()
{
ID = data.ID,
PROVINCEID = data.PROVINCEID,
DISTRICTNAME = data.DISTRICTNAME,
MSADDRESSPROVINCE = data.MSADDRESSPROVINCE?.ToDTO()
};
}
public static IQueryable<MsAddressDistrictObject ToDTO(this IEnumerable<MSADDRESSDISTRICT datas)
{
return datas.Select(district =
{
var obj = district?.ToDTO();
obj.MSADDRESSSUBDISTRICTs = district.MSADDRESSSUBDISTRICTs?.ToDTO();
return obj;
}).AsQueryable();
}
And the controller just like this.
[EnableQuery(MaxExpansionDepth = 4)]
public IQueryable<MsAddressCountryObject Get()
{
return db.MSADDRESSCOUNTRies.ToDTO()
}
And that makes the performance really bad. I think the extension is making a lot of memory allocation or some thing that make the result not being delivered directly to the client.
My goal is to create the code easy to maintain, and the performance not drop significantly.
I have many relation in other table. I want the $expand works without write all parent/child Select statement manually and one by one.
I have try to not calling ToDTO() from all the extension method. The result is the performance is fast. But I lost all the relation or I need to write the parent/child Select statement for all method.
Any suggestion will help.
Thanks.
I have an update method:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
[Column(TypeName = "jsonb")]
}
public async Task<bool> Update(int id, UserRequest user)
{
var result = await _context.Users.Where(x => x.Id == id).FirstOrDefaultAsync();
var cities = new List<string>();
cities.Add("New York");
cities.Add("London");
result = new Users()
{
Id = result.Id,
FirstName = user.FirstName,
LastName = user.LastName,
City = JsonSerializer.Serialize(cities)
};
// _context.Entry(result).Property(x => x.City).IsModified = true;
await _context.SaveChangesAsync();
return true;
}
FirstName and LastName change is reflected in db but not in City db.
_context.Entry(result).Property(x => x.City).IsModified = true;
As a solution, generally used the Entry feature, but the Entry property throws an error for me:
An exception occurred while calculating the database error page content.
Skipping display of the database error page.
System.InvalidOperationException:
StatusCode cannot be set because the response has already started.
I don't understand the cause of the issue but the following change solved my issue.
result = new Users()
{
Id = result.Id,
FirstName = user.FirstName,
LastName = user.LastName,
City = JsonSerializer.Serialize(cities)
};
to
result.FirstName = user.FirstName;
result.LastName = user.LastName ;
result.City = JsonSerializer.Serialize(cities);
One way to fix this problem is to turn lazy loading off ..... if i do not want to turn off lazy loading, is there other solution?
my action is look like this
[ResponseType(typeof(AspNetUsers))]
public IHttpActionResult Get(string id)
{
var user = db.AspNetUsers.FirstOrDefault(s => s.UserName == id);
if (user == null)
return null; //":کاربر نامعتبر";
JsonResult js = new JsonResult();
js.Data = user;
js.ContentEncoding = Encoding.UTF8;
js.ContentType = "Application/json";
js.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
//var returnUser = user.UserName + user.Email + user.FirstName + user.LastName +
// user.AspNetRoles.Select(s => s.Name);
return Ok(js);/// or return ok(user)
}
You are mixing MVC and Web Api concepts and object.
JsonResult is an MVC derived ActionResult object while IHttpActionResult is associated with Web API.
Also, if you don't want to return all the data then don't. Construct a new object result model and return only what is needed.
[ResponseType(typeof(UserModel))]
public IHttpActionResult Get(string id) {
var user = db.AspNetUsers.FirstOrDefault(s => s.UserName == id);
if (user == null)
return NotFound();
var data = new UserModel {
UserName = user.UserName,
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
Roles = user.AspNetRoles.Select(s => s.Name).ToArray()
//...other properties you want to return
};
return Ok(data);
}
public class UserModel {
public string UserName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string[] Roles { get; set; }
}
i fix my Problem whit little change in Nkosi answer .....
[ResponseType(typeof(UserModel))]
public IHttpActionResult Get(string id)
{
var user = db.AspNetUsers.Include(a=>a.AspNetRoles).Where(s => s.UserName == id).FirstOrDefault();
if (user == null)
return NotFound();
var data = new UserModel
{
UserName = user.UserName,
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
Roles = user.AspNetRoles.Select(s => s.Name).ToArray()
//...other properties you want to return
};
return Ok(data);
}
public class UserModel
{
public string UserName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string[] Roles { get; set; }
}
thanks alot
I try to update my Products table but i can't because throw an error.
This is my hardcode controller:
[HttpPost]
public ActionResult EditProduct(ProductsViewModel productViewModel)
{
TechStoreEntities context = new TechStoreEntities();
Product newProduct = new Product
{
ProductId = productViewModel.ProductId,
Name = productViewModel.Name,
Price = productViewModel.Price,
Discount = productViewModel.Discount,
Quantity = productViewModel.Quantity,
Size = productViewModel.Size,
Description = productViewModel.Description,
ProducerName = productViewModel.ProducerName,
PaymentMethods = productViewModel.PaymentMethods,
CategoryID = productViewModel.CategoryID,
SubcategoryID = productViewModel.SubcategoryID,
IsNew = productViewModel.IsNew,
IsEnable = productViewModel.IsEnable
};
context.Entry(newProduct).State = EntityState.Modified;
context.SaveChanges();
ViewBag.CategoryID = new SelectList(context.Categories.Where(c => c.SubCategoryID == null), "CategoryID", "Name");
ViewBag.SubcategoryID = new SelectList(context.Categories.Where(c => c.SubCategoryID != null), "CategoryID", "Name");
return RedirectToAction("Products");
}
This is a model:
public class ProductsViewModel
{
public int ProductId { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public int Discount { get; set; }
public int Quantity { get; set; }
public string Size { get; set; }
public string Description { get; set; }
public string ProducerName { get; set; }
public string PaymentMethods { get; set; }
public bool IsNew { get; set; }
public bool IsEnable { get; set; }
public string Category { get; set; }
public int CategoryID { get; set; }
public string Subcategory { get; set; }
public int SubcategoryID { get; set; }
public DateTime? CreateDate { get; set; }
}
I use strongly typed view:
#model MyTechStore.Models.ProductsViewModel
I add in a view:
#Html.HiddenFor(model => model.ProductId)
When i start app and enter some data to update existing data and press save, throw me exception:
System.Data.Entity.Infrastructure.DbUpdateConcurrencyException
When i debugging i saw that only the ProductId was 0. Everything else is OK. I tested with scaffolding controller but there is OK. I want to use view model, not as scaffolding controller use the model from my database.
Can someone tell me where i'm wrong?
My GET method:
public ActionResult EditProduct(int? id)
{
TechStoreEntities context = new TechStoreEntities();
ProductsManipulate product = new ProductsManipulate();
ProductsViewModel editProduct = product.EditProduct(id);
ViewBag.CategoryID = new SelectList(context.Categories.Where(c => c.SubCategoryID == null), "CategoryID", "Name");
ViewBag.SubcategoryID = new SelectList(context.Categories.Where(c => c.SubCategoryID != null), "CategoryID", "Name");
return View(editProduct);
}
And my data access layer:
public ProductsViewModel EditProduct(int? id)
{
TechStoreEntities context = new TechStoreEntities();
Product dbProduct = context.Products.Find(id);
ProductsViewModel product new ProductsViewModel
{
Name = dbProduct.Name,
Price = dbProduct.Price,
Quantity = dbProduct.Quantity,
CategoryID = dbProduct.CategoryID,
SubcategoryID = dbProduct.SubcategoryID,
IsNew = dbProduct.IsNew
};
return product;
}
You need to populate ProductId in ProductsViewModel
public ProductsViewModel EditProduct(int? id)
{
TechStoreEntities context = new TechStoreEntities();
Product dbProduct = context.Products.Find(id);
ProductsViewModel product = new ProductsViewModel()
{
// You need this line to pass the value to View
ProductId = dbProduct.ProductId,
Name = dbProduct.Name,
Price = dbProduct.Price,
Quantity = dbProduct.Quantity,
CategoryID = dbProduct.CategoryID,
SubcategoryID = dbProduct.SubcategoryID,
IsNew = dbProduct.IsNew
};
return product;
}
What that error is saying, is that EF tried to update a Product with those fields, but the it returned 0 RowCount therefore it knows something went wrong.
As you have mentioned before, the ProductId is 0, meaning you probably don't have a Product with that ID, and therefore when EF tries to update it, the row count is 0, which causes EF to throw a DbUpdateConcurrencyException.
You need to make sure your Id is populated if you want to an update an existing product.
Otherwise if you want an upsert (Update or Insert) you first need to check if a record exists for your given ProductId and if it does, do update, otherwise do insert.
Ok, I am doing a project with entity framework 6. I have my class laid out. When I try to add the information to the database; it gives me following errors:
The best overloaded method match for 'System.Data.Entity.DbSet<img_site_codefi.DAL.DefaultConnection>.Add(img_site_codefi.DAL.DefaultConnection)' has some invalid arguments
Argument 1: cannot convert from 'AnonymousType#1' to 'img_site_codefi.DAL.DefaultConnection'
Here is my controller:
public ActionResult Contact(customer cust)
{
try
{
if (ModelState.IsValid)
{
cust.Tele_comp();
saveIntoDb(cust); // database
SendMail(cust); // mail sender
return RedirectToAction("Submited", "Home");
}
return null;
}
catch (DataException)
{
return View(cust);
}
}
private void saveIntoDb(customer cust)
{
using (var cust_In = new DefaultConnection())
{
var customer = new {fname = cust.fname,lname = cust.lname, tele = cust.tele, email = cust.email, reasn = cust.reasn };
//cust_In.customers.Add(customer); //HERE IS THE ERROR!!!
cust_In.SaveChanges();
}
}
and here is the model:
[Key]
[] // how to assign a number automatically
public int Cust_Id { get; set; }
[Required(ErrorMessage = "first name is required!")]
[Display(Name = "First name")]
public string fname { get; set; }
[Display(Name = "Last name")]
public string lname { get; set; }
[Required(ErrorMessage = "area code is required!")]
[StringLength(3)]
[RegularExpression(#"^[0-9]{3,}$", ErrorMessage = "Minimum 3 numbers required & contain only numbers")]
[Display(Name = "Telephone")]
public string tel_area { get; set; }
[Required(ErrorMessage = "first three numbers are required!")]
[StringLength(3)]
[RegularExpression(#"^[0-9]{3,}$", ErrorMessage = "Minimum 3 numbers required & contain only numbers")]
public string fir_thr_tel { get; set; }
[Required(ErrorMessage = "last four numbers are required!")]
[StringLength(4)]
[RegularExpression(#"^[0-9]{4,}$", ErrorMessage = "Minimum 4 numbers required & contain only numbers")]
public string lst_fur_tel { get; set; }
[Required(ErrorMessage = "E-mail is required!")]
[RegularExpression("^[a-zA-Z0-9_\\.-]+#([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "E-mail is not valid")]
[Display(Name = "Email")]
public string email { get; set; }
[Required(ErrorMessage = "A reason is required!")]
[Display(Name = "Reason")]
public string reasn { get; set; }
public string tele { get; set; }
Also, how do I generate a number automatically for the "Cust_Id" like a database do with the sql code IDENTITY or computed.
You have 2 problems. First, this line is wrong:
var customer = new {fname = cust.fname,lname = cust.lname, tele = cust.tele, email = cust.email, reasn = cust.reasn };
You are creating an anonymous type instead of a customer object. Try this instead:
var customer = new customer
{
fname = cust.fname,
lname = cust.lname,
tele = cust.tele,
email = cust.email,
reasn = cust.reasn
};
Secondly your context DefaultConnection is wrong and contains this:
public DbSet<DefaultConnection> customers { get; set; }
You are creating a DbSet of your context class instead of customers. This should be:
public DbSet<customer> customers { get; set; }
You cannot add Anonymous typed class or Dynamic to a DbSet so you need to create an instance class of customer in order to be added to your DbSet.
public ActionResult Contact(Customer cust)
{
try
{
if (ModelState.IsValid)
{
cust.Tele_comp();
saveIntoDb(cust); // database
SendMail(cust); // mail sender
return RedirectToAction("Submited", "Home");
}
return null;
}
catch (DataException)
{
return View(cust);
}
}
private void saveIntoDb(Customer cust)
{
using (var cust_In = new DbContext())
{
var customer = new Customer {fname = cust.fname,lname = cust.lname, tele = cust.tele, email = cust.email, reasn = cust.reasn };
cust_In.Customers.Add(customer); //HERE IS THE ERROR!!!
cust_In.SaveChanges();
}
}
Also your DbContext.cs class should have this instead of your code:
public DbSet<Customer> Customers { get; set; }
For the generation of primary key you should use this:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
Make sure you try this tutorial first:
http://msdn.microsoft.com/en-us/data/jj572366.aspx