EF 4.1 in MVC3 using Lazy Loading - asp.net-mvc-3

EF 4.1 in MVC3 and Lazy loading, using code first model
I am using Membership API for creating an account. Once the account is created successfully. I redirect to create a Contact record automatically.
contactId (auto database generated), userid (storing the user id that was generated by membership api)
The models are:
public class Contact
{
public int ContactID { set; get; }
public string UserId { set; get; }
public string LastName { set; get; }
public int? CompanyID { set; get; } // not sure if I need this as it will be NULL
public virtual Company CompanyInfo { set; get; }
}
next the user can click on create Company link or logout & login later to create the company record.
public class Company
{
public int CompanyID { set; get; }
public int ContactID { set; get; }
public string CompanyName { set; get; }
public virtual Contact Contacts { set; get; }
}
When the user decides to create company record, I am checking if company already exists, if exists I am just showing the contact information and the company information OR if not found I redirect to create company.
public ActionResult chckifCompanyFound()
{
int contactId = 1; //Assuming I retrieved the value
//I think I should get the data from Company table, if company data found then contact data could be retrieved using lazy loading?
Company c= db.Company.Include(c => c.Contacts).Where(x => x.ContactID == contactId).FirstOrDefault();
if(c == null)
//redirect to create company
else
// view data from c company object
}
currently it shows an exception once it tries to create contact record after membership API creates an account. I create the record like this:
Contact contact = new Contact();
contact.UserId = userId;
contact.LastName = lastName;
db.Contacts.Add(contact);
db.SaveChanges();
Exception:
Unable to determine the principal end of an association between the types 'D.Models.Contact' and 'D.Models.Company'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
thank you so much!

Try the following (from this link):
Contact cc = db.Contacts.Include( "CompanyInfo" ).Where(x => x.ContactID == product.ContactID).FirstOrDefault();

Try replacing your models with these:
public class Contact
{
public int ContactId { set; get; }
public string UserId { set; get; }
public int CompanyId { get; set; }
public string LastName { set; get; }
public virtual Company Company { set; get; }
}
public class Company
{
public int CompanyId { set; get; }
public string CompanyName { set; get; }
public virtual ICollection<Contact> Contacts { set; get; }
}
Your Contact needs a CompanyId since it has only a single company related to it, and it will act as a foreign key between that contact and the company. The navigation property CompanyInfo will be used for the lazy loading. Your Company object only needs the Contacts collection because the Contact is where the relationship is created in the database.
To answer your question about the query I need more information... where does the product come in to play? I don't see it referenced from the contact or company, but if you want to get the Company of a Contact, simply do this:
var company = dbContext.Contacts.Find(userId).Company;
Console.WriteLine("Company Name: {0}", company.CompanyName);

try:
Contact cc = db.Contacts.Include(c=>c.CompanyInfo).Where(x => x.ContactID == product.ContactID).FirstOrDefault();

Related

How to create one-to-many related object in ASP.NET Web API?

I have two entities
public class Tax
{
public int Id { get; set; }
public string Name { get; set; }
public int ClientId { get; set; }
public Client Client { get; set; }
}
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Tax> Taxes { get; set; }
}
and in this method, I want to create a relationship between Client and Tax using ClientId, but i am getting The Client field is required error on client side, so I want to ignore field Client.
My question is how to ignore fielf client or if I'm doing something wrong, then how to create one-to-many relationship in Post method? (I'm new to ASP.NET so sorry if this is a stupid question.)
[HttpPost]
public IActionResult Post(Tax tax)
{
tax.Client = (from c in context.Clients
where c.Id == tax.ClientId
select c).FirstOrDefault<Client>();
context.Taxes.Add(tax);
context.SaveChanges();
return Created("api/taxes", tax);
}
you just need to make ClientId nullable. It will do the same as an optioanal.
public int? ClientId { get; set; }
public Client Client { get; set; }
or if you use net 6 you will have to make Client nullable too
public int? ClientId { get; set; }
public Client? Client { get; set; }
but you can remove nullable option from project to avoid all this extra problems forever
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<!--<Nullable>enable</Nullable>-->
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
in this case, if tax has a ClientId already, you only need
context.Taxes.Add(tax);
context.SaveChanges();

Automapper Many to Many Mapping Including Joining Table Data

I am developing an ASP.Net Core Web API using EF Core Code First (C#) and SQL Server.
I have a fairly simple scenario which I just cannot figure out. I have a Form entity and a Site entity. Each Form can have many Sites and each Site can be in many Forms. To enable this I have a SiteForm joining table. For each Site associated with a Form there is a Leaving Date field. So my SiteForm class looks like this:
public class SiteForm
{
public Guid SiteId { get; set; }
public Site Site{ get; set; }
public Guid FormId { get; set; }
public Form Form{ get; set; }
public DateTime? LeavingDate { get; set; }
}
My Form's Data Transfer Object (FormDto) is as follows:
public class FormDto
{
public Guid Id { get; set; }
...
public ICollection<LinkedSiteDto> LinkedSites { get; set; }
= new List<LinkedSiteDto>();
}
And my LinkedSiteDto is like this:
public class LinkedSiteDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Ident { get; set; }
public DateTime? LeavingDate { get; set; }
}
Having populated the database I can get the Sites for each Form using the following mapping:
CreateMap<Form, FormDto>()
.ForMember(dto => dto.LinkedSites, opt => opt.MapFrom(
form => form.SiteForms.Select(sf => sf.Site).ToList()));
I just cannot figure out how I would include the LeavingDate value from each joining table entry? Any suggestions would be very gratefully received.

Updating one of the related entity

I'm developing bulletin board system (as part of my training of asp.net mvc). I have a basic understanding of data modeling, but I have a doubt the way I've created my model. The core logic is to post ad with the following categories realty, auto and service. Initially I tried to use TPH approach, but then faced with problem of binding my models and automapper configuration. Now I think to use zero or one relationship.
I have a Ad model:
public class Ad
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual Realty Realty { get; set; }
public virtual Auto Auto { get; set; }
public virtual Service Service { get; set; }
}
Realty:
public class Realty
{
[Key]
[ForeignKey("Ad")]
public int AdID { get; set; }
public string Type { get; set; }
public string NumberOfRooms { get; set; }
public virtual Ad Ad { get; set; }
}
Auto and service models have the same foreign key as the Realty model.
My db context:
public DbSet<Ad> Ads { get; set; }
public DbSet<Realty> Realties { get; set; }
public DbSet<Auto> Autos { get; set; }
public DbSet<Service> Services { get; set; }
I need update Ad model with one related model only. I'm using scaffolded controller action, which includes all related models:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "Title,Descirpiton,Realty,Auto,Service")] Ad ad)
{
if (ModelState.IsValid)
{
db.Ads.Add(ad);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.ID = new SelectList(db.Autos, "AdID", "CarType", ad.ID);
ViewBag.ID = new SelectList(db.Realties, "AdID", "Type", ad.ID);
ViewBag.ID = new SelectList(db.Services, "AdID", "ServiceType", ad.ID);
return View(ad);
}
The problem, that it makes possible to post Ad with all related models together. Before diving deep I wanted to ensure that I'm on a right way of doing this.
Thanks.
You're close. Based on what it looks like you're trying to do you should be using a table-per-type model. You create the base (Ad) and then inherit from it to create the sub-types.
public class Ad
{
[Key]
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
[Table("Realty")]
public class Realty : Ad
{
public string Type { get; set; }
public string NumberOfRooms { get; set; }
}
Your context remains the same. You can now create the appropriate sub-type when you know what kind of ad is being created.
var ad = new Realty();
ad.Title = "...";
ad.Description = "...";
ad.Type = "...";
ad.NumberOfRooms = "...";
You can retrieve specific ad types by using the specific type on the context.
db.Realty.ToList();
Or you can retrieve all the ads and interrogate the types as you loop over them.
var ads = db.Ads.ToList();
foreach(var ad in ads)
{
if(ad is Realty)
// do Realty stuff
else if (ad is Auto)
// do Auto stuff
}

Mvc3 Database first relationships

I am working on a project with ASP.Net MVC3 EF4.1 and relationships between tables through foreign keys. I am using the database first approach and have three tables: Calendar, Calendar Users and Users. The relationship is that a Calendar can have many users and users can have many calendars.
When someone is creating the calendar he/she is also supposed to select the number of users that will have access to the calendar. But now when I am about to save the changes to the database in the controller thats when I get confused. In the generated classes there are also virtual ICollections that I suppose represent the foreign keys somehow. But I can't figure out how I am supposed to handle them? So how is it supposed to work? Should I be able to add the changes to the virtual ICollections and then just do db.SaveChanges() and it will work by itself or am I supposed too handle that manually?
If I am supposed to handle it manually should I then add the users, add the calendar and then add the keys in the CalendarUsers table to bind them together? I've seen some examples from code first where they have clarified the relationship by entering code in the OnModelCreating method but when using Database first it just contains: throw new UnintentionalCodeFirstException();? Hoping you perhaps can clarify it for me a bit.
Added the classes generated by the DBcontext Generator below:
public partial class Calendar
{
public Calendar()
{
this.CalendarUsers = new HashSet<CalendarUser>();
}
public int CalendarId { get; set; }
public string CalendarTitle { get; set; }
public string CalendarDescription { get; set; }
public long UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<CalendarUser> CalendarUsers { get; set; }
}
public partial class CalendarUser
{
public int CalendarUserId { get; set; }
public int CalendarId { get; set; }
public long UserId { get; set; }
public Nullable<bool> IsAdmin { get; set; }
public virtual Calendar Calendar { get; set; }
public virtual User User { get; set; }
}
public partial class User
{
public User()
{
this.Calendars = new HashSet<Calendar>();
this.CalendarUsers = new HashSet<CalendarUser>();
}
public long UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Calendar> Calendars { get; set; }
public virtual ICollection<CalendarUser> CalendarUsers { get; set; }
}
You can just create a new relationship between existing Calendar and User by setting the foreign key properties in a new instance of the CalendarUser:
var newCalendarUser = new CalendarUser
{
CalendarId = calendarId,
UserId = userId,
IsAdmin = true // or false
};
dbContext.CalendarUsers.Add(newCalendarUser);
dbContext.SaveChanges();
You can update the navigation properties and EF will take care of the foreign keys for you. For sample code that handles updates in a similar many-to-many relationship, see the Adding Course Assignments to the Instructor Edit Page section of this tutorial:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application

mVC3 EF4.1 Model design error

EF 4.1 in MVC3 and Lazy loading, using code first model
Having difficulties designing correct models. Please take a look and let me know what am I doing wrong. How can I fix it.
I am using Membership API for creating an account. Once the account is created successfully. I redirect to create a Contact record automatically.
contactId (auto database generated), userid (storing the user id that was generated by membership api)
The models are:
public class Contact
{
public int ContactID { set; get; }
public string UserId { set; get; }
public string LastName { set; get; }
public int? CompanyID { set; get; } // not sure if I need this as it will be NULL
public virtual Company CompanyInfo { set; get; }
}
next the user can click on create Company link or logout & login later to create the company record.
public class Company
{
public int CompanyID { set; get; }
public int ContactID { set; get; }
public string CompanyName { set; get; }
public virtual Contact Contacts { set; get; }
}
When the user decides to create company record, I am checking if company already exists, if exists I am just showing the contact information and the company information OR if not found I redirect to create company.
public ActionResult chckifCompanyFound()
{
int contactId = 1; //Assuming I retrieved the value
//I think I should get the data from Company table, if company data found then contact data could be retrieved using lazy loading?
Company c= db.Company.Include(c => c.Contacts).Where(x => x.ContactID == contactId).FirstOrDefault();
if(c == null)
//redirect to create company
else
// view data from c company object
}
currently it shows an exception once it tries to create contact record after membership API creates an account. I create the record like this:
Contact contact = new Contact();
contact.UserId = userId;
contact.LastName = lastName;
db.Contacts.Add(contact);
db.SaveChanges();
Exception:
Unable to determine the principal end of an association between the types 'D.Models.Contact' and 'D.Models.Company'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
thank you so much!
The answer is in your exception: The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
However, it looks like you have a 1 to 0..1 relationship here: Each company must have exactly 1 contact, but each contact can belong to zero or 1 company. Is this really your intent?
It seems to me that what you are really after is a 1 to * relationship, where each company can have MANY contacts, and each contact belongs to zero or 1 company.
Data annotations
public class Company // the principal
{
public int CompanyID { set; get; }
//public int ContactID { set; get; } Company is the principal
public string CompanyName { set; get; }
public virtual ICollection<Contact> Contacts { set; get; } // has many contacts
}
public class Contact
{
public int ContactID { set; get; }
public string UserId { set; get; }
public string LastName { set; get; }
// you do need CompanyId, but it will be nullable in the db
public int? CompanyID { set; get; }
[ForeignKey("CompanyID")]
public virtual Company CompanyInfo { set; get; }
}
Fluent API
Note: with fluent API, your entity does not need the [ForeignKey] attribute
modelBuilder.Entity<Company>()
.HasMany(principal => principal.Contacts)
.WithOptional(dependent => dependent.CompanyInfo)
.HasForeignKey(dependent => dependent.CompanyID);
You have a one-to-one relationship between Contact and Company and the exception says that EF cannot decide what's the principal (having the primary key) and what's the dependent (having the foreign key) because the navigation properties are "symmetric". EF demands that you specify this explicitly.
A few things to note about one-to-one relationships (they are more difficult to master with EF than one-to-many and even many-to-many relationships):
EF only supports Shared Primary Key Associations to define a one-to-one relationship. This means that the primary key values of the associated entities must be the same. And for one of the entities the primary key is a foreign key at the same time. You cannot use an independent foreign key.
Consequence of this point is that you can remove Contact.CompanyID and Company.ContactID. EF won't respect these properties as foreign key properties.
Second consequence is that one of the primary keys cannot be an autogenerated identity in the database because it must always have the same value as the other (the principal) entity.
You must decide which entity is the principal and which is the dependent. From your description I would guess that Contact is the principal (because you allow to have contacts without a company) and Company is the dependent (because there is no company without contact possible).
Then you can define a mapping with Fluent API in your derived context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ...
modelBuilder.Entity<Contact>()
.HasOptional(ct => ct.CompanyInfo)
.WithRequired(cm => cm.Contacts);
// ...
}
I believe that this mapping will automatically ensure (if you create the DB with EF) that the primary key of Contact is an identity in the database, but the primary key of Company isn't. If not, you can turn this off explicitly:
modelBuilder.Entity<Company>()
.Property(c => c.CompanyID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

Resources