I'm making a task management tool using AngularJS for the frontend and ASP.NET WEB API 2 for the backend. I have two entities in the database, a "Task" and a "Type". Each task has one type associated. The user fills a form when he can create a new task, and he has to select a type for that task.
Here's the C# code:
// KBTM_Task.cs file
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
}
// KBTM_Type.cs file
public class KBTM_Type
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
So my question is: how do I "connect" the two in the database? What I mean is, let's say I want to POST data to the database. I have to make two POSTs, right? One for the Task and one for the Type, since they're two separate entities.
But since they're stored with two different IDs, how do I know that a certain task has a certain type? In other words, if I send a GET request to KBTM_Task, how do I get the type of that task?
Modify your KBTM_Task entity to include the Type Id and foreign key relationship
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
public int TypeID { get; set; }
[ForeignKey("TypeID")]
public virtual KBTM_Type Type { get; set; }
}
This way when you get the data from the API your task object will already include the key ("TypeID") that can be updated and related object ("Type") that you can access its properties (Name, Description, ...).
When you update TypeID on the client object (model) you can simply push the updated task object to the API using $http.put() to handle the database update.
1) Add foreign key using fluent api (or data annotation)
// KBTM_Task.cs file
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
public int KBTM_TypeID {get;set}
public virtual KBTM_Type {get; set}
}
// KBTM_Type.cs file
public class KBTM_Type
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public KBTM_Task KBTM_Task { get; set;}
}
Add the following in the class inheriting from DbContext
public class KbtmContext : DbContext
{
...
//public virtual DbSet<KBTM_Task> KbtmTasks {get; set;}
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure KBTM_TypeID as FK for KBTM_Task
modelBuilder.Entity<KBTM_Task>()
.HasRequired(k => k.KBTM_Type)
.WithRequiredPrincipal(ad => ad.KBTM_Task);
}
}
2) If exposing the entity class in API response or request then you need to exclude navigation property from being serialized.
// KBTM_Task.cs file
public class KBTM_Task
{
...
[JsonIgnore]
public virtual KBTM_Type Type { get; set; }
}
To use the [JsonIgnore] atttribute use Install-Package Newtonsoft.Json in package manager console.(One of the popular solutions to manage serialization)
Related
I'm working on an ASP.net Web API application and would like to allow multiple users to have access to their own data without changing the database schema too much.
My tables look a little like this:
public class Asset
{
public string Name { get; set; }
[Required]
public int Id { get; set; }
public string AssetTag { get; set; }
public string Serial { get; set; }
public int Model { get; set; }
}
public class Organisation
{
[Required]
public int Id { get; set; }
[Required]
public int DefaultLocation { get; set; }
public location Location { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string Contact { get; set; }
}
public class AssetModel
{
public string Name { get; set; }
[Required]
public int Id { get; set; }
public int CategoryId { get; set; }
public int ManufacturerId { get; set; }
public string ModelNumber { get; set; }
}
*fields omitted for brevity
Each user should be able to create their own assets / organisations / etc, but should not have access to any other users fields.
I'm yet to add authorization / authentication however I'm probably going to use token based auth as outlined here:
http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/
Should this be as easy as tacking each users GUID onto each column and applying some logic? Or will I need to completely re-design the database?
I write chat in C# with Entity Framework
This is my code
public class User
{
[Key]
public long id { get; set; }
public List<UserMessages> userMessages { get; set; }
}
public class UserMessages
{
[Key]
public long Id { get; set; }
public long ChatMateId { get; set; }
public string text { get; set; }
public DateTime? dateTime { get; set; }
}
Entity Framework code-first forced me to put on the Id field in UserMessages class attribute [Key] , I don't need this field at all because UserMessages is weak entity that will point to id of User
Can I delete long Id from sql table after ef create the table, without any problems?
No, you can't. EF requires that all tables have a [Key] on them, or it will blow up. Even when mapping to SQL Views (as I've learned the hard way, believe me).
In your case, you should actually be using a proper Foreign Key in that table also, like so:
public class UserMessages
{
[Key]
public long Id { get; set; }
[ForeignKey("User")]
public long UserId { get; set; }
public long ChatMateId { get; set; }
public string text { get; set; }
public DateTime? dateTime { get; set; }
public User User { get; set; }
}
My two classes are as follows
public class Client
{
public Guid Id { get; set; }
public String Name{ get; set; }
public virtual ICollection<Product> Products{ get; set; }
}
public class Product
{
public Guid Id { get; set; }
public String Model { get; set; }
public String Version { get; set; }
[ForeignKey("Client_Id")]
public virtual Client Client { get; set; }
[ForeignKey("Client")]
public Guid? Client_Id { get; set; }
}
And the DbContext class is this:
public class ClientContext : DbContext
{
public DbSet<Client> Clients { get; set; }
public DbSet<Product> Products { get; set; }
}
I have confirmed that this model works with the associated database for all the usual CRUD stuff.
I have created a RIA DoaminService class on the web site as so:
[EnableClientAccess]
public class ClientDomainService : DbDomainService<ClientContext>
{
public IQueryable<DomainModel.Product> GetProducts()
{
return this.DbContext.Controllers;
}
public IQueryable<DomainModel.Client> GetClients()
{
return this.DbContext.Clients;
}
}
When I build the silverlight application I get the error:
Unable to retrieve association information for assocation .... Only
models that include foreign key information are supported.
I thought that the [ForeignKey] attributes added to the Client and Client_Id properties on the Product class would satisfy the "include foreign key information" requirement.
What am I missing?
Assuming your code is complete, you are missing the other side of the Foreign key relationship... the [Key] attributes on the primary keys.
This is probably just because my knowledge with the EF Code First fluent API is lacking, but I'm stumped.
I want to model the following:
A Groups collection with Id and Name
A Users collection with Id and Name
Each user is assigned to exactly one primary group
Each user may have zero or many secondary groups
The table structure I'm going for would look like:
Groups
Id
Name
Users
Id
Name
PrimaryGroupId
SecondaryGroupAssignments
UserId
GroupId
I've been beating my head against a wall trying to model this with EF Code First, but I can't get it to accept both relationships between User and Group. Sorry for not posting any .NET code (I'm happy to), but it's probably all wrong anyway.
Is there a way to make EF model this? I'm assuming I have to do some sort of configuration with the Fluent API. Maybe a better question is: is there any good, definitive reference for the Fluent API?
Thanks!
Try this (untested):
public class Group
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> PrimaryUsers { get; set; }
public virtual ICollection<User> SecondaryUsers { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int PrimaryGroupId { get; set; }
public virtual Group PrimaryGroup { get; set; }
public virtual ICollection<Group> SecondaryGroups { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasRequired(u => u.PrimaryGroup)
.WithMany(g => g.PrimaryUsers)
.HasForeignKey(u => u.PrimaryGroupId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasMany(u => u.SecondaryGroups)
.WithMany(g => g.SecondaryUsers)
.Map(m => m.MapLeftKey("UserId")
.MapRightKey("GroupId")
.ToTable("SecondaryGroupAssignments"));
}
}
Based on Ladislav's excellent answer, here's how to do it without using any mappings - just attributes applied to the Model classes themselves:
public class Group
{
public int Id { get; set; }
[MaxLength(300)]
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
[MaxLength(300)]
public string Name { get; set; }
[ForeignKey("PrimaryGroup")]
public int PrimaryGroupId { get; set; }
[Required]
public Group PrimaryGroup { get; set; }
[InverseProperty("Users")]
public ICollection<Group> SecondaryGroups { get; set; }
}
Notes
If you want, you can add the virtual keyword to the 2 ICollections and the Group. This allows lazy-loading. Performance-wise, I don't recommend it, but it is possible.
I included MaxLength attributes with an arbitrary (but safe) length of 300, because putting strings out in EF without a MaxLength gets you low-performance NVarChar(MAX) columns. Totally irrelevant to what's being asked but better to post good code.
I recommend against class names "User" and "Group" for your EF classes. They're going to complicate any SQL you attempt to run later, having to type [User] and [Group] to access them, and complicate using these classes in MVC Controllers where your class User will conflict with the Context property User that gives you access to the Asp.Net Identity library.
Is it possible to have child objects on RIA Services entities that are also entities themselves?
public class Project
{
[Key]
public int ID { get; set; }
[Editable(false)]
public String Name { get; set; }
public Machine Machine { get; set; }
}
public class Machine
{
[Key]
public int ID { get; set; }
[Editable(false)]
public String Name { get; set; }
}
You can specify the [Include] attribute on the Machineproperty in the metadata class and add a call to Include("Machine") on the object context query.
See: http://vincenthomedev.wordpress.com/2010/01/08/using-wcf-ria-services-to-include-multi-table-master-detail-data/
I don't think so, at least not in the traditional sense.