Using Entity Framework classes in WCF service without recreating the model - c#

I have a dll project that holds an edmx. In the same solution, I have a WCF Library project and within this project is an interface and a class to hold all OperationContracts. I've noticed that this works great with just one table in the model. Once I add another table to the edmx with a relationship to the first table, the service breaks. So, I've narrowed down that the errors I've been receiving are (in a general sense) due to the relationships between my EF types and more specifically the way the relationships are declared within each class... So basically I just went in and found the following in my Person class:
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string DisplayName { get; set; }
public bool IsEmployee { get; set; }
public Nullable<int> OrganizationId { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
So this led me to (and I have no idea why) try to simple remove the virtual keyword on the relationship with Contact and POOF!!!! Like magic the service was up and running just as it was with the single Person table being the only class from the model.
So my question is this... why?
Why when I remove the virtual keyword does the WCF service suddenly work? And what does this mean in regards to the relationship between Person and Contact?
I have a theory that the reason this happens is because now by removing the virtual keyword I have somehow broke the connection between Person and Contact in such a way that the service is no longer trying to pull in any more information than just the Person... and thus doesn't break?
I apologize for my naivety on this matter but would greatly appreciate a little clarity.
Thanks in advance.

The virtual keyword tells Entity Framework that it can (if you tell it too) use Lazy loading to load the elements in the collection when you query the collection.
So removing the virtual keyword means that Lazy loading will not work any more. But if you're fine with that then that's great.

Related

Update Entity without affecting its Navigation Property

in .net core 3.1
if i has a model class that has a navigation property inside of it,
public class Department
{
public int ID { get;set; }
public string Name { get; set; }
public List<Student> Students { get; set; }
}
public class Student
{
public int ID { get; set;}
public string Name { get; set;}
}
According to the models before what is the difference between .AsNoTracking() and with tracking related to the Navigation Property
For Example
if i retrieve object of Department with no tracking will it populate the Student list or not and the same for withTracking
and another thing is
if i want to update the Department without affecting the Student list inside of it, how should i achieve that.
and last this because i have been through the docs without understanding
what EntityState.Detached means
and the difference between DBSet.Attach() and DBSet.Update()
There is a fairly good article that describes the behavior of tracking here: https://learn.microsoft.com/en-us/ef/core/querying/tracking.
As for Detach/Attach you can use the following as a guide. The example will provide some commentary around its usage.
https://learn.microsoft.com/en-us/dotnet/api/system.data.objects.objectcontext.detach?view=netframework-4.8
https://learn.microsoft.com/en-us/dotnet/api/system.data.objects.objectcontext.attach?view=netframework-4.8&viewFallbackFrom=netcore-3.1
As for updating without affecting the students what I usually do is make sure that I use the .Include(x=> x.Students) then perform the update on the entity object I am interested in and save those changes to the context.
Lastly there is a fantastic book that you can also read called Entity Framework Core in action. I found it very helpful when starting out with EF.
Hope this helps. Good Luck.

One-to-many relationship in Entity Framework without FK

I inherited a project with all the one to many relationships created in this fashion
[Table("A")]
public partial class A
{
public int Id { get; set; }
public int Something {get; set; }
public virtual ICollection<B> B { get; set; }
}
[Table("B")]
public partial class B
{
[Key]
public int Id { get; set; }
public int Something {get; set; }
[Required]
public virtual A A { get; set; }
}
What struck me was the lack of a int Foreign Key property in the B model.
Entity Framework must create it because they exist in our database.
Can anyone explain first why this is happening and two if this can cause problems with lazy loading?
EntityFramework by default looks for the name "id" and makes it a key. You could specify the decoration to make it faster, since it does not have to guess the key of the table.
I don't believe it affects lazy loading since lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook
Sources:
Microsoft Key
Microsoft Lazy Loading
Foreign key properties are not required by the EF. EF can build hidden fields basing on object relations (and I usually use this configuration because I think is more POCO). The only issue is that in some cases, the exception raised during validation or SaveChanges is more cryptic, otherwise everything works fine.
About foreign key column names, you can configure them using fluent api (Map + MapLeftKey, MapRightKey and MapKey methods).

Two separate applications, two databases, one IOC. How to share membership?

I am making a website and have two databases, one for my application, and another for an external application. By external, I mean it is an opensource application that I want to work with. The application is MVCForum
This external application already has membership integrated. It uses dependency injection (Unity) and uses Entity Framework. Obviously I don't want to destroy the project and would like to be able to have my application run alongside the MVCForum so that when/if it ever gets updated, I can run the updates without too much trouble and without it affecting my application.
So, here is the problem:
I have a MembershipUser class part of an application, call it externamApp.
namespace MVCForum.Domain.DomainModel
{
public partial class MembershipUser : Entity
{
public MembershipUser()
{
Id = GuidComb.GenerateComb();
}
public Guid Id { get; set; }
public string UserName { get; set; }
public virtual IList<MembershipRole> Roles { get; set; }
//I need to have this. A user has one or many trips
public virtual IList<Trip> Trips{ get; set; }
}
}
I also have my own app, let's call it myApp. Within my app, I have a class called Trip.
namespace MyApp.Domain.DomainModel.TripModels
{
public partial class Trip : Entity
{
public Trip()
{
Id = GuidComb.GenerateComb();
}
public Guid Id { get; set; }
public virtual MembershipUser User { get; set; }
public virtual TCCategory Category { get; set; }
}
}
As you can see, MembershipUser has a property:
public virtual IList<Trip> Trips{ get; set; }
This is because a user can have one to many trips...
You can also see that Trip has a property:
public virtual MembershipUser User { get; set; }
But, I would also like to have a user attached to my Trip class.
When Entity Framework pulls an object from the database, it links with that object all the sub-objects. For example, when it pulls out MembershipUser, it will have all the roles of that user attached - because of this property (and the mapping obviously):
public virtual IList<MembershipRole> Roles { get; set; }
I realize that this is a circular reference, but I am hoping someone with more knowledge can offer a good suggestion or perhaps even a solution.
Like you said and well you have a circular dependency, it is not good to have such dependencies and you should not create solutions with that kind of approach.
The first solution that comes to my mind is that you create a new project (a library) that contains information about your membership (I suggest you to go further and create a Data library containing all the data related classes), that way you can reference that project from both your applications and and avoid the circular dependency.

Entity Framework Code First Relationships; what am I missing?

I'm experimenting with EF5 Code First and I am using the models (show below).
When I look at the database that is created, I am confused because I do not see anything in the Track table that points to the Category table. Category has a FK pointing back to Track but that means that there are going to be duplicates of the categories?
A little background: I am trying to build a model that has tracks and every track can have 1 to N Categories. All of the categories are already defined, that is they are basically a lookup and I plan to create them in the seed method when database is created.
I think I am not understanding something obvious... When I query a track, how will I know what category it contains?
Thx
public class Track : IAuditInfo
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Data { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Category> Categories { get; set; }
public Track()
{
Categories = new List<Category>();
}
}
public class Category
{
public Int32 Id { get; set; }
public Boolean IsVisible { get; set; }
public String DisplayName { get; set; }
}
Your current model is a one-to-many relationship between tracks and categories.
This usually implemented, as you have noted that entity framework does, using a foreign key on the many side (category) to the one side (track).
If I understand you correctly, what you want is a many-to-many relationship. Many tracks can be related to the same category, and a single track can belong to many categories.
To let entity framework understand that you want a many-to-many relationship you can simply add a ICollection property to your category class.
So both your classes should have a collection of the other class.
I.e. tracks have many categories and categories have many tracks.
For more information you can also see: http://msdn.microsoft.com/en-us/data/hh134698.a.nospx
Olav is right, your data model at the moment is not telling Entity Framework that there is a many-to-many relationship in there.
The simplest way to resolve this is to add
public virtual ICollection<Track> Tracks { get; set; }
to your Category class.
However... You may not want to pollute your domain model with artefacts that are not relevant to your domain. More importantly, when you do it this way, it is up to Entity Framework to figure out what to call the binding table. Prior to EF6 this naming is non deterministic (see http://entityframework.codeplex.com/workitem/1677), which may mean that two different machines compiling the same code will decide on different names for that table and cause some interesting migration problems in your production system.
The answer to both problems is to always explicitly manage many-to-many relationships with Fluent Configuration.
In your Data Context class, override the OnModelCreating, something like this:
public class MyDb : DbContext
{
public IDbSet<Track> Tracks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Track>()
.HasMany(t => t.Categories)
.WithMany()
.Map(c => c.ToTable("CategoriesForTracks"));
}
}
If you do this, you don't need to add a navigation property to your Category class, though you still can (if you do, you should use the overload for WithMany that allows you to specify a property).
Relationships between entities and how to map that to a relational database is inherently hard. For anything other than the simplest parent-child relationships you will want to use the fluent API to make sure you actually get what you want.
Morteza Manavi has a really good blog series describing relationships in EF Code First in exhaustive detail.
NOTE
You should usually make navigation properties virtual. So, you should change your Category class like this:
public virtual ICollection<Category> Categories { get; set; }
In theory, not making it virtual should just cause eager loading rather than lazy loading to happen. In practice I have always found lots of subtle bugs appearing when my navigation properties are not virtual.

What would make Entity Framework / Upshot believe my object graph "contains cycles"?

I am testing Knockout 2.1.0 and Upshot 1.0.0.2 with Entity Framework 4.3 (Code-First) and am running into the following error:
{"Object graph for type
'System.Collections.Generic.HashSet`1[[KnockoutTest.Models.Person,
KnockoutTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'
contains cycles and cannot be serialized if reference tracking is
disabled."}
I am using a fairly typical model for testing with some basic parent-child entities.
public class Client
{
public Client()
{
Projects = new HashSet<Project>();
Persons = new HashSet<Person>();
}
[Key]
public int ClientId { get; set; }
[Required]
[Display(Name = "Client Name", Description = "Client's name")]
[StringLength(30)]
public string Name { get; set; }
public ICollection<Project> Projects { get; set; }
public ICollection<Person> Persons { get; set; }
}
public class Project
{
public Project()
{
}
[Key]
public int ProjectId { get; set; }
[StringLength(40)]
public string Name { get; set; }
public int? ClientId { get; set; }
public virtual Client Client { get; set; }
}
public class Person
{
public Person()
{
PhoneNumbers=new HashSet<PhoneNumber>();
}
[Key]
public int PersonId { get; set; }
[Required]
[Display(Name="First Name", Description = "Person's first name")]
[StringLength(15)]
public string FirstName { get; set; }
[Required]
[Display(Name = "First Name", Description = "Person's last name")]
[StringLength(15)]
public string LastName { get; set; }
[ForeignKey("HomeAddress")]
public int? HomeAddressId { get; set; }
public Address HomeAddress { get; set; }
[ForeignKey("OfficeAddress")]
public int? OfficeAddressId { get; set; }
public Address OfficeAddress { get; set; }
public ICollection<PhoneNumber> PhoneNumbers { get; set; }
public int? ClientId { get; set; }
public virtual Client Client { get; set; }
}
public class Address
{
[Key]
public int AddressId { get; set; }
[Required]
[StringLength(60)]
public string StreetAddress { get; set; }
[Required]
[DefaultValue("Laurel")]
[StringLength(20)]
public string City { get; set; }
[Required]
[DefaultValue("MS")]
[StringLength(2)]
public string State { get; set; }
[Required]
[StringLength(10)]
public string ZipCode { get; set; }
}
public class PhoneNumber
{
public PhoneNumber()
{
}
[Key]
public int PhoneNumberId { get; set; }
[Required]
[Display(Name = "Phone Number", Description = "Person's phone number")]
public string Number { get; set; }
[Required]
[Display(Name = "Phone Type", Description = "Type of phone")]
[DefaultValue("Office")]
public string PhoneType { get; set; }
public int? PersonId { get; set; }
public virtual Person Person { get; set; }
}
My context is very generic.
public class KnockoutContext : DbContext
{
public DbSet<Client> Clients { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<Person> Persons { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<PhoneNumber> PhoneNumbers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
I also have a little bit of sample data--though it should not be relevant.
protected override void Seed(KnockoutContext context)
{
base.Seed(context);
context.Clients.Add(new Client
{
Name = "Muffed Up Manufacturing",
Persons = new List<Person> {
new Person {FirstName = "Jack", LastName = "Johnson",
PhoneNumbers = new List<PhoneNumber>
{
new PhoneNumber {Number="702-481-0283", PhoneType = "Office"},
new PhoneNumber {Number = "605-513-0381", PhoneType = "Home"}
}
},
new Person { FirstName = "Mary", LastName = "Maples",
PhoneNumbers = new List<PhoneNumber>
{
new PhoneNumber {Number="319-208-8181", PhoneType = "Office"},
new PhoneNumber {Number = "357-550-9888", PhoneType = "Home"}
}
},
new Person { FirstName = "Danny", LastName = "Doodley",
PhoneNumbers = new List<PhoneNumber>
{
new PhoneNumber {Number="637-090-5556", PhoneType = "Office"},
new PhoneNumber {Number = "218-876-7656", PhoneType = "Home"}
}
}
},
Projects = new List<Project>
{
new Project {Name ="Muffed Up Assessment Project"},
new Project {Name ="New Product Design"},
new Project {Name ="Razor Thin Margins"},
new Project {Name ="Menial Managerial Support"}
}
}
);
context.Clients.Add(new Client
{
Name = "Dings and Scrapes Carwash",
Persons = new List<Person> { new Person {FirstName = "Fred", LastName = "Friday"},
new Person { FirstName = "Larry", LastName = "Lipstick" },
new Person { FirstName = "Kira", LastName = "Kwikwit" }
},
Projects = new List<Project>
{
new Project {Name ="Wild and Crazy Wax Job"},
new Project {Name ="Pimp Ride Detailing"},
new Project {Name ="Saturday Night Special"},
new Project {Name ="Soapy Suds Extra"}
}
}
);
IEnumerable<DbEntityValidationResult> p = context.GetValidationErrors();
if (p != null)
{
foreach (DbEntityValidationResult item in p)
{
Console.WriteLine(item.ValidationErrors);
}
}
}
}
Basically, whenever I attempt to use an "Include" from Client, Person, Project, etc. I get a similar error to the one listed above.
namespace KnockoutTest.Controllers
{
public class ClientController : DbDataController<KnockoutTest.Models.KnockoutContext>
{
public IQueryable<Client> GetClients()
{
return DbContext.Clients.Include("Persons").OrderBy(o => o.Name);
}
}
public class ProjectController : DbDataController<KnockoutTest.Models.KnockoutContext>
{
public IQueryable<Project> GetProjects()
{
return DbContext.Projects.OrderBy(o => o.Name);
}
}
public class PersonController : DbDataController<KnockoutTest.Models.KnockoutContext>
{
public IQueryable<Person> GetPersons()
{
return DbContext.Persons.Include("Client").OrderBy(o => o.LastName);
}
}
public class AddressController : DbDataController<KnockoutTest.Models.KnockoutContext>
{
public IQueryable<Address> GetAddresses()
{
return DbContext.Addresses.OrderBy(o => o.ZipCode);
}
}
public class PhoneNumberController : DbDataController<KnockoutTest.Models.KnockoutContext>
{
public IQueryable<PhoneNumber> GetPhoneNumbers()
{
return DbContext.PhoneNumbers.OrderBy(o => o.Number);
}
}
}
Can you see any reason why .NET should be complaining about this model?
Regardless, what options do I have to work around it?
Thank you for any assistance!
The short answer is that Steve Sanderson's demonstration of Knockout, Upshot, and Entity Framework 4.x Code-First to build a Single Page Application was (while great!!!) maybe a little misleading. These tools do not play nearly as nicely together as they appear at first glance. [Spoiler: I do believe there is a reasonable workaround but it involves stepping outside of the Microsoft arena ever-so-slightly.]
(For Steve's fantastic Single Page Application (SPA) presentation, please visit http://channel9.msdn.com/Events/TechDays/Techdays-2012-the-Netherlands/2159. It is well worth a watch.)
In most any Web application, We conceptually need to move and manipulate data in the following way:
Data Source (often a Database) -> Web Application -> Browser Client
AND
Browser Client -> Web Application -> Data Source (often a Database)
In the past, manipulating data to receive it from and transmit it to the database was a real nightmare. If you have to be around in the .NET 1.0/1.1 days, you may recall a development process that included steps like:
Manually defining a data model
Creating all of the tables, setting up relationships, manually defining indexes and constraints, etc.
Creating and testing stored procedures to access the data - generally manually specifying each field to be included in each procedure.
Create POCO (Plain Old CLR Objects) to hold the data
Code to open a connection to the database and iteratively recurse each record returned and map it into the POCO objects.
This was just to get the data into the system. Going back the other way we had to repeat several of these steps in reverse order. The point is that database coding was very time consuming (and really quite boring). Obviously, a number code generation and other tools came along and simplified things.
The real breakthroughs were with NHibernate, Entity Framework 4 (Code-First approach), and other similar ORM tools which (almost) completely abstracted the database from the developer. These tools not only increased development speed, but also improved overall code quality since their were fewer opportunities to mistakenly introduce bugs.
Now, in many applications, connectivity to and interaction with a database is (almost) an afterthought since most database details are hidden away.
Microsoft has also provided Upshot.js and the WebAPI with the idea that these two tools, when used in conjunction with one another, are going to revolutionize the communication between the server and the browser in the same way that NHibernate and Entity Framework 4 have done between the server and the database.
This would indeed be a very worthy accomplishment--especially as clients are pushing for more interactive Web applications.
One of the main stumbling blocks that prevents developers from moving more of the user interface to the (browser) client is the significant amount of coding required. Some of the steps include:
Transmit the data to the client (usually in JSON format)
Map all of the properties from the .NET objects into JavaScript objects
Re-create all of the meta-data about the object and its properties
Bind that data to the elements in the client browser
Monitor changes
Re-map the data (for sending back to the server) once it has been modified
Transmit the data back to the server
This really seems quite like "deja vu" because it is quite similar in complexity to the legacy processes for getting data into and out of a database.
Depending on how the Web application is configured, there may be additional fun mapping the data once it returns to the server to actual database objects. (This will be the case more often than not.)
This server->client->server data transmission requires a lot of coding and offers many opportunities for unexpected challenges Don't forget how much fun it is to debug JavaScript! (Ok, it's less painful now than it was a couple of years ago, but it is still not as developer-friendly as debugging C# code in Visual Studio.)
Steve Sanderson's presentation on Single Page Applications offers a far different (and better) solution.
The idea is that WebAPI, Upshot.js, and Knockout would be able to seamlessly deliver data to and receive data from a browser client while providing a highly interactive user experience. Wow! Doesn't that make you just want to reach out and hug someone?
While this idea is not new, it is one of the first serious efforts I think have seen to really do this in .NET.
Once the data is delivered through the WebAPI and reaches the client (via Upshot), then frameworks like Knockout would be able to consume the data and deliver the very high level of interactivity cutting edge Web applications require. (While it may not be immediately clear, what I describing are applications which do not primarily function by loading "pages" but rather by primarily communicating JSON formatted data through AJAX requests.)
Any tool that cuts down on all of this coding is obviously going to be quickly embraced by the developer community.
Upshot.js (the renamed and upgraded version of RIA/JS) was supposed to take care of several of the mundane tasks listed above. It is supposed to be the glue between the WebAPI and Knockout. It is intended to dynamically map objects which are tansmitted in JSON or XML from .NET and also expose the associated meta-data for things like object properties, required fields, field lengths, display names, descriptions, etc. (The meta-data is what allows the mapping and MAY be accessed for use in validation.)
Note: I am still uncertain how to access the upshot meta-data and tie it to a validation framework like jQuery validation or one of the Knockout validation plugins. This is on my todo list to test.
Note: I am uncertain which of these types of meta-data are supported. This is on my todo list to test. As a side note, I also plan to experiment with meta-data outside of System.ComponentModel.DataAnnotations to also see if NHibernate attributes are supported as well as custom attributes.
So with all of this in mind, I set out to use the same set of technologies that Steve used in his demo in a real-world Web application. These included:
Entity Framework 4.3 using Code-First approach
ASP.NET MVC4 with WebAPI
Upshot.js
Knockout.js
The expectation is that all of these technologies would function well together because a) they are the latest Microsoft tools (with the exception of the open source Knockout) and because Steve Sanderson, currently of Microsoft, used them together in major Microsoft presentation demonstrating the development of single page application.
Unfortunately, what I found in practice was that Entity Framework 4.x and Upshot.js view the world in very different ways and their orientations are somewhat more contradictory than complementary.
As mentioned, Entity Framework Code First does a really fantastic job allowing developers to define highly functional object models which it near-magically translates into a functional database.
One of the great features of Entity Framework 4.x Code First is the ability to navigate from a parent object to a child AND navigate from a child object back to its parent. These two-way associations are a cornerstone of EF. They save a tremendous amount of time and greatly simplify development. Moreover, Microsoft has repeatedly touted this functionality as great reason to use Entity Framework.
In Scott Guthrie's, blog post (http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx) where he initially introduced and explained EF 4 Code First approach, he demonstrates the concept of two-way navigation with the following two classes:
public class Dinner
{
public int DinnerID { get; set; }
public string Title { get; set; }
public DateTime EventDate { get; set; }
public string Address { get; set; }
public string HostedBy { get; set; }
public virtual ICollection<RSVP> RSVPs { get; set; }
}
public class RSVP
{
public int RsvpID { get; set; }
public int DinnerID { get; set; }
public string AttendeeEmail { get; set; }
public virtual Dinner Dinner { get; set; }
}
As you can see, Dinner contains an association with RSVPs AND RSVPs contains an association to Dinner. There are countless other examples of this on the Internet occurring in many variations.
Because these two way associations are such a core feature of Entity Framework, a reasonable person might expect that Microsoft would support this functionality in the library (Upshot.js) it uses to bring data from a .NET server application to the client. If the functionality was not supported, that is likely something they would want to share as it would significantly key architectural decisions and would most like not work with any properly designed EF 4 Code First implementation.
In my test code (listed in the original question above), I naturally assumed normal EF Code-First functionality (like two-way binding/navigation) was supported because that is what the presentation appeared to show.
However, I immediately started receiving nasty little run-time errors like:
"Object graph for type
'System.Collections.Generic.HashSet`1[[KnockoutTest.Models.Person,
KnockoutTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'
contains cycles and cannot be serialized if reference tracking is
disabled."
I tried numerous different approaches to try and fix the problem. Based on my ideas and my reading, here are a few of the failed solutions I attempted.
Removed the association from one side of the relationship. This is NOT a good solution because it is very handy to be able navigate in each direction between parent and child. (This is probably why these associative properties are referred to as navigation properties.) Remove the relationship from either side had side effects. When the relationship was removed from the parent, the ability to navigate a list of children was also removed. When the relationship was removed from the child, .NET provided me with another friendly error.
"Unable to retrieve association information for association
'KnockoutTest.Models.Client_Persons'. Only models that include foreign
key information are supported. See Entity Framework documentation for
details on creating models that include foreign key information."
Just in case the issue was the result of the system becoming confused about there being a foreign key, I explicitly specified a [ForeignKey] attribute on the child entity. Everything compiles but .NET returns the "Object graph for type... contains cycles and cannot be serialized..."
Some of my reading indicated that adding an attribute like [DataContract(IsReference = true)] in WCF might keep .NET from getting confused about cyclical references. That's when I get this beauty.
"The type 'KnockoutTest.Models.Person' cannot be serialized to JSON
because its IsReference setting is 'True'. The JSON format does not
support references because there is no standardized format for
representing references. To enable serialization, disable the
IsReference setting on the type or an appropriate parent class of the
type."
This error is very important because it basically tells us that we NOT going to be able to use Upshot AND Entity Framework Code-First together in their normal configuration. Why? Entity Framework is designed to utilize two-way binding. However, when two-way binding is implemented, Upshot says that it cannot handle cyclical references. When cyclical references are managed, Upshot basically says that it cannot handle references between parent and child objects because JSON doesn't support it.
When I watched Steve's demo, I recalled that he DID have a relationship between Customers and Deliveries. I decided to go back and take a much closer look at his object model.
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
public class Delivery
{
// Primary key, and one-to-many relation with Customer
public int DeliveryId { get; set; }
public virtual int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
// Properties for this delivery
public string Description { get; set; }
public bool IsDelivered { get; set; } // <-- This is what we're mainly interested in
What we find is that in Steve's demo is that his relationship only goes one way, and it binds the child to the parent rather than the parent to the children.
In this demo, it kind of works. However, in many real-world applications, this approach makes data access impractical. Take, for example, the demo scenario I included in my original question. We have:
Clients
Projects
Persons
Addresses
PhoneNumbers
Most developers, I think would not want to start their query from addresses or phone numbers. They would expect to be able to select a list of clients or Projects or Persons and then navigate to a list of its descendants.
I am not 100% certain that is impossible to use entities which have two-way binding enabled, but I am unaware of any configuration that is likely to yield success using only the Microsoft tools.
Fortunately, I do think there is a solution (which takes care of the cyclic dependency issue) which I plan to test in the next few days. That solution is... JSON.Net.
JSON.Net supports cyclic dependencies and maintains references to child objects. If it works as expected, it will take care of two of the errors I got in my testing.
Once I have tested, I will report results here.
I think Steve's demo was brilliant, and I loved his demo. I believe Knockout is amazing. I am very thankful for where Microsoft seems to be going. If there are noteworthy limitations in the tool, I think Microsoft maybe should have been more forthcoming with them.
I don't mean to be overly critical of Microsoft (and definitely am not critical of Steve at all) because I think they have done a wonderful job. I love what upshot promises and am looking forward to seeing where it goes.
I would really love to see someone take upshot and re-factor it (and the WebAPI) so that it can integrate fully with Entity Framework without the use of a third-party tool.
I don't know if a similar tool exists for NHiberbnate, but I would love to maybe even see someone extend upshot to integrate with (or develop a similar library for) NHibernate. By extend, I am primarily talking about consuming the meta-data from NHibernate.
When I test JSON.Net, I also plan to test NHibernate as well.
If you expose navigation property on both sides of the relation you will get a cycle. For example Client instance contains collection of related Project instances and those Project instances contains navigation property back to their principal Client instance = cycle.
The workaround is either using navigation property only on one side of the relation, using serialization which can resolve cycles out of the box or exclude navigation property on one side from serialization (try to mark it with IgnoreDataMemberAttribute).

Categories

Resources