code first database model - asp.net-mvc-3

I'm trying to create a MVC3 application, i'm troubled with EF code first to create DB.
I have this tables: User, Category, Product, Loan.
A User can create none or more Categories.
A User can add none or more Products.
A User can add none or more Loans.
A Category can have one or more Products.
A Category belongs to a User.
A Product can have none or more Loans.
A Product belongs to a User.
A Product is in a Category.
A Loan belongs to a User.
A Loan is added to a Product.
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public virtual ICollection<Category> Categorys { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<Loan> Loans { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public int UserID { get; set; }
public int CategoryID { get; set; }
public virtual User User { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<Loan> Loans { get; set; }
}
public class Loan
{
public int LoanID { get; set; }
public bool LoanStatus { get; set; }
public int UserID { get; set; }
public int ProductID { get; set; }
public virtual User User { get; set; }
public virtual Product Product { get; set; }
}
Have maded the context:
public class BuisnessContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Category> Categorys { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Loan> Loans { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Have added the connectionString:
<add name="BuisnessContext"
connectionString="Data Source=|DataDirectory|Buisness.sdf"
providerName="System.Data.SqlServerCe.4.0"/>
Also a have maded a simple Initializer class:
public class BuisnessInitializer : DropCreateDatabaseIfModelChanges<BuisnessContext>
{
protected override void Seed(BuisnessContext context)
{
var users = new List<User>
{
new User { UserName = "u1"},
new User { UserName = "u2"} };
users.ForEach(s => context.Users.Add(s));
context.SaveChanges();
var categories = new List<Category>
{
new Category { CategoryName = "N1", UserID=1 } };
categories.ForEach(s => context.Categorys.Add(s));
context.SaveChanges();
var products = new List<Product>
{
new Product { ProductName = "N1", UserID = 1, CategoryID = 1 }
};
products.ForEach(s => context.Products.Add(s));
context.SaveChanges();
var loans = new List<Loan>
{
new Loan { LoanStatus = true, UserID = 2, ProductID = 1 }
};
loans.ForEach(s => context.Loans.Add(s));
context.SaveChanges();
}
}
Also i have generate a controller for User to get the users, but when i try to get the Users i received an error like:
Model compatibility cannot be checked because the EdmMetadata type was not included in the model. Ensure that IncludeMetadataConvention has been added to the DbModelBuilder conventions.
I tried to change the Database.SetInitializer<BuisnessContext>(new BuisnessInitializer());
whith Database.SetInitializer<BuisnessContext>(null);
Then i geted the error that table User doesen't exist and i didn't find any table in my APP_DATA folder -> Buisness.mdf
The database was created, but there was any table.
I understand that in my BuisnessContext i must to put some code for One to many or something like this, but i don't know how to do that.
Any help please!

I have found the answer for my question on .net mvc cyclical reference issue with entity
So the answer for me is like this:
modelBuilder.Entity<Product>()
.HasRequired(p => p.User).WithMany(p => p.Products).HasForeignKey(p => p.UserID).WillCascadeOnDelete(false);
modelBuilder.Entity<Product>()
.HasRequired(p => p.Category).WithMany(p => p.Products).HasForeignKey(p => p.CategoryID).WillCascadeOnDelete(false);
modelBuilder.Entity<Loan>()
.HasRequired(l => l.Product).WithMany(l => l.Loans).HasForeignKey(l => l.ProductID).WillCascadeOnDelete(false);
The reason is that here i am making some different path for the same tables like User - Category, User - Category - Product, Category - Product, User - Product - Loan.
Maybe it will be a good answer for others.

Related

Checking grandchildren records to return grand-parent. Linq

I have different roles and each user can have multiple roles. Each role is connected to customer record in different way, e.g. a business analyst has many-to-many relation to project and each customer has many projects; whereas a customer record can have only one project manager associated to it.
public class Customer
{
public CustomerProjectManager ProjectManager { get; set; }
public ICollection<Project> Projects{ get; set; }
...
}
public class Project
{
public ICollection<ProjectBusinessAnalyst> BusinessAnalysts { get; set; }
public ICollection<ProjectDeveloper> ProjectDevelopers { get; set; }
...
}
public class ProjectDeveloper
{
public int Id { get; set; }
public Project Project{ get; set; }
public int ProjectId { get; set; }
public string DeveloperId { get; set; }
public string DeveloperEmail { get; set; }
public string DeveloperName { get; set; }
}
public class CustomerProjectManager
{
public int Id { get; set; }
public ICollection<Customer> Customers { get; set; }
public string ProjectManagerId { get; set; }
public string ProjectManagerEmail { get; set; }
public string ProjectManagerName { get; set; }
public CustomerProjectManager()
{
Customers = new List<Customer>();
}
}
I need to fetch customer records on basis of roles. To explain further, I need to combine multiple customer lists fetched on the basis of different roles assigned to a single user. I am unable to form right linq query.
I have a sample query, mentioned below, which sometimes returns the right records but if I have a new user and no customers are assigned to this user, the query returns all existing customers. Its important for me that all the combination and filtration is done in Iqueryable
Please help!
public async Task<List<Customer>> FetchCustomers(string userId, List<string> userRoles, string userEmail)
{
if (userRoles.Contains("Admin"))
{
customer = _context.Customers;
}
else if (userRoles.Contains("Project Manager") ||
userRoles.Contains("Business Analyst") ||
userRoles.Contains("Developer"))
{
if (userRoles.Contains("Project Manager"))
{
customers = customers.Where(c => c.ProjectManager.ProjectManagerId == userId
|| c.Projects.Any(op =>
op.ProjectsCompleted.Any(assignee =>
assignee.UserId == userId)));
}
if (userRoles.Contains("Business Analyst"))
{
var allPossibleCustomers = _context.Customers.Where(c =>
c.Projects.Any(op => op.BusinessAnalysts.Any(ba => ba.BusinessAnalystId == userId)));
customers = customers?.Union(allPossibleCustomers) ?? allPossibleCustomers;
}
if (userRoles.Contains(Roles.Developer.GetDescription()))
{
var allPossibleCustomers = _context.Customers.Where(c =>
c.Projects.Any(op => op.PREDevDevelopersAssigned.Any(ba => ba.DeveloperId == userId)));
customers = customers?.Union(allPossibleCustomers) ?? allPossibleCustomers;
}
}
var listData = await PagingList<Customer>.CreatePageAsync(customers, page, limit);
return listData;
}
Apparently I was trying to return the wrong list. The linq query is correct.

Cannot implicitly convert type error when building query to populate viewmodel

I wanted to make a simple query to retrieve component data + language data for a specific article and then put that into a viewmodel.
The ProductComponent table is a child-table of Product and the relevant fields in it are the ComponentID and ProductId (foreign key, parentId), so I wanted to link ProductComponents to Product and ProductTranslations where I have all the language specific data, so I tried to make it all in one query to retrieve a list of components for a certain product.
Here is the query:
public IEnumerable<ProductComponents> ListComponents(int productid, string language)
{
var query = (from c in context.ProductComponents
from ct in context.ProductTranslations
where ct.ProductId == c.ComponentId
where ct.ProductLanguage == language
from cp in context.Product
where cp.ProductId == c.ComponentId
where c.ProductId == productid
select new EnumComponents
{
ProductId = c.ComponentId,
Name = ct.ProductName,
SKU = cp.SKU
});
return query;
}
That gives this error, and highlighting the return Query; part as well:
Cannot implicitly convert type 'System.Linq.IQueryable<Artikelhantering.Models.EnumComponents>' to 'System.Collections.Generic.IEnumerable<Artikelhantering.Models.ProductComponents>'. An explicit conversion exists (are you missing a cast?)
Here is most of the data model, based on what I found googling and looking through Stack Overflow the relationships between tables might be the culprit, so I am including most of it.
public class Product
{
[Key]
public int ProductId { get; set; }
[DisplayName("Article nr")]
public string SKU { get; set; }
[DisplayName("Product Category")]
public int ProductCategoriesId { get; set; }
[DisplayName("Alternative Category")]
public int? AdditionalCategoriesId { get; set; }
[DisplayName("Show purchase button?")]
public bool Purchase { get; set; }
[DisplayName("Show Product?")]
public bool ShowProduct { get; set; }
[DisplayName("Picture name")]
public string Picture { get; set; }
[DisplayName("Is reference product?")]
public bool Reference { get; set; }
[DisplayName("Inprice")]
public decimal inPrice { get; set; }
[DisplayName("Additional costs")]
public decimal AddCost { get; set; } /
[DisplayName("Weight in kg")]
public decimal Weight { get; set; }
[DisplayName("Volume in m^3")]
public decimal Volume { get; set; }
[DisplayName("Vat code, use 0")]
public decimal VAT { get; set; }
public virtual IList<ProductTranslations> ProductTranslations { get; set; }
public virtual IList<ProductPrices> ProductPrices { get; set; }
public virtual IList<ProductComponents> ProductComponents { get; set; }
public virtual IList<ProductAccessories> ProductAccessories { get; set; }
public virtual ProductCategories ProductCategories { get; set; }
public virtual ProductCampaigns ProductCampaigns { get; set; }
}
public class ProductTranslations
{
[Key]
public int ProductTranslationsId { get; set; }
public int ProductId { get; set; } // This one links to the Product class
[DisplayName("Language Code")]
public string ProductLanguage { get; set; }
[DisplayName("Description")]
public string Description { get; set; }
[DisplayName("Product Name")]
public string ProductName { get; set; }
[MaxLength(255)]
[DisplayName("Meta Keywords")]
public string MetaKeywords { get; set; }
[MaxLength(255)]
[DisplayName("Meta Description")]
public string MetaDescription { get; set; }
public virtual Product Product { get; set; }
}
public class ProductComponents
{
[Key]
public int ProductComponentsId { get; set; }
public int ProductId { get; set; }
public int ComponentId { get; set; }
public virtual IList<ProductTranslations> ProductTranslations { get; set; }
public virtual Product Product { get; set; }
}
And then I define the relationships between the models like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<ProductCategories>()
.HasMany(x => x.ProductCategoriesTranslations) // Categories has many Translations
.WithRequired(y => y.ProductCategories) // Translations require a category
.HasForeignKey(p => p.ProductCategoriesId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductPrices) // Product has many ProductPricings
.WithRequired(y => y.Product) // ProductPricing has required Product
.HasForeignKey(p => p.ProductId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductTranslations) // Product has many ProductTranslations
.WithRequired(y => y.Product) // ProductTranslations has required Product
.HasForeignKey(p => p.ProductId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductComponents) // Product has many ProductComponents
.WithRequired(y => y.Product) // ProductComponents has required Product
.HasForeignKey(p => p.ProductId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductAccessories) // Product has many ProductAccessories
.WithRequired(y => y.Product) // ProductAccessories has required Product
.HasForeignKey(p => p.ProductId);
}
I am guessing I need to define a proper relationship for ProductComponents to ProductTranslations and Product, but I am not quite sure how, I've tried various ways to create a relationship between ProductComponents -> ProductTranslations but without any success. Ofcourse the issue might well be somehing else.
Looks like I figured it out on my own. Pretty simple and obvious error now that it finally clicked. The key was to rename public IEnumerable<ListComponents> to public IEnumerable<EnumComponents>
It seems logical in retrospect, I should be using the view model here and not the model, because that's what I am trying to populate after all, I can then call the model from inside the view model just fine.
public IEnumerable<EnumComponents> ListComponents(int productid, string language)
{
//return context.ProductComponents.Where(m => m.ProductId == productid);
var query = (from c in context.ProductComponents
where c.ProductId == productid
select new EnumComponents
{
Name = c.ProductTranslations
.Where(i => i.ProductLanguage == language)
.Where(i => i.ProductId == c.ComponentId)
.Select(i => i.ProductName)
.Single(),
SKU = c.Product.SKU,
ProductId = c.ComponentId
});
return (query);
}

Entity FrameWork many-to-many

public class Project
{
public virtual int ID { get; set; }
[Required]
public virtual String Title { get; set; }
public String Definition { get; set; }
public DateTime StartDate { get; set; }
[Required]
public int CreaterID { get; set; }
public virtual ICollection<Status> Status { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
public virtual ICollection<User> Users { get; set; }
public Project()
{
Users = new HashSet<User>();
}
}
public class User
{
public int ID { get; set; }
[DisplayName("Kullanıcı Adı")]
[Required]
[MinLength(5, ErrorMessage = "Kullanıcı Adı En Az 5 Karakter Olmalıdır")]
public string username { get; set; }
[DataType(DataType.Password)]
[DisplayName("Şifre")]
[Required]
[MinLength(3,ErrorMessage="Şifre En Az 3 Karakter Olmalıdır")]
public string password { get; set; }
[Required]
public String Name { get; set; }
[Required]
public String Surname { get; set; }
public int? CreaterID { get; set; }
public int level { get; set; }
public ICollection<Task> Tasks { get; set; }
public ICollection<Project> Projects { get; set; }
public User()
{
Projects = new HashSet<Project>();
}
}
public class TaskDB : DbContext
{
public DbSet<Comment> Comments { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<Situation> Situaitons { get; set; }
public DbSet<Task> Tasks { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>().
HasMany(c => c.Users).
WithMany(p => p.Projects).
Map(
m =>
{
m.MapLeftKey("ProjectId");
m.MapRightKey("UserId");
m.ToTable("ProjectUser");
});
}
}
If I add project , current user added to project users list but project not added current user's projects list
This is my project add code
[HttpPost]
public ActionResult Create(Project proje,Status status)
{
proje.StartDate = DateTime.Now;
proje.Status = new HashSet<Status>();
var user = _db.Users.Single(r=> r.ID == UserRole.ID);
proje.Users.Add(user);
proje.Status.Add(status);
user.Projects.Add(proje);
if (ModelState.IsValid)
{
var projeler = _db.Projects;
projeler.Add(proje);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(proje);
}
I Search this problem's cause I did not find , I want to learn why entity framework add user to project's list but not add project to user's list
Your code to add the new project to the database looks correct and the relationship is most likely stored.
But possibly you don't load the Projects list with a User. If you call...
var project = _db.Projects.Single(p => p.ID == 1);
var users = project.Users; // lazy loading because Users is virtual
...you will see the project's users because they get lazily loaded since the Project.Users property is marked as virtual. If you do the same with a User...
var user = _db.Users.Single(u => u.ID == 1);
var projects = user.Projects; // no lazy loading because Projects is not virtual
...the projects don't get loaded because the User.Projects property is not marked as virtual.
Either mark the property as virtual as well to enable lazy loading for the User.Projects collection:
public virtual ICollection<Project> Projects { get; set; }
Or use eager loading:
var user = _db.Users.Include(u => u.Projects).Single(u => u.ID == 1);

Multiple Parent models for a child model

I'm creating an MVC3 asp.net application using Entity Framework 4 and C#.
I've tried to read up on EF and model binding, lazy loading, etc. But I've hit an obstacle.
I have a User Model. The Store and UserType models can have an ICollection of Users. When I add a User with the Create Action, How do I specify multiple parents?
I think that I only know how to add if there is one parent.
My Models:
public class UserType
{
public virtual int ID { get; set; }
public virtual string UserTypeName { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class Store
{
public virtual int ID { get; set; }
public virtual string StoreName { get; set; }
public virtual Address StoreAddress { get; set; }
public virtual ICollection<Workroom> Workrooms { get; set;}
public virtual ICollection<User> Users { get; set; }
}
public class User
{
public virtual int ID { get; set; }
public virtual string Username { get; set; }
public virtual string Email { get; set; }
public virtual Store Store { get; set; }
public virtual UserType UserType { get; set; }
}
Here is my db context:
public DbSet<Workroom> Workrooms { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Store> Stores { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserType> UserTypes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Store>()
.HasMany(store => store.Workrooms)
.WithRequired(workroom => workroom.Store);
modelBuilder.Entity<Store>()
.HasMany(store => store.Users)
.WithRequired(user => user.Store);
modelBuilder.Entity<UserType>()
.HasMany(usertype => usertype.Users)
.WithRequired(user => user.UserType);
base.OnModelCreating(modelBuilder);
}
Here's my create action:
public ActionResult Create()
{
return View(new User());
}
//
// POST: /Users/Create
[HttpPost]
public ActionResult Create(User newUser)
{
try
{
int storeID = newUser.Store.ID;
var store = _db.Stores.Single(r => r.ID == storeID);
store.Users.Add(newUser);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.InnerException.Message);
return View();
}
}
Do I just add another "Add" call for UserType? for example:
int userTypeID = newUser.UserType.ID
var usertype = _db.UserTypes.Single(s => s.ID == userTypeID)
How would the Entity Framework know that Users has another Parent??? ...do I care?
My hunch is that I should be doing this a different way, more specific and more accurate.
In this case, you probably want to add the user to the Users table, rather than the Stores. Then you assign the StoreID and UserTypeID to the user before you commit.
It looks like you're already setting the StoreID in your UI, are you doing the same for UserType? If so, then just add the user to the users table and you should be good.

many to many mapping in entity framework code first

I'm using Entity Framework 4 CTP5 Code First and I have a model along the lines of:
public class User {
public int UserId { get; set; }
public string Email { get; set; }
public ICollection<Customer> TaggedCustomers { get; set; }
}
public class Customer {
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<User> TaggedBy { get; set; }
}
There is a many to many relationship where a User can 'tag' a Customer and a Customer can be 'tagged' by many users. I have a working DbContext and I can query customers using
var customers = DbContext.Customers.Include(c => c.TaggedBy);
But each customer will have all users that have tagged the customer. How do I restrict the TaggedBy collection to just result with a specifed UserId?
I've tried along the lines of DbContext.Customers.Include(c => c.TaggedBy.Select(x => x.Id == userId)); but that throws an error.
EF Feature CTP5: Fluent API Samples - ADO.NET team blog - Site Home - MSDN Blogs
modelBuilder.Entity<Product>()
.HasMany(p => p.Tags)
.WithMany(t => t.Products)
.Map(m =>
{
m.MapLeftKey(p => p.ProductId, "CustomFkToProductId");
m.MapRightKey(t => t.TagId, "CustomFkToTagId");
});
Code First Mapping Changes in CTP5 - ADO.NET team blog - Site Home - MSDN Blogs
modelBuilder.Entity<Product>()
.HasMany(p => p.SoldAt)
.WithMany(s => s.Products)
.Map(mc => {
mc.ToTable("ProductsAtStores");
mc.MapLeftKey(p => p.Id, "ProductId");
mc.MapRightKey(s => s.Id, "StoreId");
});
Mark your collections as virtual and then you can easily do:
public class User
{
public int UserId { get; set; }
public string Email { get; set; }
public virtual ICollection<Customer> TaggedCustomers { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<User> TaggedBy { get; set; }
}
using(var context = new MyDbContext())
{
var user = context.Users.Single(o => o.UserId == 0);
var customers = user.TaggedCustomers;
}
Results in much cleaner code in my opinion.

Resources