I have the model that has multiple entities. For example I have an order that have CustomerId to foreign key. In the model I have some fields from the order and some fields from the customer entity. When I am saving new record I would like to save customer first, get newly generated identity value and put it to the order table. How to achieve that? I can do that by getting MAX(ID) from the customer table, however I am pretty sure that there is better way to handle that.
This is my controller method:
public ActionResult Create(OrderModels model, FormCollection form)
{
try
{
Customer customer = new Customer() { FirstName = model.FirstName, MiddleName = model.MiddleName, SecondName = model.SecondName, Email = model.Email, PhoneNbr = model.PhoneNbr };
int orderSource = Int32.Parse(form["OrderSourceList"]);
int paymentType = Int32.Parse(form["PaymentTypeList"]);
string warehouseGuid = form["Warehouses"];
ProductLogic productLogic = new ProductLogic();
Product product = productLogic.GetProductByArticle(model.Article);
DateTime now = DateTime.Now;
SalesOrder salesOrder = new SalesOrder()
{
OrderNbr = model.OrderNbr,
OrderSourceId = orderSource,
NpWarehouseRef = new Guid(warehouseGuid),
TTN = model.TTN,
OrderStatusId = 1,
PaymentTypeId = paymentType,
OrderDate = DateTime.Now
};
using (AccountingRepository repository = new AccountingRepository())
{
repository.AddOrUpdate<Customer>(customer);
repository.AddOrUpdate<SalesOrder>(salesOrder);
}
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
AccountingRepository has dispose method
public void Dispose()
{
if (_context != null)
{
_context.SaveChanges();
_context.Dispose();
}
}
My Order class:
using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace Accounting.Entity
{
[Table("SalesOrder", Schema = "dbo")]
public class SalesOrder
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
...
public int? CustomerId { get; set; }
...
public virtual Customer Customer { get; set; }
internal class Config : EntityTypeConfiguration<SalesOrder>
{
public Config()
{
HasRequired(r => r.Customer)
.WithMany(r => r.SalesOrder)
.HasForeignKey(r => r.CustomerId);
}
}
}
}
My Customer class:
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace Accounting.Entity
{
[Table("Customer", Schema = "dbo")]
public class Customer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string SecondName { get; set; }
public string PhoneNbr { get; set; }
public string Email { get; set; }
public virtual HashSet<SalesOrder> SalesOrder { get; set; }
internal class Config : EntityTypeConfiguration<Customer>
{
public Config()
{
}
}
}
}
Two ways:
Save the customer entity, and Entity Framework will back fill the PK property with it's ID. You can then use this for the order entity.
db.Customers.Add(customer);
db.SaveChanges();
order.CustomerId = customer.Id; // has a value now
Just associate the customer with the order via a navigation property. When you save everything Entity Framework works out which relationships need to be saved first and then fills in the appropriate FK ids in the related entities.
order.Customer = customer;
db.Orders.Add(order);
db.SaveChanges(); // customer is saved first and the id is set for order.CustomerId automatically
Related
Hi i have an entity and i am gonna add two tables from database named as country and state.
There is a relation between these two tables based on CountryId.
I used the "Update Model from database ..." to add these two entity types.
I have manually written two classes for these two entity-types given as below:-
public partial class Country
{
//[Key] //[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int CountryID { get; set; }
public string CountryName { get; set; }
}
public partial class State
{
public int StateID { get; set; }
public string StateName { get; set; }
public int CountryID { get; set; }
}
public DbSet<Country> Countries { get; set; }
public DbSet<State> States { get; set; }
Controller to fetch coutries and states :-
public JsonResult GetCountries()
{
List<Country> allCountry = new List<Country>();
using (SunilEntities dc = new SunilEntities())
{
allCountry = dc.Countries.OrderBy(a => a.CountryName).ToList();
}
return new JsonResult { Data = allCountry, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
public JsonResult GetStates(int countryID)
{
List<State> allState = new List<State>();
using (SunilEntities dc = new SunilEntities())
{
allState = dc.States.Where(a => a.CountryID.Equals(countryID)).OrderBy(a => a.StateName).ToList();
}
return new JsonResult { Data = allState, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
But I am getting an error "The entity type Country is not part of the model for the current context".
What should be the exact classes to be used to use these two tables in my controller?
Is there any way I can get automated classes after entity being updated with newer tables?
Under your yourmodel.edmx file there is yourmodel.tt and it generates relevant classes, thus there is no need to write these classes. By using relevant namespace you can use them.
I got the solution change models as below:-
public partial class Country
{
public Country()
{
this.States = new HashSet<State>();
}
public int CountryID { get; set; }
public string CountryName { get; set; }
public virtual ICollection<State> States { get; set; }
}
public partial class State
{
public int StateID { get; set; }
public string StateName { get; set; }
public Nullable<int> CountryID { get; set; }
public virtual Country Country { get; set; }
public virtual State State1 { get; set; }
public virtual State State2 { get; set; }
}
and change controller as given below:-
public JsonResult GetCountries()
{
using (SunilEntities dc = new SunilEntities())
{
var ret = dc.Countries.Select(x => new { x.CountryID, x.CountryName }).ToList();
return Json(ret, JsonRequestBehavior.AllowGet);
}
}
// Fetch State by Country ID
public JsonResult GetStates(int countryID)
{
using (SunilEntities dc = new SunilEntities())
{
var ret = dc.States.Where(x => x.CountryID == countryID).Select(x => new { x.StateID, x.StateName }).ToList();
return Json(ret, JsonRequestBehavior.AllowGet);
}
}
I have a login system that record every time that users login on my website.
I'm using MVC template that gives me a login system.
I had my 'Activity log' working. On the view it shows the data I had on Model, so, I needed to display more data, that were on Users table.
To do that I followed the 3rd answer of this question.
Because I created the model that contains info about others 2 Models (user and activity), the system have created this table on database too (it's empty).
There are some bad pratices here? It's normal, in this situation, the system create the table?
Another question, I do querys on controller, I'm doing it right?
ActivityLogViewModels.cs
using System;
using System.Data.Entity;
namespace AccountSystem.Models
{
public class ActivityLogModels
{
public int ID { get; set; }
public string userID { get; set; }
public DateTime Acess { get; set; }
}
public class ActivityLogViewData
{
public int ID { get; set; }
public int accessID { get; set; }
public string userID { get; set; }
public DateTime Acess { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
}
ActivityLogController.cs
using System;
...
namespace AccountSystem.Controllers
{
public class ActivityLogController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: ActivityLog
public ActionResult Index()
{
IEnumerable<ActivityLogViewData> model = null;
List<ActivityLogViewData> verify = new List<ActivityLogViewData>();
SqlConnection connection = new SqlConnection("xxx");
connection.Open();
SqlCommand command = new SqlCommand("", connection);
command.CommandText = "select dbo.AspNetUsers.Email,dbo.AspNetUsers.UserName,dbo.ActivityLogModels.ID,dbo.ActivityLogModels.Acess,dbo.AspNetUsers.Id from dbo.ActivityLogModels inner join dbo.AspNetUsers on dbo.ActivityLogModels.userID=dbo.AspNetUsers.Id";
SqlDataReader dr = command.ExecuteReader();
while (dr.Read())
{
verify.Add(new ActivityLogViewData { Email = dr[0].ToString(), UserName = dr[1].ToString(), accessID = Convert.ToInt32(dr[2].ToString()), Acess = Convert.ToDateTime(dr[3].ToString()), userID = dr[4].ToString()});
}
connection.Close();
//return View(db.ActivityLog.ToList());
return View(verify);
}
The Page (Create, Edit, Details and delete don't work after my changes)
Table empty
I am just getting into MVC 4 and Entity Framework 5 and want to know if what I am doing is correct?
I have a UserObject and a JobObject, the jobObject has a reference to a User Object.
public class Job
{
public int id { get; set; }
public virtual MyUser User { get; set; }
public JobType JobType { get; set; }
}
When I want to create an instance of the Job I am passing in the query string a parameter UserID, but the Job only deals with an instance of MyUser.
Is the following the correct way to associate the user to the job?
[HttpPost]
public ActionResult Create(Job job, int userid)
{
if (ModelState.IsValid)
{
MyUser staffmember = db.MyUsers.Find(userid);
if (staffmember == null)
{
return View("StaffMemberNotFound");
}
job.User = staffmember;
db.Jobs.Add(job);
db.SaveChanges();
}
}
Or is there a better way to associate the user to the job?
Your way will work but I prefer to simply work with ids if possible.
What I would suggest is that you add a MyUserId property to your Job class (remember to update the database if you are using codefirst):
public class Job
{
public int id { get; set; }
[ForeignKey("User")]
public int MyUserId { get; set: }
public virtual MyUser User { get; set; }
public JobType JobType { get; set; }
}
Then simply populate the MyUserId. You can also change your check to simply check if the id exists as apposed to finding an object and letting EF map that to a class before returning it to you
[HttpPost]
public ActionResult Create(Job job, int userid)
{
if (ModelState.IsValid)
{
if (!db.MyUsers.Any(u => u.Id == userid)
{
return View("StaffMemberNotFound");
}
job.MyUserId = userid;
db.Jobs.Add(job);
db.SaveChanges();
}
}
EF will do the rest of the mapping for you when you next retrieve the record from the database.
Your approach works fine, the only small optmization you could make is not taking the "retrieval hit" of MyUser staffmember = db.MyUsers.Find(userid); since you already have the userid.
I am using ASP.NET MVC 4 and Entity Framework 5.0, and here is my code (different model objects, but same intent as what you are doing).
Note: I let EF generate my model classes by right-clicking on the Models folder and choosing Add->ADO.NET Entity Data Model in VS.NET 2012.
Store.Models.Product
namespace Store.Models
{
using System;
using System.Collections.Generic;
public partial class Product
{
public long Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public System.DateTime DateAdded { get; set; }
public Nullable<long> CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
Store.Models.Category
namespace Store.Models
{
using System;
using System.Collections.Generic;
public partial class Category
{
public Category()
{
this.Products = new HashSet<Product>();
}
public long Id { get; set; }
public string CategoryName { get; set; }
public System.DateTime DateAdded { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
On my Create.cshtml page, I have the User select the CategoryId from the drop-down list. This Category Id is bound to Product.CategoryId. All I do in my method is this:
ProductController
public class ProductController : Controller
{
...
[HttpPost]
public ActionResult Create(Product product)
{
product.DateAdded = DateTime.Now;
if (dbContext != null)
{
dbContext.Products.Add(product);
dbContext.SaveChanges();
}
return RedirectToAction("Index");
}
...
}
Good time of a day!
I have a MVC project with query in controller:
var getPhotos = (from m in db.photos
join n in db.comments on m.id equals n.photoid
where n.ownerName == User.Identity.Name
orderby n.id descending
select new {
m.imgcrop, m.id,
n.commenterName, n.comment
}).Take(10);
How to pass this query to view model, and the model to view.
Spend all evening to find the examples, but cant. Thanks for help!
UPDATED
Full Model Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace photostorage.Models
{
public class GlobalModel
{
public class PhotoViewModel
{
public photos Photos { get; set; }
public profiles Profile { get; set; }
public IQueryable<comments> Comments { get; set; }
public IQueryable<photos> NextPrev { get; set; }
}
public class UserPhotoList
{
public IQueryable<photos> Photos { get; set; }
public profiles Profile { get; set; }
}
public class UserProfileView
{
public IQueryable<photos> Photos { get; set; }
public profiles Profile { get; set; }
}
public class GetLastComments
{
public IQueryable<photos> uPhoto { get; set; }
public IQueryable<comments> uComments { get; set; }
}
}
}
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using photostorage.Models;
namespace photostorage.Controllers
{
public class HomeController : Controller
{
private photostorageEntities db = new photostorageEntities();
public ActionResult Index()
{
if(Request.IsAuthenticated) {
GlobalModel.GetLastComments model = new GlobalModel.GetLastComments();
var getPhotos = (from m in db.photos
join n in db.comments on m.id equals n.photoid
where n.ownerName == User.Identity.Name
select new {
m.imgcrop, m.id,
n.commenterName, n.comment
}).Take(10);
return View("Index_Auth", model);
}else{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View("Index");
}
}
public ActionResult About()
{
return View();
}
}
}
In this case you can make a "view model" that will only be used by your view and not by the rest of your application. Something like the following:
public class CommentsViewModel
{
public int MessageId { get; set; }
public string ImageCrop { get; set; }
public string CommenterName { get; set; }
public string Comment { get; set; }
}
Then change your query like so:
var getPhotos = (from m in db.photos
join n in db.comments on m.id equals n.photoid
where n.ownerName == User.Identity.Name
orderby n.id descending
select new CommentsViewModel {
ImageCrop = m.imgcrop,
MessageId = m.id,
CommenterName = n.commenterName,
Comment = n.comment
}).Take(10).ToList();
Make your view strongly typed to the new class and pass the data to it like so:
View("name_of_your_view", getPhotos);
If you wanted to do this, like you had:
var getPhotos = (from m in db.photos
join n in db.comments on m.id equals n.photoid
where n.ownerName == User.Identity.Name
select new {
m.imgcrop, m.id,
n.commenterName, n.comment
}).Take(10);
You could actually have this without creating a new "CommentsViewModel", but just use what should be the existing tables and models:
var getPhotos = (from m in db.Photos
join n in db.Comments on m.Id equals n.PhotoId
where n.OwnerName == User.Identity.Name
select new {
ImageCrop = m.ImageCrop,
Id = m.Id,
CommenterName = n.CommenterName,
Comment = n.Comment
}).Take(10);
The models would be something like these examples, if you had a foreign key relationship on the Photo.Id to Comments.PhotoId:
public class Photos
{
public int Id { get; set; }
public string ImageCrop { get; set; }
[ForeignKey("PhotoId")]
public virtual Comments Comment { get; set; }
}
public class Comments
{
public int Id { get; set; }
public int PhotoId { get; set; }
public string CommenterName { get; set; }
public string OwnerName { get; set; }
public string Comment { get; set; }
}
Just a note: The models you displayed in your question had none of these columns, yet you were building a query against them. It's best to remember to give a complete picture when asking for help.
I working my way through the Apress "Pro ASP.NET MVC Framework" ( http://www.apress.com/book/view/9781430210078 ) book, and in an example the author creates a link to a db table (as well as a fake repository) using linq like this:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq.Mapping;
namespace DomainModel.Entities
{
[Table(Name = "Products")]
public class Product
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int ProductID { get; set; }
[Column] public string Name { get; set; }
[Column] public string Description { get; set; }
[Column] public decimal Price { get; set; }
[Column] public string Category { get; set; }
public string this[string propName]
{
get {
if ((propName == "Name") && string.IsNullOrEmpty(Name))
return "Please enter a product name";
if ((propName == "Description") && string.IsNullOrEmpty(Description))
return "Please enter a description";
if ((propName == "Price") && (Price < 0))
return "Price must not be negative";
if ((propName == "Category") && string.IsNullOrEmpty(Category))
return "Please specify a category";
return null;
}
}
public string Error { get { return null; } } // Not required }
}
creating an interface:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DomainModel.Entities;
namespace DomainModel.Abstract
{
public interface IProductsRepository
{
IQueryable Products { get; }
void SaveProduct(Product product);
void DeleteProduct(Product product);
}
}
a fake repository (not included) and then a real DB connection repository:-
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DomainModel.Abstract;
using System.Data.Linq;
using DomainModel.Entities;
namespace DomainModel.Concrete
{
public class SqlProductsRepository : IProductsRepository
{
private Table productsTable;
public SqlProductsRepository(string connectionString)
{
productsTable = (new DataContext(connectionString)).GetTable();
}
public IQueryable Products
{
get { return productsTable; }
}
public void SaveProduct(Product product)
{
EnsureValid(product, "Name", "Description", "Category", "Price");
// If it's a new product, just attach it to the DataContext
if (product.ProductID == 0)
productsTable.InsertOnSubmit(product);
else {
// If we're updating an existing product, tell the DataContext
// to be responsible for saving this instance
productsTable.Attach(product);
// Also tell the DataContext to detect any changes since the last save
productsTable.Context.Refresh(RefreshMode.KeepCurrentValues, product);
}
productsTable.Context.SubmitChanges();
}
public void DeleteProduct(Product product)
{
productsTable.DeleteOnSubmit(product);
productsTable.Context.SubmitChanges();
}
private void EnsureValid(IDataErrorInfo validatable, params string[] properties)
{
if (properties.Any(x => validatable[x] != null))
throw new InvalidOperationException("The object is invalid.");
}
}
off to a DB table "Products" with the column name specified and it works nicely. I am using this technique in a real-world app, as it handles the DB access layer nicely, but I need to be able to access several tables from my object. How can I do this - do I need to split my object up into a hierarchy that reflects its tables or can I access more than 1 table from the main object, with other additional objects hanging off it that have their own tables? If so then how do I create the ORM links between the objects and tables?
Cheers
MH
Just add the related tables to your repository interface, just as you have for Product and then create the concrete implementations in your repository class, again just as you have for Product.
I've used the same pattern on my app, I have two repositories, each handling 5-10 tables. There are two distinct groups of tables which are related, hence two repositories.
I would change the SQLRepository constructor thus:
public SqlProductsRepository(string connectionString)
{
DataContext dc = new DataContext(connectionString);
productsTable = dc.GetTable<Product>();
}
You can then extend it easily thus e.g.:
private Table<Order> ordersTable;
public SqlProductsRepository(string connectionString)
{
DataContext dc = new DataContext(connectionString);
productsTable = dc.GetTable<Product>();
ordersTable = dc.GetTable<Order>();
}
IQueryable<Order> Orders
{
get { return from o in ordersTable select o; }
}
EDIT - Answering comment
Here's an example of how to deliver subordinate objects (related tables) via this method:
[Table(Name="Projects")]
public class Project
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public Guid ID { get; set; }
[Column]
public String Name { get; set; }
[Column]
public bool Active { get; set; }
[Association(ThisKey="ID", OtherKey = "ProjectID")]
private EntitySet<ProjectDate> _projectDates = new EntitySet<ProjectDate>();
public IQueryable<ProjectDate> ProjectDates
{
get { return _projectDates.AsQueryable(); }
}
}
And the ProjectDate class for completeness
[Table(Name="ProjectDates")]
public class ProjectDate
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public Guid ID { get; set; }
[Column]
public Guid ProjectID { get; set; }
[Column]
public DateTime TargetDate { get; set; }
[Column(CanBeNull = true)]
public DateTime? ActualDate { get; set; }
[Column(CanBeNull=true, IsDbGenerated = true)]
public DateTime? Created { get; set; }
private EntityRef<Project> _project;
[Association(ThisKey = "ProjectID", Storage = "_project", OtherKey = "ID")]
public Project Project
{
get { return _project.Entity; }
set { _project.Entity = value; ProjectID = _project.Entity.ID; }
}
}