ASP.NET MVC: Where should this business logic go? - c#

I'm working on my first real MVC application and I'm trying to follow general OOP best practices. I'm refactoring some simple business logic that I had in a controller into my domain model. I've been doing some reading lately and it seems pretty clear that I should put the logic somewhere in a domain model entity class in order to avoid the "anemic domain model" anti-pattern.
The application will allow people to purchase leases for parking spaces. Rates are determined by the length of the spot and whether or not the customer is a member of the business park.
So I have entity classes in my domain model that look like this (simplified):
public class Customer
{
int ID { get; set; }
string Name { get; set; }
bool IsMember { get; set; }
}
public class ParkingSpace
{
int ID { get; set; }
int Length { get; set; }
}
public class ParkingSpaceLease
{
int ID { get; set; }
DateTime OpenDate { get; set; }
DateTime CloseDate { get; set; }
Customer Customer { get; set; }
ParkingSpace ParkingSpace { get; set; }
}
Edit: Just to clarify the LeaseQuote is not an entity class as it is just used to display the cost breakdown to perspective customers and is not persisted anywhere.
public class LeaseQuote
{
int SubTotal { get; set; }
int Discount { get; set; }
int Total { get; set; }
}
Now as a feature of the application I need to be able to generate quotes for different customer and parking space combinations. The quotes will normally be accessed outside the context of actually creating a lease such as when a customer calls up to inquire about a price.
So what is the best way to go about this? Does it make sense to instantiate a new ParkingSpaceLease object inside the controller just to call a GetQuote method on it?
var lease = new ParkingSpaceLease();
var quote = lease.GetQuote(length: 168, isMember: true);
return Json(quote);
Or should the LeaseQuote class have the method?
var leaseQuote = new LeaseQuote();
var quote = leaseQuote.GetQuote(length: 168, isMember: true);
return Json(quote);
It feels strange putting the logic in the actual ParkingSpaceLease class. I guess it feels kind of "heavy" to create a new lease object when I know that I'm not going to actually do anything with it other than access the GetQuote method which seems kind of like a separate service.
So where should the GetQuote method go and why should it go there?

It almost sounds like your LeaseQuote isn't an entity and more of a business level class. I mean, you're not storing it in the database anywhere, are you? And it's not a part of another data object.
When I see this
Now as a feature of the application I need to be able to generate quotes for different customer and parking space combinations. The quotes will normally be accessed outside the context of actually creating a lease such as when a customer calls up to inquire about a price.
I think of a method signature like this
public LeaseQuote GetQuote(Customer customer, ParkingSpace parkingSpace, int length)
But with that in mind, I'd probably also want to store information about the cost of the parking space within the ParkingSpace entity and (if applicable) the customer's discount in the Customer entity.
Where would this stuff go? In a model class (business model, not LINQ or Entity model) that accesses your entities and serves as a provider for your controller.
Now I know that's not using your models exactly as written. And it could just be personal bias. But when I think about data models and data entities, they should not have any addon methods outside of what's coming back from the database. They should just represent the data unaltered as it appears in the database. If you're acting on the data, that belongs in a tier above the data entities.
Update:
What I am curious about from your example is why one would want to pass the full Entity objects (Customer and Parking Space) versus just the properties needed to perform the calculation?
It depends on your standard of code. Exposing the entity itself could be dangerous if the consuming code manipulates the entity. I prefer passing the entity mainly because that's what I'm used to. But I'm also careful not to manipulate the entity on the way in. That, and I think the method signature reflects what the GetQuote method is focused on; it's related to a customer and a parking space.
I could also make the case that if more fields go into the Entity later that can effect the GetQuote method, then the method signature doesn't have to change. In this case, only the implementation for GetQuote has to change.
Short answer: Preference.

Just make GetQuote a static method in ParkingSpaceLease.

I think you may have your object model slightly askew, which would lead to your concern about the lease being the wrong place from which to get a quote. It seems to me that a lease would be wholly composed by the parking space which is being leased, and would be related only to the customer purchasing the lease. IOW:
public class ParkingSpace
{
int ID { get; set; }
int Length { get; set; }
IEnumerable<ParkingSpaceLease> Leases { get; set; }
LeaseQuote GetQuote(Customer customer/*, other relevant parameters */) { ... }
}
public class ParkingSpaceLease
{
int ID { get; set; }
DateTime OpenDate { get; set; }
DateTime CloseDate { get; set; }
Customer Customer { get; set; }
}
public class LeaseQuote
{
//Properties
ParkingSpaceLease GetLease();
}
EDIT I missed the part about the LeaseQuote being a separate class.

Related

Distrubuted domain driven design modeling

I am working on an events ticketing system and I am trying to use DDD. However, I am unsure about how to model some of my aggregates.
I have the main Event aggregate:
public class Event : Entity<Guid>, IAggregateRoot
{
public string Name { get; private set; }
public Organizer Organizer { get; private set; }
public Venue Venue { get; private set; }
public DateTime StartsAt { get; private set; }
public short MinAge { get; private set; }
public bool IsAvailable { get; private set; }
public EventType Type { get; private set; }
//constructor and mehtods
}
and also have the Venue and Organizer Aggregates:
public class Venue : Entity<Guid>, IAggregateRoot
{
public string Street { get; }
public Venue(Guid id, string street) : base(id)
{
Street = street;
}
}
public class Organizer : Entity<Guid>, IAggregateRoot
{
public Organizer(Guid id) : base(id)
{
}
}
My questions are:
I have different services that handle creation and other operations for the Venues and Organizers. The Events service is consuming kafka messages from the other two services and persists the venues and organizers in its own database. I would like to mention that the Venues and Organizers services have a lot more information about their respective entities than the Events service, so it is not a 100% data duplication. Is this the right way to share data between different services?
In the events service, should the Organizer and Venue be considered an aggregate or an entity? Neither of those is accessed on itss own in the context of the Event service.
How should I handle persisting and retrieving entities from the database?
An Entity is something that has an own entity, an unique identifier, that has significance.
You use an aggregate when you have entities (and also value objects) with a strong dependency and has sense manage all of them like a single object. In this form, outside you work with some entity (the aggregate root) that manage all the aggregates entities.
With an example is easier to understand. Suppose you have Invoice entity and InvoiceLine entity. Both of them are important, has a unique identifier... and you may work with them as entities. You have a table in the database for invoices and for the lines. Working with the entities as is, you must control some things like, for example, the total of the invoice. When in any place you change the price of a line, you need have near the invoice, to update the total. It's a very simple example but in more complex scenaries, this kind of things ends with wrong data because in some place, you forget update some related data.
So, it's interesting have and aggregate root that contains this two aggregates and all about them are encapsulated in the root aggregate. You update line price through the aggregate root and root update the total of the invoice. Each entity have a reference to the root.
Usually, the aggregate root has a repository to manage the data of all their aggregates. Entity Framework differ a bit in this point, having a repository per entity.

Where to implement business calculations on a query using Clean architecture

We are re-building our core application trying to use clean architecture and CQRS.
Projects have all been set up.
Currently this is all working
Domain layer currently holds complex models ie. models that simply map to the results from a stored proc.
namespace UHNM.myHotelCore.Domain.Entities.floor
{
public class FloorCx
{
public int FloorId { get; set; }
public int x { get; set; }
public int y { get; set; }
public string FloorCode { get; set; }
public string Floor { get; set; }
}
}
This is all working. We now want to introduce calculations.
We use generally use automapper in our query handler in the application layer to map our complexmodel to our returned DTO.
var floors = _autoMapper.Map<List<FloorDTO>>(_floor.GetFloors(request.FloorId));
Our return model from the API looks like this :-
namespace UHNM.myHotelCore.Application.CQRS.Queries.Floor.DTO
{
public class Floor
{
public int FloorId { get; set; }
public decimal Average { get; set; }
public string FloorCode { get; set; }
public string Floor { get; set; }
}
}
Where average is calculated based on x and y from the complex model.
Using clean architecture, where is generally the best place for these calculations to go? and at what point are they called?
Would they sit within FloorCx?
public class FloorCx
{
public int FloorId { get; set; }
public int x { get; set; }
public int y { get; set; }
public string FloorCode { get; set; }
public string Floor { get; set; }
Public decimalCalculateAverage(int x, int y)
{
//Calculation goes here
}
}
Create a separate class for the calculations?
Or even a separate project for business logic?
From what I understand, your FloorCx is placed in Domain layer. The current shape of that class is a pure anemic model, which is an anti pattern in DDD world.
Not sure if your intention is to follow DDD principles as well, but if it is, then you could introduce repositories that return your FloorCx data models, which then you map to your real business objects. Such business objects should have business behavior, so having calculations inside is a way to go as long as the calculations can be self-contained, meaning they rely on object's internal properties, or accept primitive types (to reduce coupling between your domain model and other resources). When the calculations require more information from the outside world, you can create business services that do them.
Also, in DDD world, AutoMapper and similar solutions are often discouraged, also because your domain objects should not have public setters on their properties, but rather expose constructors or static factory methods that control what is initialized and how, ensuring object's internal consistency.
Having that said, I would create FloorCxDataModel that represents objects fetched from your repository (which abstracts away the stored procedure call and transformations), which then is mapped to a FloorCx business model that exposes methods for calculations and other business behavior. If those calculations depend on external information, for simple types that should work too, but for more complex input parameters I would create a FloorCxCalculationService that resides in Domain layer and does necessary calculations for you.
In the end, you map that business object to your FloorDto (which contains only data, no additional behavior) and return it from your API. In my view, that DTO should also have read-only public properties and have constructor/static factory method that does initialization.

POCO's, behavior and Peristance Igorance

From what I have read POCO classes should be persistence ignorant and should not contain references to repositories.
Q1. Given the above, how would I populate the QuestionBlocks collection? I have read that POCO's should contain behavior so you don't end of with an anemic model, so I'm kind of confused as how one is supposed to do that without persistence. If that's the case then what kind of behavior would you put in a POCO?
Ex:
public class Survey
{
public int SurveyId { get; set; }
public string Title { get; set; }
public int BrandId { get; set; }
public DateTime Created { get; set; }
public List<SurveyQuestionBlock> QuestionBlocks { get; set; }
[ResultColumn]
public string Name { get; set; }
/// <summary>
/// Constructor
/// </summary>
public Survey()
{
Created = DateTime.Now;
QuestionBlocks = new List<SurveyQuestionBlock>();
}
}
I would append another view: POCO states for objects which are not dependent on any framework. The wiki definition of a POJO is much more meaningful to me then the one for POCO:
http://en.wikipedia.org/wiki/Plain_Old_Java_Object
To paraphrase the wiki definition of the POJO, we can say that POCO object might not be forced to:
I. Extend prespecified class:
public class MyClass : AnyFramework.ObjectBase {...
II. Implement prespecified interfaces
public class MyClass : AnyFramework.IHaveDependency {...
III. Contain prespecified attribute
[AnyFramework.KeyAttribute]
public class MyClass {...
Given this (almost anything else is allowed) in the meaning of taking care about the object state. Other words, if object will check Business logic, it is correct.
But any POCO object can be used in a framework. Today it is mostly for ORM which is responsible for persistence. All application tiers are working with POCO objects, while data layer is responsible for loading and persisting (CRUD). This is mostly done via Proxies of these POCO objects.
So, POCO could be used as full business object, which can take care about itself (check correctness of collection items, properties...). This makes it different from DTO
Given the above, how would I populate the QuestionBlocks collection?
When reading from a database, the persistence infrastructure should populate the QuestionBlocks collection - reconstitution. Reconstruction should not invoke behavior, it should only set appropriate fields on the POCO. This is the responsibility of the repository. A repository is typically referenced from an application service, which sets up the stage for invoking entity behavior.
If that's the case then what kind of behavior would you put in a POCO?
The behavior in the POCO entity should be concerned with making changes to the entity itself as well as maintaining invariants - ie ensuring the integrity of the entity. In your example, the simplest kind of behavior on the POCO should be method for adding a new question block to the collection on the survey. Ideally, you would make many of the properties on the survey entity read-only:
public class Survey
{
public int SurveyId { get; private set; }
public string Title { get; private set; }
public int BrandId { get; private set; }
public DateTime Created { get; private set; }
public IList<SurveyQuestionBlock> QuestionBlocks { get; private set; }
public string Name { get; private set; }
public void AddQuestionBlock(string questionBlockInfo)
{
this.QuestionBlocks.Add(new SurveyQuestionBlock(...));
}
public Survey()
{
Created = DateTime.Now;
QuestionBlocks = new List<SurveyQuestionBlock>();
}
}
The persistence layer should be able to set the values of the read-only properties via reflection. You can go a step further and only expose the question blocks collection as a read-only collection to ensure that it can only be modified from within the entity itself.

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).

Dealing with lazy loading outside of an ISession?

I'm using NHibernate to do database access in my application. My ISessions have no persistance, and I'm happy with this as it makes it easier for me to separate my application into different layers. The only difficulty is dealing with lazy loading in a nice way.
I have a model class that looks like this:
public class User {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Country CountryOfBirth { get; set }
public virtual Country CountryOfResidence {get; set; }
}
At the moment, I have CountryOfBirth and CountryOfResidence set to fetch="join". However, as the list of countries in my database is mostly static, I want to cache these values. I changed the CountryOfBirth property to look like this:
Country countryOfBirth;
public virtual Country CountryOfBirth{
get
{
if (country is INHibernateProxy)
countryOfBirth = CountryRepository.GetById(countryOfBirth.Id);
return countryOfBirth;
}
set { countryOfBirth = value; }
}
However, it requires my Model class to know that it is being used by NHibernate, which breaks encapsulation.
Is there a better way to achieve this? For instance, is there a way to get NHibernate to automatically go through my Repository classes if it tries to load a proxy and the session has expired?
Or should I use a different method?
If you want to add cache capabilities then look at NHibernate L2 Cache. Check this tutorial http://nhforge.org/blogs/nhibernate/archive/2009/02/09/quickly-setting-up-and-using-nhibernate-s-second-level-cache.aspx and search for this topic. By using cache you don't polute your model with any NH proxies, repositories, etc.

Categories

Resources