So I have a model that has a Salesman and a Region. The salesman belongs to a region.
Salesman.cs
public class Salesman : Employee
{
public Salesman()
{
this.Invoices = new List<Invoice>();
}
public int? RegionId { get; set; }
[ForeignKey("RegionId")]
public virtual Region Region { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
Salesman inherits Employee with stores basic name, address, etc data - which is not relevant.
Region.cs
public class Region
{
public Region()
{
this.Salesmen = new List<Salesman>();
}
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int? SalesManagerId { get; set; }
[ForeignKey("SalesManagerId")]
public virtual Salesman SalesManager { get; set; }
public virtual ICollection<Salesman> Salesmen { get; set; }
}
The issue I am having is that Region.Salesmen is not being filled by EF like it has in other projects.
The Region populates fine in the Salesman.Region property.
Stuff tried and in the project
Lazy-loading is on (I have explicitly enabled it)
Renaming property names ie. Salesmen -> Salesmans
The database has the correct schema
Changing ICollection to ISet
I am possibly thinking that it might have to do with a naming convention like Salesman -> Salesmen as opposed to Salesman -> Salesmans
Thanks in advance.
So I solved it by adding an attribute to the ICollection<Salesman> Salesmen property.
Now my model looks like this...
public class Region
{
public Region()
{
this.Salesmen = new List<Salesman>();
}
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int? SalesManagerId { get; set; }
[ForeignKey("SalesManagerId")]
public virtual Salesman SalesManager { get; set; }
[InverseProperty("Region")]
public virtual ICollection<Salesman> Salesmen { get; set; }
}
Don't ask me why it works, and why EF couldn't link it up without a little help.. but it does.
Related
I have following RecipeModel, IngredientModel and RecipePartModel classes which represent the DTO classes for the frontend user:
public class RecipeModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public IEnumerable<RecipePartModel> RecipeParts { get; set; }
}
public class IngredientModel
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class RecipePartModel
{
public Guid Id { get; set; }
public IngredientModel Ingredient { get; set; }
public string Unit { get; set; }
public decimal Quantity { get; set; }
}
Here are my entity classes:
public class Recipe : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public virtual IEnumerable<RecipePart> RecipeParts { get; set; }
}
public class Ingredient : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public int Amount { get; set; }
public virtual IEnumerable<RecipePart> RecipeParts { get; set; }
}
public class RecipePart : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public Ingredient Ingredient { get; set; }
public Recipe Recipe { get; set; }
public string Unit { get; set; }
public decimal Quantity { get; set; }
}
My question is - how can I map the Recipe to RecipeModel using AutoMapper? I tried something like this but I assume it is bad, because it just join all the RecipeParts for the whole database, am I correct?
public class DomainProfile : Profile
{
public DomainProfile()
{
CreateMap<Ingredient, IngredientModel>().ReverseMap();
CreateMap<Recipe, RecipeModel>()
.ForMember(x => x.RecipeParts, opt => opt.MapFrom(src => src.RecipeParts));
}
}
To answer your question about how to use AutoMapper to map a type to another type, there are many ways of doing this. Documentation is here: http://docs.automapper.org/en/stable/Getting-started.html.
I wrote a console app and got it working in the quickest way I know possible using your code. When I debug this, and check inside recipeModel, it references a list of RecipePartModels with a single RecipePartModel. Inside that RecipePartModel, it references an IngredientModel.
static void Main(string[] args)
{
var profile = new DomainProfile();
Mapper.Initialize(cfg => cfg.AddProfile(profile));
var recipe = new Recipe
{
RecipeParts = new List<RecipePart>
{
new RecipePart()
{
Ingredient = new Ingredient()
}
}
};
var recipeModel = Mapper.Map<Recipe, RecipeModel>(recipe);
Console.ReadKey();
}
To answer your concern about getting all recipes from the database, if you're using Entity Framework, it depends on if you have lazy loading turned on. Lazy loading ensures that, when you get a recipe from the database, the recipe parts will not be loaded. They will only be loaded when you access the recipe part directly later on in the program flow. Lazy loading is turned on by default so this is the default behaviour. If you turn it off, you've enabled eager loading which loads all recipe parts and in turn their ingredient.
This might help: http://www.entityframeworktutorial.net/lazyloading-in-entity-framework.aspx.
There is nothing bad about this mapping. In fact you don't even need the ForMember call as this is the default convention. The mapping will simply convert each element in the entity child collection to a corresponding model object.
Of course, whether you load your entities in an efficient manner is another matter. If you load a large amount of Recipe entities, and lazy load the RecipeParts collections for each, you will have a major "SELECT N+1" problem. But this is not the fault of AutoMapper.
So my teammates and I are building a website that aggregates textbook prices from different textbook websites (we aren't dynamically getting the prices from the websites themselves, but for the purposes of the school project we are in, we are just randomly entering them in).
So I've got two tables in my database Book1 - a "Books" table and a "Prices" table. I have a foreign key in my Prices table (screenshot of design view) that relates back to my Books table (screenshot of design view).
The ISBN field of our Prices table is a foreign key to the ISBN field of our Books table.
We believe we've set everything else up correctly but when we run it, it returns an exception saying:
The property 'ISBN' cannot be configured as a navigation property. The
property must be a valid entity type and the property should have a
non-abstract getter and setter. For collection properties the type
must implement ICollection where T is a valid entity type.
Here are all the relevant classes.
Book.cs
public partial class Book
{
[Key, Required]
[StringLength(14)]
public string ISBN { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
public string YearPublished { get; set; }
public virtual Price Price { get; set; }
}
Price.cs
public partial class Price
{
[Key, Required]
public int PriceID { get; set; }
[ForeignKey("ISBN")]
public string ISBN { get; set; }
public decimal? AmazonPrice { get; set; }
public decimal? BarnesAndNoblePrice { get; set; }
public decimal? CheggPrice { get; set; }
public decimal? SecondAndCharlesPrice { get; set; }
public decimal? AlibrisPrice { get; set; }
public decimal? ThriftBooksPrice { get; set; }
public decimal? ValoreBooksPrice { get; set; }
public virtual Book Book { get; set; }
public virtual IEnumerable<Book> Books { get; set; }
}
MyModel.cs
public class MyModel : DbContext
{
public MyModel()
: base("name=DefaultConnection") { }
public virtual DbSet<Book> Books { get; set; }
public virtual DbSet<Price> Prices { get; set; }
}
HomeController.cs
public ActionResult Index()
{
var model = new IndexVM();
var ctx = new MyModel();
foreach (var bk in ctx.Books)
{
model.Books.Add(bk);
}
return View(model);
}
We've been told by our professor that it has something to do with the IEnumerable class, but we've tried every combination in the book and we still can't get it to work.
Any and all help will be appreciated.
In Price.cs
[ForeignKey("ISBN")]
public string ISBN { get; set; }
should read
[ForeignKey("Book")]
public string ISBN { get; set; }
Additionally in Book.cs I think you need somewhere to store the cross reference to Price
[ForeignKey("Price")]
public int PriceId { get; set; }
this question describes the same issue Foreign Key Annotation in MVC
Forewarning: I know approximately nothing when it comes to MVC/Entity Framework/Linq queries.
I'm having an issue when I try to query the database. Here's the query I'm using:
int? UserId = db.StudentModel
.Where(c => c.UserName == certUserName)
.Select(c => c.UserId)
.FirstOrDefault();
When it searches the database, it successfully retrieves the UserId.
The problem is that I then use the following query:
CompletionsModel student = db.Completions.Find(UserId);
When I do this, it throws an inner exception that states
{"Invalid column name 'UserProfile_UserId'."}
The weird thing is that when I go to my code and mouse over the 'db' part of the command to see what data it's holding, it has CourseModel, StudentModel, and Completions (though the model's actual filename is CompletionsModel - is that a clue?), so it seems like they're linked properly.
Here's the code for my three models and the database context.
CompletionsModel (UserProfile is white text in my code; not sure why it's teal here Same with UserId and CompletionDate):
[Table("Completion")]
public class CompletionsModel
{
[Key]
public int UserId { get; set; }
public string PRD_NUM { get; set; }
public DateTime CompletionDate { get; set; }
public virtual CourseModel PRD { get; set; }
public virtual StudentModel UserProfile { get; set; }
}
CourseModel:
[Table("PRD")]
public class CourseModel
{
[Key]
public string PRD_NUM { get; set; }
public string PRD_TIT { get; set; }
//because any number of students can be enrolled in one course
public virtual ICollection<CompletionsModel> CompletionsModel { get; set; }
}
StudentModel:
[Table("UserProfile")]
public class StudentModel
{
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<CompletionsModel> CompletionsModel { get; set; }
}
DBContext:
public class ClassContext : DbContext
{
public ClassContext()
: base("DefaultConnection")
{
}
public DbSet<StudentModel> StudentModel { get; set; }
public DbSet<CompletionsModel> Completions { get; set; }
public DbSet<CourseModel> CourseModel { get; set; }
}
And finally, an image of my database layout - maybe this will help things along, too:
I'm too at the beginning of Entity Framework, but what does irritate me is, that you have kind of foreign key relationship between Completion and UserProfile, without really defining, a int column as foreign key in your Completion Table.
You could try to change
public virtual StudentModel UserProfile { get; set; }
to something like
public virtual int StudentId { get; set; }
[ForeignKey("StudentId")]
public virtual StudentModel UserProfile { get; set; }
The same for PRD
But maybe a change in your database is not what you want.
What you could also do, is to remove these lines
public virtual CourseModel PRD { get; set; }
public virtual StudentModel UserProfile { get; set; }
Hope that helps.
EDIT:
I guess the problem is, that you are missing the ForeignKey Attribute at your UserProfile property in your Completions table.
So use
[ForeignKey("UserId")]
public virtual StudentModel UserProfile { get; set; }
instead of
public virtual StudentModel UserProfile { get; set; }
if the UserIds are representing the same user
I have a relatively complex relationship I need to set up between a User object and a lot of lookup tables. The user object is your run of the mill user model:
public class Youth : IAuditInfo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid YouthGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public string ImageName { get; set; }
[ForeignKey("FkYouthId")]
public ICollection<User> Parents { get; set; }
public CubPack Pack { get; set; }
public virtual ICollection<RequirementsLog> RequirementsLogs { get; set; }
public Youth()
{
Parents = new List<User>();
}
}
The lookup tables is where it gets complex and I can't figure out the path of least complexity in binding them together. For the lookups it is a series of tables starting with one 'master' table, that rolls down hierarchically to requirements and sub requirements, like this:
Master:
public class BearTrail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<BearTrailRequiredBadge> BearTrailRequiredBadges { get; set; }
public virtual ICollection<BearTrailElectiveBadge> BearTrailElectivedBadges { get; set; }
}
Required Badges:
public class BearTrailRequiredBadge
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string Description { get; set; }
public virtual ICollection<BearTrailRequiredBadgeSubRequirement> BearTrailRequiredBadgeSubRequirements { get; set; }
}
Required Badge sub requirement:
public class BearTrailRequiredBadgeSubRequirement
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Number { get; set; }
public string Text { get; set; }
public bool Required { get; set; }
}
This is one set of the lookups, there are about four nested classes like this, and some one off tables as well. Total lookup tables is about 16, give or take.
I was initially thinking if using my RequirementLog model to bind it:
public class RequirementsLog
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Youth> Youth { get; set; }
public BearTrail BearTrailRequirements { get; set; }
public TigerTrail TigerTrailRequirements { get; set; }
public WolfTrail WolfTrailRequirements { get; set; }
public WebelosTrail WebelosTrailRequirements { get; set; }
public WebelosArrowOfLight WebelosArrowOfLightRequirements { get; set; }
}
So there is a many to many between RequirementsLog and Youth. The table created out of RequirementsLog has one PK column (ID), and FK columns for each property. The many to many table created out of this (RequirementsLogYouths) has two PKs (RequirementsLogId, and YouthId).
Am I going about this the right way? The end goal is to have the 16 or so tables server as just lists of various requirements, and have another table(s) to track a particular youths progress through the requirements. I have a hard time visualizes some of this DBA stuff, so any input would be greatly appreciated.
In most cases, a requirements "log" be in a one (people) to many (the log).
Unless... One logged item is for many kids...
If so, the you need a third table, that maps many people to multiple logged events. That is, if this is truly a many to many. In general, that situation almost always begs for a third, intermediate mapping table. Read up a bit on many to many designs, and you'll quickly see it, and how simple it is.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Entity1>()
.HasMany(b => b.Entities2)
.WithMany(p => p.Entities1)
.Map(m =>
{
m.ToTable("Entitie1Entity2");
m.MapLeftKey("Entity1Id");
m.MapRightKey("Entity2Id");
});
}
I have an EF4 Entity Workgroup. Below is the meta-data for that model for reference.
[MetadataType(typeof(WorkgroupMetaData))]
public partial class Workgroup {
public Contact manager { get; set; }
}
[Bind(Exclude = "id")]
public class WorkgroupMetaData
{
[ScaffoldColumn(false)]
public int id { get; set; }
[DisplayName("Org. Number")]
[Required(ErrorMessage = "Org. Number is required.")]
public string org_number { get; set; }
[DisplayName("Workgroup Name")]
[Required(ErrorMessage = "Workgroup name is required.")]
public string name { get; set; }
[DisplayName("Customer Contact")]
public int customer_contact_id { get; set; }
[DisplayName("Manager")]
public int manager_id { get; set; }
[DisplayName("Tech. Lead")]
public int lead_id { get; set; }
[DisplayName("Time Approver")]
public int time_approver { get; set; }
[DisplayName("Description")]
public string description { get; set; }
[ScaffoldColumn(false)]
public object created_at { get; set; }
[ScaffoldColumn(false)]
public object last_modified_at { get; set; }
}
I've got a ViewModel defined as:
public class WorkgroupViewModel
{
public Workgroup Workgroup { get; set; }
public List<Workgroup> Workgroups { get; set; }
}
On the view I have a grid to dump out the workgroups available. This works but I was wondering how to convert the ID fields to the actual strings from another table. Basically the manager, customer_contact, lead are all references to the Contact entity. I would like to show the names from Contacts instead of just the id.
How can this be accomplished? I've looked around a bit but I can't seem to find a suggestion or an answer. Maybe I looking at this from the wrong perspective?
You might consider using a wrapper around Workgroup (decorator pattern) or Tuple or creating a custom class that binds them together.
public class WorkgroupDisplayModel
{
public Workgroup Workgroup { get; set; }
public Manager Manager { get; set; }
// Add additional properties for each related type
}
In your EF query you can do something like:
var query = from w in Context.Workgroups
join m in Context.Managers
on w.manager_id equals m.uid
// Additional joins for each related table
where w.Description == "Project 1" // Whatever criteria
select Tuple.Create(w, m); // Add param for each type
//or
//select new WorkgroupDisplayModel { Workgroup = w, Manager = m, ... };
var list = query.ToList();
var contact = list[0].Item1; // Tuple has strongly typed Item1 thru ItemN
var manager = list[0].Item2;
Then your view model could have:
List<Tuple<Workgroup, Manager, Customer, Lead>> Workgroups { get; set; }
or
List<WorkgroupDisplayModel> Workgroups { get; set; }