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

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.

Related

Using Entity Framework with SQL Server many-to-many relations

Do not know how to use the advanced features of displaying thing here, so please excuse ;-)
The database structure is
User --> UserOwnerR <-- Owner
Also I have several support structures (ex. addresses belonging to a specific owner).
I need to find all addresses to whom a specific user has access because it belongs to on/many owners, but not addresses to whom the user have a owner relation.
n:m relations can be realized without a join table in EF Core 5+.
public class User
{
// user properties
public IEnumerable<Owner> Owners { get; set; }
}
public class Owner
{
// owner properties
public IEnumerable<User> Users { get; set; }
}
You did not specify wether you’re using ef Code first approach (you generated your Schema based on c# classes) or database first approach (generate c# classes from database tables) or none of those (manually set up your entities).
If you are able to change your classes manually, you might add navigation properties. Those might look like this:
public class User
{
// whatever
public IEnumerable<UserOwnerR> userOwners { get; set; }
}
public class Owner
{
// whatever
public IEnumerable<UserOwnerR> userOwners { get; set; }
}
public class UserOwnerR
{
public virtual Owner owner { get; set; }
public virtual User user { get; set; }
}
Now you are able to place conditions while joining those tables together. Use the sql syntax based query option with linq, as it’s easier to connect your tables that way. You might want to take a look at Entity Framework Join 3 Tables to construct your individual query.

Using Entity Framework classes in WCF service without recreating the model

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.

How to store hierarchy in Entity Framework

Take a look at the following UI:
I am allowing the user, who owns an auto shop of some kind, to select what types of service his shop provides. There are 5 levels to my hierarchy and thousands of total entries. Selecting (checking) any skill area automatically selects all sub-areas or child areas. Each area has an ID which for the top level looks like 10000, the next level 11000, etc...
My questions:
How can I achieve this UI with one code base supporting mobile, tablet and desktop interfaces?
How can I represent the hierarchy using Entity Framework for populating the hierarchy as well as storing the user's selections?
The application is in ASP.Net MVC 4 using SimpleMembership.
The best way to have one code base supporting multiple platforms is to use ASP.NET MVC 4 WebAPI (JSON or XML) as a REST web service. You can find more about it here: http://www.asp.net/web-api
There are also a couple of ready-to-use parsers to deserialize the JSON/XML data into C# object on the client side.
Second question seems to be more related to the database schema behind the Entity Framework abstraction. In terms of EF probably the best way to organize your data is to have categories referencing other categories with the last category referencing the actual services. Are you familiar with the concept of database relations, foreign keys, etc?
Edit:
public class Category1{
public string Name { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Category2{
public string Name { get; set; }
public Category1 ParentCategory { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item{
public string Name { get; set; }
public decimal Price { get; set; }
}
This is an example how would you write code-first EntityFramework objects. Category2 has a parent of type Category1 and both categories contain a collection of items. Pluralsight has an excellent course on EF code first which covers all the basics.

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

Wrapping DbSet<TEntity> with a custom DbSet/IDbSet?

First off, I think this is somewhat ridiculous to do but the other members of my team insist upon it and I can't come up with a good argument against it other than "I think it's dumb"...
What we're trying to do is create a completely abstract data layer and then have various implementations of that data layer. Simple enough, right? Enter Entity Framework 4.1...
Our end goal here is that the programmers (I do my best to stay only on the data layer) never want to have to be exposed to the concrete classes. They only ever want to have to use interfaces in their code, aside from obviously needing to instantiate the factory.
I want to achieve something like the following:
First we have our "Common" library of all of the interfaces, we'll call it "Common.Data":
public interface IEntity
{
int ID { get; set; }
}
public interface IUser : IEntity
{
int AccountID { get; set; }
string Username { get; set; }
string EmailAddress { get; set; }
IAccount Account { get; set; }
}
public interface IAccount : IEntity
{
string FirstName { get; set; }
string LastName { get; set; }
DbSet<IUser> Users { get; set; } // OR IDbSet<IUser> OR [IDbSet implementation]?
}
public interface IEntityFactory
{
DbSet<IUser> Users { get; }
DbSet<IAccount> Accounts { get; }
}
From that we then have an implementation library, we'll call it "Something.Data.Imp":
internal class User : IUser
{
public int ID { get; set; }
public string Username { get; set; }
public string EmailAddress { get; set; }
public IAccount Account { get; set; }
public class Configuration : EntityTypeConfiguration<User>
{
public Configuration() : base()
{
...
}
}
}
internal class Account : IAccount
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DbSet<IUser> Users { get; set; } // OR IDbSet<IUser> OR [IDbSet implementation]?
public class Configuration : EntityTypeConfiguration<Account>
{
public Configuration() : base()
{
...
}
}
}
Factory:
public class ImplEntityFactory : IEntityFactory
{
private ImplEntityFactory(string connectionString)
{
this.dataContext = new MyEfDbContext(connectionString);
}
private MyEfDbContext dataContext;
public static ImplEntityFactory Instance(string connectionString)
{
if(ImplEntityFactory._instance == null)
ImplEntityFactory._instance = new ImplEntityFactory(connectionString);
return ImplEntityFactory._instance;
}
private static ImplEntityFactory _instance;
public DbSet<IUser> Users // OR IDbSet<IUser> OR [IDbSet implementation]?
{
get { return dataContext.Users; }
}
public DbSet<IAccount> Accounts // OR IDbSet<IUser> OR [IDbSet implementation]?
{
get { return dataContext.Accounts; }
}
}
Context:
public class MyEfDataContext : DbContext
{
public MyEfDataContext(string connectionString)
: base(connectionString)
{
Database.SetInitializer<MyEfDataContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new User.Configuration());
modelBuilder.Configurations.Add(new Account.Configuration());
base.OnModelCreating(modelBuilder);
}
public DbSet<User> Users { get; set; }
public DbSet<Account> Accounts { get; set; }
}
Then the front-end programmers would be using it such as:
public class UsingIt
{
public static void Main(string[] args)
{
IEntityFactory factory = new ImplEntityFactory("SQLConnectionString");
IUser user = factory.Users.Find(5);
IAccount usersAccount = user.Account;
IAccount account = factory.Accounts.Find(3);
Console.Write(account.Users.Count());
}
}
So that's pretty much it... I'm hoping someone on here might be able to either point me in the right direction or help me out with a good argument that I can fire back at the development team. I've looked at some other articles on this site about EF not being able to work with interfaces and one reply saying that you can't implement IDbSet (which I find kind of curious, why would they provide it if you couldn't implement it?) but so far to no avail.
Thanks in advance for any help!
J
The first argument is that EF doesn't work with interfaces. DbSet must be defined with a real entity implementation.
The second argument is that your entities should not contain DbSet - that is context related class and your entities should be pure of such dependency unless you are going to implement Active record pattern. Even in such case you will definitely not have access to DbSet of different entity in another entity. Even if you wrap set you are still too close to EF and entity never have property accessing all entities of another entity type (not only those related to current instance).
Just to make it clear DbSet in EF has very special meaning - it is not a collection. It is entry point to database (for example each LINQ query on DbSet hits database) and it is in normal scenarios not exposed on entities.
The third argument is that you are using a single context per application - you have a single private instance per singleton factory. Unless you are doing some single run batch application it is definitely wrong.
The last argument is simply practical. You are paid for delivering features not for wasting time on abstraction which doesn't give you (and your customer) any business value. It is not about proving why you should not create this abstraction. It is about proving why you should do it. What value will you get from using it? If your colleagues are not able to come with arguments which have business value you can simply go to your product manager and let him use his power - he holds the budget.
Generally abstraction is part of well designed object oriented application - that is correct. BUT:
Every abstraction will make your application somehow more complex and it will increase cost and time of development
Not every abstraction will make your application better or more maintainable - too much abstraction has reverse effect
Abstracting EF is hard. Saying that you will abstract data access in the way that you can replace it with another implementation is task for data access gurus. First of all you must have very good experience with many data access technologies to be able to define such abstraction which will work with all of them (and in the end you can only tell that your abstraction works with technologies you thought about when you design that). Your abstraction will work only with EF DbContext API and with nothing else because it is not an abstraction. If you want to build universal abstraction you should start studying Repository pattern, Unit of Work pattern and Specification pattern - but that is a big deal of work to make them and to implement them universal. The first step needed is to hide everything related to data access behind that abstraction - including LINQ!
Abstracting data access to support multiple APIs make sense only if you need it now. If you only think that it can be useful in future than it is in business driven projects completely wrong decision and developer who came with that idea is not competent to make business targeting decisions.
When it make sense to do "a lot of" abstraction?
You have such requirement now - that moves burden of such decision to person responsible for budget / project scope / requirements etc.
You need abstraction now to simplify design or solve some a problem
You are doing open source or hobby project and you are not driven by business needs but by purity and quality of your project
You are working on platform (long living retail product which will live for a long time) or public framework - this generally returns to the first point because this type of products usually have such abstraction as requirement
If you are working only targeted application (mostly single purpose applications on demand or outsourced solutions) the abstraction should be used only if necessary. These applications are driven by costs - the target is delivering working solution for minimal costs and in the shortest time. This target must be achieved even if resulting application will not be very good internally - the only thing which matters is if application meets requirements. Any abstraction based on "what if ... happens" or "perhaps we will need ..." increases costs by virtual (non existing) requirements which will in 99% never happen and in most cases initial contract with customer didn't count which such additional costs.
Btw. this type of applications is targeted by MS APIs and designer strategy - MS will make a lot of designers and code generators which will create non optimal but cheap and quick solutions which can be created by people with smaller skill set and are very cheap. The last example is LightSwitch.

Categories

Resources