How to return hierarchical graph with RIA services - silverlight

I would like a DomainService as follows:
[EnableClientAccess]
public class FamilyService : DomainService
{
public IQueryable<Person> GetPeople() // for the time being fake out a set of people
{
var people = new[]{ new Person(), ... };
return people.AsQueryable();
}
}
The Person class should be simple enough:
public class Person
{
[Key]
public Guid Id { get; set; }
public Person Parent { get; set; }
public String Name { get; set; }
public Person[] Children { get; set; }
}
In the Silverlight client, the Person class that is generated does not include the Parent or Children properties. What am I missing?
(Perhaps I should point out that while this is obviously a mock example, I am planning on using a loosely coupled approach, that this mimics. In fact most of my data doesn't reside in a database at all A significant majority of the RIA samples use an end-to-end parity of model from the database to the UI, which is not an option in my case.)

Try adding the [Include] attribute to the Parent & Children properties.
public class Person
{
[Key]
public Guid Id { get; set; }
[Include]
public Person Parent { get; set; }
public String Name { get; set; }
[Include]
public Person[] Children { get; set; }
}
If the Person class is coming out of the DB (in your real world app) and you are using EF, then you need to force them to be loaded eagerly, using the .Include() method.

Related

How to to make entity relationships in WEB API database?

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)

How to add foreign key information to code first POCO to comply with RIA Services

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.

EF Code First: Many-to-many and one-to-many

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.

RIA Services: Entities with child entities

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.

Modeling a Generic Relationship (expressed in C#) in a Database

This is most likely one for all you sexy DBAs out there:
How would I effieciently model a relational database whereby I have a field in an "Event" table which defines a "SportType"?
This "SportsType" field can hold a link to different sports tables E.g. "FootballEvent", "RubgyEvent", "CricketEvent" and "F1 Event".
Each of these Sports tables have different fields specific to that sport.
My goal is to be able to genericly add sports types in the future as required, yet hold sport specific event data (fields) as part of my Event Entity.
Is it possible to use an ORM such as NHibernate / Entity framework / DataObjects.NET which would reflect such a relationship?
I have thrown together a quick C# example to express my intent at a higher level:
public class Event<T> where T : new()
{
public T Fields { get; set; }
public Event()
{
EventType = new T();
}
}
public class FootballEvent
{
public Team CompetitorA { get; set; }
public Team CompetitorB { get; set; }
}
public class TennisEvent
{
public Player CompetitorA { get; set; }
public Player CompetitorB { get; set; }
}
public class F1RacingEvent
{
public List<Player> Drivers { get; set; }
public List<Team> Teams { get; set; }
}
public class Team
{
public IEnumerable<Player> Squad { get; set; }
}
public class Player
{
public string Name { get; set; }
public DateTime DOB { get; set;}
}
DataObjects.Net supports automatic mappings for open generics. Some details on this are described here.
You can do this by having all the Event types inherit from an abstract Event base class. This make sense to me because all the events share some common properties: date, venue, etc. You can use a table per concrete class or table per subclass strategy to store the objects in a relational database. Here are some links to articles describing inheritance mapping with NHibernate:
Chapter 8. Inheritance Mapping
Fluent NHibernate and Inheritance Mapping
NHibernate Mapping – Inheritance
The example converted to DO4 must look as follows:
// I'd add this type - adding an abstract base makes design more clean + allows you to share
// the behavior among all the descendants
[Serializable]
[HierarchyRoot]
public abstract class EventBase : Entity
{
[Key]
Guid Id { get; private set; } // Or some other type
}
[Serializable]
public class Event<T> : EventBase
where T : IEntity, new() // IEntity indicates DO4 must try to map its descendants automatically
// Although I'd put some stronger requirement, e.g. by using IEventData instead of IEntity here
{
public T Data { get; set; }
public Event(T data)
{
Data = data;
}
}
[Serializable]
[HierarchyRoot]
public class FootballEvent
{
// You need [Key] here
public Team CompetitorA { get; set; }
public Team CompetitorB { get; set; }
}
[Serializable]
[HierarchyRoot]
public class TennisEvent
{
// You need [Key] here
public Player CompetitorA { get; set; }
public Player CompetitorB { get; set; }
}
[Serializable]
[HierarchyRoot]
public class F1RacingEvent
{
// You need [Key] here
public EntitySet<Player> Drivers { get; private set; }
public EntitySet<Team> Teams { get; private set; }
}
[Serializable]
[HierarchyRoot]
public class Team
{
// You need [Key] here
public EntitySet<Player> Squad { get; set; }
}
[Serializable]
[HierarchyRoot]
public class Player
{
public string Name { get; set; }
public DateTime DOB { get; set; }
}
In this case Event instances will be available (= mapped automatically) for all suitable Ts from model. E.g. in this case they'll be:
- EventBase // Yes, even it, coz it's suitable
- FootballEvent
- TennisEvent
- F1RacingEvent
- Team
- Player
If you'd like to restrict this to just certain types, you must do the following:
- Add an interface inherited from IEntity all these types will support, e.g. IEventData.
- Use it as generic parameter constraint for generic parameter T in Event.
Cross Posted from: http://forum.x-tensive.com/viewtopic.php?f=29&t=5820,
Answer By Alex Yakunin,
Chief Executive DataObjects.NET
There are a bunch of options like XML columns and EAV (also known as database within a database), but none of which will translate well with an ORM to traditional static object-oriented languages, and all of which have drawbacks with respect to data type safety and referential integrity at the database level.
If you need this level of dynamic structure in both the database and the client, you might need to go with an object or document database (and language) which is much more dynamic by design - relational databases tend to work best with static relationships and data models.

Resources