I am looking for the simplest, best-practice approach to building a web app using EF database-first approach and the MVC 4.5/Web API. My application will use the angular framework to make ajax calls to retrieve json data on demand.
Currently, I'm getting excessive and ill-structured data in response to my api calls and would like to know how to go about cleaning that up.
The application will provide job tracking so I have tables like:
Person
Id
Name
Email
Department : FK -> Department
Department
Id
Name
Jobs
Id
RequestedBy : FK -> Person
AssignedTo : FK -> Person
JobHistory
Id
JobId : FK -> Jobs
So, ideally when I call
$http.get(/api/People)
I'd get:
[{Name: 'alice', Email: 'alice#here.com', Department: 'ABC'},
{Name: 'bob', Email: 'bob#here.com', Department: 'CDE'}]
I suppose this means that normally the principal entities will be eagerly loaded but not all the other tables with dependencies to Person. But there will be times when I want to use those back references to get all the jobs an individual may be working on or or has requested.
If possible, I'd rather not have to resort to things like modifying the T4 templates. While that is clever, it is not very flexible. Also, I dislike the idea of being given a power tool and told that I have to study the schematics and rewire the insides before using it. I'm not a professional developer and really want to focus on the job, not the tool; Linq2Sql was very good for that. Of course, if modifying the innards is the right answer, I'll take it.
This seems like such a straightforward use of the Web API I feel sure I'm missing something obvious.
Thanks
Edit
Additional info that is most likely causing part of my problem. At some point, to get the above to work, I added the following to Global.asax.cs:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;
With eager loading, the two fields in Jobs that refer to Person result in a circular reference. The above code allows Json.Net to serialize it but fills the json with unwanted data. I believe the solution is to somehow switch to lazy loading but can this be done in the database first approach?
So, oddly enough, to get lazy loading to work with database first, I had to set the "Lazy Loading Enabled" to false in the properties of the ConceptualEntityModel.
I then added
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
in Application_Start() and removed the PreserveReferencesHandling as mentioned above in the question. That gave me the clean json I wanted from my model.
One difficulty still remains but barring a better response, I will work around it. In my PersonRepository I use:
public IEnumerable<Person> GetAll() {
return db.People
.Include("C_Departments")
.AsNoTracking();
}
To get the people along with their Departments. Unfortunately, if there is a navigation property in the Departments entity pointing back to people, it eagerly loads all members of the department along with the department info. In other words, at the .Include, lazy loading stops again.
Related
Using Durandal, I have two view models within my app, let's say vmCompanies & vmEmployees. I have two views, one for each view model and within each, you can see all companies & all employees.
However, when I'm loading my employees, in the DB they have an ID for which company they are employed by. What I'd like to do is the following pseudo:
From within the vmEmployees, get a reference to the vmCompanies
If the vmCompanies has already been initialized (which I know it is 99% of the time), get a reference to it so I can use something like linq.js to find the specific company this employee works for
If the vmCompanies has not been initialized (aka: activated), do so
This way I can avoid the requirement of the vmEmployees having it's own internal cache of companies. So far I've been unable to figure out how in Durandal to query and ask "give me this view model that you've already loaded." It seems like it has that internally because when I navigate between views, they are cached and not reloaded (same with the VMs)... I've just so far been unable to see how I can do it.
You can manually require() a view model by its ____moduleId____. As long as your view model module returns an object and not a function, you'll be dealing with a singleton, so you can be sure you'll get the correct instance.
If you're not sure what the __moduleId__ is, you can use this chrome extension to view your view model's properties, including __moduleId__.
However, instead of manually instantianting VMs, a better alternative may be to create a separate module that you use to store the cached companies. I have a data module with its own internal cache that I use generically for this purpose, but you could create one specifically for companies, and store that information in it. It could even be responsible for loading its own data, if that's appropriate.
Here's some simple code to help explain:
Note this uses the sugar syntax for require JS - if you're using the array-based syntax, you'll need to translate accordingly. I can help if needed.
//companies-cache.js
define(function(require){
//some auto-loading logic here, if you wish
var companies = ko.observableArray([]);
return {
companies: companies
};
});
//vmCompanies, vmEmployees
define(function(require){
var companiesCache = require("viewModels/companies-cache");
//additional properties for this specific VM
...
return {
companies: companiesCache.companies
};
});
I have encountered a LINQ issue and hope that you can help me to figure it out.
Here is what is happening.
I get an IQueryable<LicenseEntity> of entities from the repository.
I look at the fields in these entities and see that they contain valid data. There is a field for a related entity Customer in the LicenseEntity. It contains valid data, too, because I loaded it with the LicenseEntity.
I use .Select to project each LicenseEntity to a LicenseViewModel.
For each LicenseEntity, a LicenseEntity is passed into AutoMapper.Mapper.Map and is loaded into a LicenceViewModel entity.
After all of the entities have been processed, when I look at the list of LicenseViewModels in the debugger, it reports a null reference exception and there are no items to view.
To determine whether AutoMapper what causing my problem, I replaced it with a MapMe(). When I stopped at the return statement in MapMe and looked at the s parameter, which is the original entity, I found that the data in it is okay except that the customer field is now null. I assume that Select has done something that I don't know about.
How I can make Select retain all of the information in the original entity when it is doing its projection? Our solution cannot materialize the list because it may be very, very large. I've included my test code below and would really appreciate your help.
// Get the IQueryable<LicenseEntity> list of licenses from the repository.
var list = LicenseRepository.List();
// Convert the IQueryable<LicenseEntity> to an IQueryable<LicenseViewModel>
var vmlist = list.Select(x => MapMe(x, new LicenseViewModel()));
//var vmlist = list.Select(x => AutoMapper.Mapper.Map(x, new LicenseViewModel()));
// This function was used to see the LicenseEntity that was passing into Map().
// I discovered that the entity has all the correct data except for a related
// entity field, which was present in the original LicenseEntity before
public LicenseViewModel MapMe(LicenseEntity s, LicenseViewModel d)
{
return d;
}
The following code works properly however it materializes the entities, which we cannot do.
List<LicenseViewModel> vms = new List<LicenseViewModel>();
foreach (var item in list)
{
var vm = AutoMapper.Mapper.Map(item, new LicenseViewModel());
vms.Add(vm);
}
You've tagged this LINQ-to-Entities but there's no mention of the underlying technology in the text. But it's very likely that the problem is caused by lazy loading of associated objects.
This is a design choice that applies to most ORMs that I've worked with. When you load an object, connected objects are not loaded by default. If they were loaded by default it's quite clear you'd quickly break everything
when you load a Licence, the related Customer is automatically loaded
when the Customer is loaded all related objects are loaded - Company, Address, all other Licences, etc
for each of those objects, every related object is loaded...
The answer is that you need to specify which related objects to load. In the Entity Framework you do this using the Include method.
Because you are using a repository abstraction you might find this more difficult than it needs to be, but without knowing more I can't give any advice. This type of functionality - pretty basic stuff - is always a difficulty with repositories and 'unit-of-work' patterns.
I think your mapping should be more like:
var vms = Mapper.Map<List<LicenseEntity>, List<LicenseViewModel>>(list);
(ie - you don't need the foreach loop).
But unfortunately I doubt very much that that'll fix your issue as I suspect that'll also materialize your entities.
I've found the solution for projecting domain entities to viewmodels. If you are struggling with the same kind of issue as I had, please review the following links:
http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/
http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code
By the way, in one one my domain entities, I had a partial class with some "calculated" properties... Properties whose values were generated from other fields in the database record. These cannot be in the domain entity because they will interfere with the aforementioned solutions. I moved them to my ViewModel class, which is where they were really required, and all is well.
Hope that this info helps...
Mike
I'm not sure which title would be more descriptive, so I kept it this way. I feel kinda lost in the world of MVC.
FYI: I use PHP, but that doesn't seem of much importance in this particular case.
My problem is as follows:
I have a UserController containing the following methods:
login
new
show
overview
Then I have my UserModel, containing - in this case - roughly the same methods:
login
create
fetch
The problem is: what do I keep my user data in once fetched from the database (or XML feed, or webservice, or whatever...)? I thought of a User 'business object', containing all (relevant) properties from the database. Then, when fetching the users from the database, I instantiate a new User object for each user I fetch. If only 1 user returned from the search, I return only the User object. If more users get returned, I instantiate a UserCollection object containing all User objects - in which case I can iterate over them, etcetera.
Is that a correct way of dealing with users in MVC?
And then: imagine I made an overview of 10 users. 5 of them get edited at once - imagine a status modification using checkboxes. How do I process the changes? Do I loop over all changed User objects and store them back in the database? Then it would start to look like an implementation of the Active Record Pattern, something I'm told not to use.
I hope someone can clarify which classes and/or methods I'd need to solve this 'architectural' problem.
Since it is a rather lengthy discussion. I will give the link to an article that I have written on MVC, trying to explain it in simple terms. You may want to take a look at it.
What is MVC pattern about?
If I understand correctly, your UserModel is a bit off;
the Model part of MVC is intended as a programmatic representation of the real world model.
Meaning- it represents all the properties and actions of the real-world subject. The classic example is the Car class, which has properties such as Wheel, CurrentSpeed, and actions such as GoForward(), GoReverse() etc..
So, in your case, I think your model should be what you described as a 'user business object'.
Your controller would be responsible for fetching the UserModels from storage (or wherever), and updating them back.
your workflow would be something like this:
View would call the Controller's GetUsers.
Controller goes to storage, and fetches a list of UserModels.
Controller returns them to the view.
View displays them in some way.
And the other way around for updating.
The UserModel class would be responsible for logic that pertains to individual users (for example- ChangePassword()).
I have 3 tables in my database - Conference, ActivityTypes and a joining table ConferenceActivities. Each conference may have a zero or many standard activities and each activity may occur in zero or more conferences.
Conference (ConferenceID, ConferenceName)
ConferenceActivities (ConferenceID,ActivityTypeID)
ActivityTypes (ActivityTypeID, ActivityTypeDesc)
I used Entity framework over my existing database, with the .tt templates in a MVC3 app I generated a DbContext and the POCO objects, noting that it generates just two objects, ActivityType and Conference which is nice it seems to understand the intermediary table.
It also adds a collection of activities to my conference
public virtual ICollection<ActivityType> ActivityTypes { get; set; }
And a collection of Conferences to my Activity.
public virtual ICollection<Conference> Conferences { get; set; }
I would like to return using JSON an object matching a specific conference.. that includes the ActivityTypes. Some might described this object as 'shaped' or 'jagged' because it has a collection within it.
I'm using code like the following although I've tried many different variations incase I had the syntax wrong but I still get a runtime error about recursion.
public JsonResult GetConference(int conferenceId)
{
Conference x = context.Conferences.Include("ActivityTypes").FirstOrDefault<Conference>(i => i.EventID == eventId);
return Json(x, JsonRequestBehavior.AllowGet);
}
It doesn't seem to work 'out of the box' and I see a lot of Stack Overflow questions that seem to relate but despite trying numerous different approaches but all create an error about recursion or parameterless constructors.. I am starting to doubt whether this is even possible.
I have tried attaching [IgnoreDataMemberAttribute] or [ScriptIgnore] to different properties to stop it trying to serialise the recursive relationship (I think the form is irrelevant but when your desperate..) and I still get the same error.
I've tried this sort of approach which generates the parameterless error
var thing = from r in context.Conferences
select new
{
r.ConferenceID,
r.ConferenceName,
ActivityTypes = new List<ActivityType>(
(from c in r.ActivityTypes
select new ActivityType()
{
ActivityTypeDesc = c.ActivityTypeDesc,
ActivityTypeID = c.ActivityTypeID
}).Cast<ActivityType>())
};
return Json(thing, JsonRequestBehavior.AllowGet);
Another suggestion was to set the Lazy Loading Enabled property on the model to false.. which made no difference I still get the recursion error.
I've tried removing the Virtual keyword which I belive turns off the lazy loading.. and trying with .Include(..) but again no joy.
Another suggestion was to change the accessor from public to internal however that made no different to the serialization.
Somebody also suggested removing the collection of Conferences from the ActivityType class, not really what I want to do.. but doing that just produces an error `Schema specified is not valid.' anyway.
I am using I believe the latest version nuget and the associated EF, scaffolding and templates.
Is what I'm trying to achieve something that should work out of the box? If so what setting(s) could I be missing and if not, what is the best practise here - the only way I know I can make this work is to build up the object manually myself.
Suggestions or a link to a complete working demo (I've seen a lot of suggestions and code snippets that just don't seem to work!!) appreciated. I'm certain somebody's done this and got it working surely.
don't pass your model classes around. create a viewmodel with the data you need for the view, or, in your case, the json output. i see where you tried projecting it into an anonymous type, but project it into a concrete viewmodel and it should work perfectly.
I've asked the question a few different times in a few different ways and I haven't yet gotten any responses. I'm trying again because I feel like my solution is too convoluted and I must be missing something simpler to do.
Using EF 4.1, POCO, DbContext API, AutoMapper, and Razor in an MVC 3 application.
I have a many-to-many relationship between two of my entities: Proposals and CategoryTags. I can successfully map (Automapper) a Proposal to my ProposalViewModel including the collection of CategoryTags.
In my View, I use javascript to allow the user to add, update, and remove tags by dynamically creating elements, each one that stores the ID of the chosen tag.
I can successfully post my ViewModel back to my controller with it's CategoryTags collection populated (although only with the ID property for each CategoryTag).
When that ViewModel is posted back to my controller, I don't know how to get those tags from the ViewModel and add them to my Model in such a way that db.SaveChanges() updates the database properly.
The only way I've had any success is to disconnect the CategoryTags collection in mapping (by namig them differently), iterate through each tag and manually look it up in my context and then call the .add() method. This is sloppy for a number of reasons which leads me to believe I'm doing it wrong.
Can anyone offer any direction at all?
UPDATE:
For anyone who is interested, my functional code:
Dim p As New Proposal
Dim tempTag As CategoryTag
p = AutoMapper.Mapper.Map(Of ProposalViewModel, Proposal)(pvm)
db.Proposals.Attach(p)
db.Entry(p).Collection("CategoryTags").Load()
For Each ct In pvm.Tags
tempTag = db.CategoryTags.Find(ct.Id)
If tempTag Is Nothing Then
Continue For
End If
If ct.Tag = "removeMe" Then
p.CategoryTags.Remove(tempTag)
Continue For
End If
p.CategoryTags.Add(tempTag)
Next
db.Entry(p).State = EntityState.Modified
db.SaveChanges()
Return RedirectToAction("Index")
The only working way is doing this manually - you can read full description of the problem if you want. The description is related to ObjectContext API but DbContext API is just wrapper suffering same issues (actually DbContext API suffers even more issues in this scenario and because of that I will skip solution with manually setting relationships).
In short. Once you post your data back to the controller you must create new context instance and attach your Proposal and realated CategoryTags. But after that you must inform the context about changes you did. It means you must say context which tags have been added to proposal and which have been removed. Otherwise context cannot process your changes because it doesn't do any automatic merge with data in database.
The easiest way to solve this is loading current Proposal with related CategoryTags from database (= you will have attached instances) and merge incoming data into attached object graph. It means you will manually remove and add tags based on posted values.