I am using System.ComponentModel.DataAnnotations in my domain classes with EF5 and framework 4.5
public class SalesOrderLine : LoggedEntity
{
[ForeignKey("SalesLine")]
[Required]
public int SalesLine_Id { get; set; }
public SalesLine SalesLine { get; set; }
}
However the example here uses the column name as the foreign key, not the navigation property name.
Is there any difference between the two ways of doing it?
[
There is no difference. Both usages do the same.
Which is clear or not clear depending on if you are using EF 5 with .NET 4.0 or with .NET 4.5.
In .NET 4.0 (where the ForeignKeyAttribute class is part of the EntityFramework.dll assembly) the description you'll see in Intellisense (when hovering over the attribute for example) says (emphasis by me):
Denotes a property used as a foreign key in a relationship. The
annotation may be placed on the foreign key property and specify the
associated navigation property name, or placed on a navigation
property and specify the associated foreign key name.
In .NET 4.5 (where the ForeignKeyAttribute class has been moved into the framework's System.ComponentModel.DataAnnotations.dll assembly) the description has become as informative as a tautology:
Denotes a property used as a foreign key in a relationship.
The example you posted shows how to use EF Code-first to create the navigation property of Manager and map it to type Person -
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public int ManagerId { get; set; }
// The following will use ManagerId as the foreign key and map it to PersonId, or w/e the key field is for person
[ForeignKey("ManagerId")]
public Person Manager { get; set; }
}
Your code uses SalesLine_Id to establish the foreign key relationship to the SalesLine, which is of type SalesLine.
public class SalesOrderLine : LoggedEntity {
[ForeignKey("SalesLine")]
[Column("SalesLine_Id")]
[Required]
public int SalesLine_Id { get; set; }
public SalesLine SalesLine { get; set; }
}
What's important to understand is that you probably don't have to decorate your class with all of the annotations that you are, unless you are using an odd naming convention, because EF will automatically map everything the way you want it to provided the names are fairly consistent -
public class SalesOrderLine {
public int SalesOrderLineId { get; set; }
public string Description { get; set; }
public int SalesLineId { get; set; }
public virtual SalesLine SalesLine { get; set; }
}
public class SalesLine {
public int SalesLineId { get; set; }
public string Description { get; set; }
public ICollection<SalesOrderLine> SalesOrderLines { get; set; }
}
Would work just fine, as an example. If you need to make the relationship any more complicated you will probably need to start looking at using the Fluent API for configurations, but as is that code would work fine without dataannotations. Since SalesLineId is not nullable it is required, EF understands it is a foreign key, and everything is happy.
Related
As a learning exercise, I was trying to form a simple One to many Relation between Vendors and Shops where each Vendor has multiple shops.
Using two model classes I achieved the following:
This is what scaffolding gives me for Shops
Why am I not getting a drop down here?
Not sharing any code as of now. As most of it is auto generated. Let me know if someone would like me to paste the code for my models.
Another doubt why are my table Columns alphabetically Ordered? If I am not mistaking entity framework 6 used to order it as per the class definition. Is there a way to override it? Cause the view scaffold seems to mirror the DB in terms of ordering objects.
Thanks.
Update:
I added a property Vendorid in the Shops Class. Now I see the Dropdown but no values. Do I need to get down to Fluent API for achieving this?
public class Shops
{
public int Id { get; set; }
public String Name { get; set; }
public int Rank { get; set; }
public String Address { get; set; }
public Boolean Active { get; set; }
public int VendorId { get; set; }
public Vendors Vendor { get; set; }
}
Vendors
public class Vendors
{
public int Id { get; set; }
public String Name { get; set; }
public int Rank { get; set; }
public Boolean Active { get; set; }
public ICollection<Shops> Shops { get; set; }
}
Env: Visual Studio 2015 (Latest ASP.net Patch Applied).
Project Template: Web Application
This depends on what approach on EF you are using. Are you sure they have relationship in their classes? If your using code first, you have to map it manually using fluent API. on OnModelCreating method on DbContext class, use something like this
modelBuilder.Entity().HasMany(v => v.Shop).WithOptional(s => s.Vendor).
If your using Database First, just create relationship on tables diagram(or script) and update the model on your project and EF will create it for you.
I have complex type for Audit fields
My complex type:
[ComplexType]
public class AuditData {
[Column("CreatorUserId")]
public int? CreatorUserId { get; set; }
public DateTime? CreationTime { get; set; }
[Column("ModifierUserId")]
public int? ModifierUserId { get; set; }
public DateTime? ModificationTime { get; set; }
}
My base Entity (all other inherti this one) has AuditData property:
public abstract class Entity : IEntity, IAuditedEntity, INotifiedDbContextBeforeSave
{
// Summary:
// Unique identifier for this entity.
public int Id { get; set; }
public bool IsActive { get; set; }
public int Old_Id { get; set; }
public string Old_TableName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public AuditData AuditData { get; set; }
// can this 2 lines below work as navigation property with foreign key in complex type
public virtual User CreatorUser { get; set; }
public virtual User ModifierUser { get; set; }
//... other fields
}
I have 2 navigation properties CreatorUser and ModifierUser.
I know you cant have navigation property in ComplexType but can my navigation property on entity be mapped with foreign key in complexType
something like:
[ForeignKey("CreatorUserId")] // --> should point to AuditData.CreatorUserId
public virtual User CreatorUser { get; set; }
becouse CreatorUserId will be property in every entity but EF is not aware of it.
Mybe there is solution in fluent API ?
The official documentation says:
Complex types are non-scalar properties of entity types that enable scalar properties to be organized within entities. Like entities, complex types consist of scalar properties or other complex type properties. Because complex types do not have keys, complex type objects cannot be managed by the Entity Framework apart from the parent object.
It follows that that complex types can not participate in any relations among entities, so they can't contain foreign keys
I want to display customer information.
Then I created some classes; Customer, Delivery, Order, OrderLine, Product, and rentalDB.
rentalDB class sets 5 DbSet of Product, Customer, Order, OrderLine, and Delivery.
When I make UserController with list view, I cannot display the customer information, and it takes errors:
One or more validation errors were detected during model generation:
System.Data.Edm.EdmEntityType: : EntityType 'OrderLine' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntityType: : EntityType 'Delivery' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �OrderLine� is based on type �OrderLine� that has no keys defined.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �Delivery� is based on type �Delivery� that has no keys defined.
I don't know why these entities require key?
I have no idea for this error..
Could you help me?
--UserController.cs--
namespace MvcApplication2.Controllers
{
public class UserController : Controller
{
//
// GET: /User/
rentalDB _db = new rentalDB();
public ActionResult Index()
{
var model = _db.Customer;
return View(model);
}
}
}
--Delivery.cs in Models folder--
namespace MvcApplication2.Models
{
public class Delivery
{
public int trackId { get; set; }
public String address { get; set; }
public String postCode { get; set; }
public decimal deliveryPrice { get; set; }
public DateTime deliveryDate { get; set; }
public DateTime returnDate { get; set; }
}
}
--OrderLine.cs in Models folder--
namespace MvcApplication2.Models
{
public class OrderLine
{
public int basketId { get; set; }
public int productId { get; set; }
public int quantity { get; set; }
}
}
In order to use the entity framework, every entity needs a key. This is how EF tracks objects in its cache, posts updates back to the underlying data store, and links related objects together.
Yours objects already have keys, you just need to tell the EF about them:
namespace MvcApplication2.Models
{
public class Delivery
{
[Key] public int trackId { get; set; }
public String address { get; set; }
public String postCode { get; set; }
public decimal deliveryPrice { get; set; }
public DateTime deliveryDate { get; set; }
public DateTime returnDate { get; set; }
}
}
When you use an ORM (object-relational mapper) framework like NHibernate or Entity framework that helps you map a relational database to an object model you need something that will let you make a meaningful relation between your objects in memory and rows of data in your database and that thing is a key (id as NHibernate call it) and usually that's the natural way that RDBMS track records using a Primary key (usually you use DB primary key as the key of your object)
When you check to see if two objects are equal using == operator you are checking that those objects have the same reference (or address in memory). This kind of equality is not very helpful when you are using an ORM .You can load multiple instances of a record in memory with different references so that it's impossible to check the equality of objects by their references .That's when checking equality by value come into play and keys have the main role in this play.
I have an entity for my accessing my pages (pages of a blog).
Here it is:
public class Page
{
[Key]
public int PageId { get; set; }
public string AuthorName { get; set; }
[ForeignKey("AuthorName")]
public virtual MembershipUser Author { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Content { get; set; }
public DateTime? PublishDate { get; set; }
public bool Published { get; set; }
public DateTime LastModified { get; set; }
}
As you can see, I would like to keep a reference to the person who created the page (in fact this is the connected user). So I defined an Author member in my class (of type MembershipUser). I also try to define the foreign key but it doesn't work because there is no primary key in the MembershipUser entity. I cannot add one to this entity because if is an aspnet object.
How can I proceed in order to have a reference to the creator of the page in my Page entity?
Thanks.
You can extend the MembershipUser by creating a CustomMembershipUser that will inherit from MembershipUser.
You can add any fields you want to your CustomMembershipUser, you will then also have to create a table of your own with both the original fields and your extra fields.
Here is some documentation that describes how you can do this.
What ORM are you using?
Anyway, you won't be able to reference a class that is not mapped in your ORM. You could create an entity like SystemUser, map it to a table and reference it at your Page entity. When you log in using Membership, you could query that SystemUser and store it in HttpSession so you can use it later.
I have the following two entities:
public class User
{
public int PKID { get; set; }
public string Login { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public virtual ICollection<AppAccess> AppAccessList { get; set; }
}
public class AppAccess
{
public int PKID {get; set; }
public string Login { get; set; }
public string AppCode { get; set; }
}
The PKID field is the primary key and identity column of each table but the Login field is what links them in a one-to-many relationship where the User is the principal entity.
How can I setup the navigation property AppAccessList using the fluent API, if it is even possible, to use the Login field as the relationship key? BTW, the entities are based on existing schema that cannot be refactored at this time.
I don't believe this is possible out of the box, certainly not the ability to add a navigation property. I tried using the new power tools to reverse engineer a database and it just ignored the relationship, neither could i create it using code (annotations or fluent interface).
You can create the relationship using raw sql on the on OnModelCreating method to make the constraint, but you'd need to use join's manually to navigate between the tables.