after an earlier question i'm stil struggling with EF Code-First;
I have 3 (in this example, in practice there are more) where 1 table uses multiple Id's for accessing other tables.
I have 2 problems
1: the Id's for shipping and delivery aren't set (remain '0') when saving to the database.
2: when using DBMigrations an index is created twice for the RecordId
.Index(t => t.RecordId),
.Index(t => t.RecordId);
code example:
Record Class:
public class Record
{
public Record()
{
Shipping = new Shipping();
Delivery = new Delivery();
}
public int RecordId { get; set; }
public int ShippingId { get; set; }
public int DeliveryId { get; set; }
public virtual Shipping Shipping { get; set; }
public virtual Delivery Delivery { get; set; }
}
Shipping Class:
public class Shipping
{
public int ShippingId { get; set; }
public string ShippingName { get; set; }
public virtual Record Record { get; set; }
}
Delivery Class:
public class Delivery
{
public int DeliveryId { get; set; }
public String DeliveryText { get; set; }
public virtual Record Record { get; set; }
}
context:
public class Context : DbContext
{
public DbSet<Record> Records { get; set; }
public DbSet<Shipping> Shippings { get; set; }
public DbSet<Delivery> Deliveries { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Record>()
.HasRequired(m => m.Shipping)
.WithRequiredDependent(x => x.Record)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Record>()
.HasRequired(m => m.Delivery)
.WithRequiredDependent(x => x.Record)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
Main program (method):
using (Context context = new Context())
{
var model = context.Records.Create();
var shipping = model.Shipping;
shipping.ShippingName = "TestContext";
var delivery = model.Delivery;
delivery.DeliveryText = "customText";
context.Entry(model).State = EntityState.Added;
context.SaveChanges();
}
Main program (second try)
using (Context context = new Context())
{
var model = context.Records.Create();
model.Shipping = context.Shippings.Create();
var shipping = model.Shipping;
shipping.ShippingName = "TestContext";
model.Delivery = context.Deliveries.Create();
var delivery = model.Delivery;
delivery.DeliveryText = "customText";
context.Entry(model).State = EntityState.Added;
context.SaveChanges();
}
TO avoid the extra index, don't specify the key fields in your record class.
To get the default Identity behaviour name the key fields Id
public class Record
{
public Record()
{
Shipping = new Shipping();
Delivery = new Delivery();
}
public int Id { get; set; }
public virtual Shipping Shipping { get; set; }
public virtual Delivery Delivery { get; set; }
}
Related
As the title says for some reason my database is not being created when specifying CreateDatabaseIfNotExists works fine with drop always and drop if model changes. The database is not created I get an error when in my view because the data is empty.
DBContext
public class SchemaDBContext : DbContext
{
public SchemaDBContext()
{
Database.SetInitializer(new SchemaDBInitializer());
}
public DbSet<UserRole> UserRoles { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<SalesStaff> SalesStaffs { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<CreditCard> CreditCards { get; set; }
public DbSet<Enquiry> Enquiries { get; set; }
public DbSet<Trip> Trips { get; set; }
public DbSet<TripImage> TripImages { get; set; }
public DbSet<Coach> Coaches { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<Review> Reviews { get; set; }
public DbSet<CartItem> CartItems { get; set; }
public DbSet<ShoppingCart> ShoppingCarts { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
public DbSet<Order> Orders { get; set; }
}
Initializer
public class SchemaDBInitializer : CreateDatabaseIfNotExists<SchemaDBContext>
{
protected override void Seed(SchemaDBContext context)
{
var customers = generateCustomers();
customers.ForEach(s => s.ShoppingCart = new ShoppingCart());
customers.ForEach(s => context.Customers.Add(s));
context.SaveChanges();
var salesStaffs = generateSalesStaffs();
salesStaffs.ForEach(s => context.SalesStaffs.Add(s));
context.SaveChanges();
var customerUsers = customers.Select(u => u.User).ToList();
var salesStaffUsers = salesStaffs.Select(u => u.User).ToList();
var userRoles = new List<UserRole>
{
new UserRole()
{
Role = "Customer",
Users = customerUsers
},
new UserRole()
{
Role = "SalesStaff",
Users = salesStaffUsers
}
};
userRoles.ForEach(s => context.UserRoles.Add(s));
context.SaveChanges();
var trips = generateTrips();
trips.ForEach(s => context.Trips.Add(s));
context.SaveChanges();
var tickets = generateTickets(customers, trips);
tickets.ForEach(s => context.Tickets.Add(s));
context.SaveChanges();
var reviews = generateReviews(customers, trips);
reviews.ForEach(s => context.Reviews.Add(s));
context.SaveChanges();
var enquiries = generatEnquiries(customers, salesStaffs);
enquiries.ForEach(s => context.Enquiries.Add(s));
context.SaveChanges();
var orders = generateOrders(customers);
orders.ForEach(order => context.Orders.Add(order));
context.SaveChanges();
}
}
The seed method is not being called by initializer.
My question is: I have two tables room and reservation . They are connected many to many from table RoomReservation.
public class Room
{
public int Id { get; set; }
public double CostNight { get; set; }
public virtual ICollection<Reservation> Reservation { get; set; }
}
public class Reservation
{
public int Id { get; set; }
public string DateOfEntry { get; set; }
public int NumberOfNights { get; set; }
public virtual ICollection<Room> Room { get; set; }
}
On My DbContext I configurated this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Reservation>().HasMany(c => c.Room)
.WithMany(s => s.Reservation)
.Map(t => t.MapLeftKey("RoomId")
.MapRightKey("ReservationId")
.ToTable("RoomReservation"));
}
After button pressing I save data into database:
public ActionResult Reservation(int number, string date, int roomid)
{
var reserv = new Reservation
{
NumberOfNights = number,
DateOfEntry = date
};
db.Reservation.Add(reserv);
db.SaveChanges();
}
How can I save data into table RoomReservation?
You need to retrieve the instances of Room from the database:
List<Room> selectedRooms = db.Rooms.Where(r => r.RoomId == roomid).ToList();
var reserv = new Reservation
{
NumberOfNights = number,
DateOfEntry = date,
Room = selectedRooms
};
db.Reservation.Add(reserv);
db.SaveChanges();
How to map foreign keys from two different table to one table in fluent Api?
My two model is like
public class Customer
{
[Key]
public string Userid { get; set; }
public string PassWord { get; set; }
public bool premium { get; set; }
}
public class Roles
{
[Key]
public string Name { get; set; }
public string Description { get; set; }
}
And 3rd table which has primary key of above table as foreign key?
public class CustomerRoles
{
public string RoleName { get; set; }
public string UserId { get; set; }
}
How to map in Fluent Api?
public class Customer
{
[Key]
public string Userid { get; set; }
public string PassWord { get; set; }
public bool premium { get; set; }
public ICollection<CustomerRole> CustomerRoles { get; set; }
}
public class Role
{
[Key]
public string Name { get; set; }
public string Description { get; set; }
public ICollection<CustomerRole> CustomerRoles { get; set; }
}
public class CustomerRole
{
public string RoleName { get; set; }
public string UserId { get; set; }
public Role Role { get; set; }
public Customer Customer { get; set; }
}
public class AppContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Role> Roles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Customer>().HasMany(c => c.CustomerRoles).WithRequired(cr => cr.Customer);
modelBuilder.Entity<Role>().HasMany(r => r.CustomerRoles).WithRequired(cr => cr.Role);
modelBuilder.Entity<CustomerRole>().HasKey(cr => new { cr.RoleName, cr.UserId });
}
}
PS: Class name should not be plural, it can confuse with array property.
update how to use it
static void Main(string[] args)
{
using (var ctx = new AppContext())
{
Customer customer = new Customer { Userid = "A" };
ctx.Customers.Add(customer);
Role role1 = new Role { Name = "Role1" };
ctx.Roles.Add(role1);
Role role2 = new Role { Name = "Role2" };
ctx.Roles.Add(role2);
customer.CustomerRoles = new[]
{
new CustomerRole { Customer = customer, Role = role1 },
new CustomerRole { Customer = customer, Role = role2 },
};
ctx.SaveChanges();
}
}
Im having trouble linking my loaned items to my Library for each customer. It does it fine when it goes through the "AddToLibrary" method but when it comes to retreiving it, the medialibrary is empty and the query in the IEnumerable<Item> ItemsOnLoan method is returning null. This is a very basic ASP.NET MVC 4 application and im very new to this so its probably something silly ive missed out.
I just want to be able to add an item to the loaned items table, have the list of loaned items for each customer appear in their personal Library (defined in model) and then retreive the list of their items. Below is all the code and I am using a code first approach. Thank you :)
Model
public class Customer
{
public int Id { get; set; }
public string ForeName { get; set; }
public string SurName { get; set; }
public Address address { get; set; }
public string Email { get; set; }
public string Telephone { get; set; }
public string Mobile { get; set; }
public List<LoanedItem> Library { get; set; }
public Customer()
{
if (Library == null || Library.Count == 0)
{
Library = new List<LoanedItem>();
}
}
public IEnumerable<Item> ItemsOnLoan
{
get
{
var items = (from i in Library
where i.Customer.Id == this.Id
select i).OfType<item>();
return items;
}
}
}
Loaned Item model
public class LoanedItem
{
public int? Id { get; set; }
public Customer Customer { get; set; }
public MediaItem Item { get; set; }
}
ItemController --> adding to library method
public ActionResult AddToLibrary(int id)
{
Item libraryItem = db.Items.Find(id);
Customer c = db.Customers.Find(1);
LoanedItem newLoanGame = new LoanedItem()
{
Customer = c,
Item = libraryItem
};
db.LoanedItems.Add(newLoanGame);
db.SaveChanges();
return RedirectToAction("Index");
}
Customer Controller
public ActionResult ViewProfile(int id = 1)
{
Customer c = db.Customers.Find(id);
if (c == null)
{
return HttpNotFound();
}
return View(c);
}
public ActionResult GetLibraryItems(int id = 1)
{
var items = db.Customers.Find(id).ItemsOnLoan;
return View(items);
}
Context
public class LibraryContext : DbContext
{
public DbSet<Address> Addresses { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<LoanedItem> LoanedItems { get; set; }
public DbSet<Item> Items { get; set; }
public LibraryContext()
: base("LbContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new CustomerConfiguration());
modelBuilder.Configurations.Add(new LoanedItemConfiguration());
modelBuilder.Entity<Item>();
modelBuilder.Entity<Address>();
base.OnModelCreating(modelBuilder);
}
}
Assuming that Proxy generation is enabled try this:
public class Customer
{
public int Id { get; set; }
public string ForeName { get; set; }
public string SurName { get; set; }
public Address address { get; set; }
public string Email { get; set; }
public string Telephone { get; set; }
public string Mobile { get; set; }
public virtual ICollection<LoanedItem> ItemsOnLoan { get; set; }
public Customer()
{
}
}
using this to acccess:
public ActionResult GetLibraryItems(int id = 1)
{
var customer = db.Customers.Find(id);
if (customer != null)
{
var items = customer.ItemsOnLoan;
return View(items);
}
//handle not found or throw an exception
throw new Exception();
}
follow this link for more information on Proxies and Lazy Loading.
I am using EF code first approach with fluent api. I am having one registration form in my application where in registering candidate can select multiple options from dropdown(Interested In Dropdown on Sign-Up form) that has a predefined set of options (which may increase in future but the chances are very rare). When the user submits the form I want to save this records to database. So I created following entities.
Participant Class where the registering candidates information will be saved
public class Participant
{
public Participant()
{
Interests = new Collection<Interest>();
}
[Key, ForeignKey("User")]
public int Id { get; set; }
[DisplayName("First Name")]
[StringLength(50, ErrorMessage = "First name cannot be more than 50 characters")]
[Required(ErrorMessage = "You must fill in first name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
[StringLength(50, ErrorMessage = "Last name cannot be more than 50 characters")]
[Required(ErrorMessage = "You must fill in last name")]
public string LastName { get; set; }
[Required(ErrorMessage = "You must indicate your full birthday")]
[DisplayName("Birthday")]
[DataType(DataType.DateTime)]
public DateTime BirthDate { get; set; }
[DisplayName("Gender")]
[Required(ErrorMessage = "You must select gender")]
public int Gender { get; set; }
public string Address { get; set; }
public int CountryId { get; set; }
public Country Country { get; set; }
[DisplayName("Zip code")]
[StringLength(10, ErrorMessage = "Zip code cannot be more than 10 characters")]
public string ZipCode { get; set; }
public string Mobile { get; set; }
public string PhotoUrl { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Interest> Interests { get; set; }
public string MedicalConditions { get; set; }
}
Interest Class from where the Interested In dropdown on Sign-up form will get populate The user can select multiple options from the Interested In dropdown
Interest Class
public class Interest
{
public Interest()
{
Participants = new Collection<Participant>();
}
public int Id { get; set; }
public string InterestName { get; set; }
public virtual ICollection<Participant> Participants { get; private set; }
}
To hold each participants interest I created a ParticipantInterests table in DB with following schema. ParticipantInterests Id (PK) ParticipantId (FK from Participants table) InterestId (FK Interests table)
I added public virtual ICollection Participants { get; set; } in Interest model and
public virtual ICollection Interests { get; set; } in Participant model to form Many-To-Many association.
My Data Context class is as follows
public class STNDataContext : DbContext
{
public DbSet<Participant> Participants { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Country> Countries { get; set; }
public DbSet<Interest> Interests { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<SecurityQuestion> SecurityQuestions { get; set; }
public DbSet<Tour> Tours { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Participant>()
.HasKey(p => p.Id);
modelBuilder.Entity<User>()
.HasOptional(u => u.Participant)
.WithRequired();
modelBuilder.Entity<Participant>()
.HasMany(p => p.Interests)
.WithMany(i => i.Participants)
.Map(m =>
{
m.ToTable("ParticipantInterests");
m.MapLeftKey("ParticipantId");
m.MapRightKey("InterestId");
});
modelBuilder.Entity<User>().HasRequired(u => u.Role);
modelBuilder.Entity<Participant>().HasRequired(p => p.Country);
modelBuilder.Entity<User>().HasOptional(u => u.SecurityQuestion);
}
public virtual void Commit()
{
base.SaveChanges();
}
Controller Action Code
public virtual ActionResult Register(StudentRegisterViewModel studentRegisterViewModel)
{
if (ModelState.IsValid)
{
if (_userService.IsUserExists(studentRegisterViewModel.Participant.User) == false)
{
studentRegisterViewModel.Participant.User.Username = studentRegisterViewModel.Username;
studentRegisterViewModel.Participant.User.Email = studentRegisterViewModel.Email;
studentRegisterViewModel.Participant.User.DateCreated = DateTime.Now;
studentRegisterViewModel.Participant.User.Id = 3;
studentRegisterViewModel.Participant.User.IsApproved = false;
studentRegisterViewModel.Participant.User.RoleId = 2;
studentRegisterViewModel.Participant.CountryId = 1;
var interests = new List<Interest>();
foreach (var interestItem in studentRegisterViewModel.SelectedInterests)
{
var interest = new Interest { Id = interestItem };
interest.Participants.Add(studentRegisterViewModel.Participant);
interests.Add(interest);
studentRegisterViewModel.Participant.Interests.Add(interest);
}
studentRegisterViewModel.Participant.Interests = interests;
_participantService.CreatParticipant(studentRegisterViewModel.Participant);
var user = _userService.GetUser(studentRegisterViewModel.Participant.User.Username);
}
}
studentRegisterViewModel.Gender =
Enum.GetNames(typeof(Gender)).Select(
x => new KeyValuePair<string, string>(x, x.ToString(CultureInfo.InvariantCulture)));
studentRegisterViewModel.Interests = _interestService.GetAllInterests();
return View(studentRegisterViewModel);
}
Participant Repository (DAL)
public class ParticipantRepository : Repository<Participant>, IParticipantRepository
{
public ParticipantRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{
}
}
Participant Service (BLL)
public class ParticipantService : IParticipantService
{
private readonly IParticipantRepository _participantRepository;
private readonly IUnitOfWork _unitOfWork;
public ParticipantService(IParticipantRepository participantRepository, IUnitOfWork unitOfWork)
{
this._participantRepository = participantRepository;
this._unitOfWork = unitOfWork;
}
public void CreatParticipant(Participant participant)
{
_participantRepository.Add(participant);
_unitOfWork.Commit();
}
}
Database Factory
public class DatabaseFactory : Disposable, IDatabaseFactory
{
private STNDataContext _stnDataContext;
public DatabaseFactory()
{
Database.SetInitializer<STNDataContext>(null);
}
public STNDataContext Get()
{
return _stnDataContext ?? (_stnDataContext = new STNDataContext());
}
protected override void DisposeCore()
{
if (_stnDataContext != null)
_stnDataContext.Dispose();
}
}
Unit of Work Class
public class UniOfWork : IUnitOfWork
{
private readonly IDatabaseFactory _databaseFactory;
private STNDataContext _stnDataContext;
public UniOfWork(IDatabaseFactory databaseFactory)
{
this._databaseFactory = databaseFactory;
}
public STNDataContext StnDataContext
{
get { return _stnDataContext ?? (_stnDataContext = _databaseFactory.Get()); }
}
public void Commit()
{
StnDataContext.Commit();
}
}
When I try to Create Participant I get following error.
Cannot insert the value NULL into column 'InterestName', table 'StudyTourNetworkDB.dbo.Interests'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated.
Ideally as per my thinking it should insert Participant Information in Participants table and Participants Interests in ParticipantsInterests table. But it is trying to insert record in Interests table also which should not happen. Please help me resolve this problem. I may be doing wrong by creating many-to-many association.
Thanks
Note : I could understand the problem as Interests collection does not get added / attach to context but I could not find out how to add Interest collection to the same context with repository pattern and unit of work.
Please provide me the solutions. Thanks in advance
You are correct in that your Interest objects are being re-added, because the copies held in your model are not being tracked by EF and therefore it thinks they are new. Instead, you will need to look up the versions from your repository, and add those instead.
Instead of:
var interests = new List<Interest>();
foreach (var interestItem in studentRegisterViewModel.SelectedInterests)
{
var interest = new Interest { Id = interestItem };
interest.Participants.Add(studentRegisterViewModel.Participant);
interests.Add(interest);
studentRegisterViewModel.Participant.Interests.Add(interest);
}
studentRegisterViewModel.Participant.Interests = interests;
Try something like:
// Look up the actual EF entities which match your selected items. You'll
// probably need to adapt this to make it work
var selectedInterestIds = studentRegisterViewModel.SelectedInterests.Select(i => i.Id);
var interests = _interestService.GetAllInterests().Where(i => selectedInterestIds.Contains(i.Id));
studentRegisterViewModel.Participant.Interests = interests;
Note that with a many-to-many relationship, you don't need to set both sides - in your example you were filling in the Participant field of the Interest entity - this will be set automatically by EF since you're adding it to the Interests property of the Participant.