How to manage new Breeze entities which are aggregate roots? - asp.net-web-api

I have a domain model which has a Customer, which in turn has 1 Address (1:1) and 1 or more Phone numers (1:M).
Customer has user supplied PK (a string), while Address and Phone use identity column (server generated).
I am struggling in trying to understand how to manage Breeze entity creation for a "Add new Customer" screen.
The form on the screen allows user to enter Customer, Address, and Phone data.
I am using Durandal and Knockout so my "customeradd.js" viewmodel looks something like this:
// -- snip ---
var customer = ko.observable(),
hasChanges = ko.computed(function () {
return datacontext.hasChanges();
});
var vm = {
hasChanges: hasChanges,
customer: customer,
activate: activate
};
return vm;
function activate() {
customer(datacontext.createCustomer());
}
// -- snip ---
and my "/services/datacontext.js" :
// -- snip ---
breeze.NamingConvention.camelCase.setAsDefault();
var manager = new breeze.EntityManager(config.remoteServiceName);
var hasChanges = ko.observable(false);
manager.hasChangesChanged.subscribe(function (eventArgs) {
hasChanges(eventArgs.hasChanges);
});
function createVehicle() {
return manager.createEntity("Customer");
}
// -- snip ---
My questions are following:
Once I create a Customer, do I need to create Address and list of Phones and add them to Customer entity before making it a KO observable? Or is this done automatically by createEntity() method?
How do I create a Customer but without having to specify the Id? If I set the key to null or '', Breeze complains ("Error: Cannot attach an object to an EntityManager without first setting its key or setting its entityType 'AutoGeneratedKeyType' property to something other than 'None'"). However, if I generate the temp key (using either breeze.core.getUuid() or something else), then it shows up in my Id form field, and I really want the end user to specify it....Do I have to resort to extending the entity with extra field and then do the swapping and validation before saving (I don't like this idea at all)? Is there a better way?
In order to enable/disable buttons on my form I am tracking if there are changes in EntityManager. But every time entity is created, it is automatically in 'added' state so hasChanges is true. What I want is for changes to be picked up only if user edits the form (and therefore makes changes to underlaying entity). What is the best way to approach this?
BTW, I have seen this recommendation to register custom constructor for entity (I have already implemented it but I am still not clear how to let user supply their own id and to flag entity as modified only when user edits it...)

I realize this has been up for a while, but here are my thoughts (in case anyone comes looking).
If you use the entityManager to create your customerl and everything is specified correctly in the metadata, you can just create the customer and add phone numbers/addresses as needed. Breeze automatically makes an entity's properties observable (if specified correctly and if breeze knows that KO is being used)
If you can only do it the way that you say, then you are stuck. Ideally, you would have a user-entered ID which is NOT the key (though you could still force it to be unique) and a database-generated key, which Breeze will manage behind the scenes (assigning a negative key until it is saved to the data store, then updating the key and all related keys without any input from you).
if you use the 2nd approach for answer 2, then your buttons can easily be enabled and disabled using ko data-binding. When you create the entity, save its value to the viewmodel (custSource). Then you can add to the save button the data-bind="disable: custSource == Customer(), enable: custSource != Customer()". (You might need to play around with the syntax -- I haven't tested that part yet)
I don't think you need a custom constructor unless you are doing something different from what I understand.
PS. you should be aware that I believe Breeze wants Knockout defined as 'ko', while Durandal definitely expects it to be 'knockout', so you will probably need a 'map' property in your require.config

I think you could solve some of your problems by taking a slightly different approach to your entity creation. Here's your current approach:
Create a customer entity
User modifies that entity
Save the changes
Instead, try this:
User enters customer information
Create and save the customer entity
I realize that this doesn't really answer your questions, but I've found the second approach to be much easier to implement. Just have the user enter all the information you need to create a customer, and then supply those values to createEntity.
customeradd.js
// -- snip ---
var vm = {
customerId: ko.observable(),
address: ko.observable(""),
phoneNumbers: ko.observableArray([]),
submit: submit
};
return vm;
function submit() {
datacontext.createCustomer(
vm.customerId(),
vm.address(),
vm.phoneNumbers());
}
// -- snip ---
/services/datacontext.js
// -- snip ---
/**
* Creates a new customer.
* #param {int} id - The customer's id number.
* #param {string} address - The customer's address.
* #param {string[]} phoneNumbers - An array of the customer's phone numbers.
*/
function createCustomer(id, address, phoneNumbers) {
return manager.createEntity("Customer",
{
id: id,
address: address,
phoneNumber: phoneNumbers
});
}
// -- snip ---

Related

In Meteor, where do I model my business rules?

Beginner question : I've worked through the Try Meteor tutorial. I've got fields in my HTML doc, backed by helper functions that reference collections, and BOOM --> the fields are updated when the data changes in the DB.
With the "Hide completed" checkbox, I've also seen data-binding to a session variable. The state of the checkbox is stored in the Session object by an event handler and BOOM --> the list view is updated "automatically" by its helper when this value changes. It seems a little odd to be assigning to a session object in a single page application.
Through all this, my js assigns nothing in global scope, I've created no objects, and I've mostly seen just pipeline code, getting values from one spot to another. The little conditional logic is sprayed about wherever it is needed.
THE QUESTION... Now I want to construct a model of my business data in javascript, modelling my business rules, and then bind html fields to this model. For example, I want to model a user, giving it an isVeryBusy property, and a rule that sets isVeryBusy=true if noTasks > 5. I want the property and the rule to be isolated in a "pure" business object, away from helpers, events, and the meteor user object. I want these business objects available everywhere, so I could make a restriction, say, to not assign tasks to users who are very busy, enforced on the server. I might also want a display rule to only display the first 100 chars of other peoples tasks if a user isVeryBusy. Where is the right place to create this user object, and how do I bind to it from my HTML?
You can (and probably should) use any package which allows you to attach a Schema to your models.
Have a look at:
https://github.com/aldeed/meteor-collection2
https://github.com/aldeed/meteor-simple-schema
By using a schema you can define fields, which are calculated based on other fields, see the autoValue property: https://github.com/aldeed/meteor-collection2#autovalue
Then you can do something like this:
// Schema definition of User
{
...,
isVeryBusy: {
type: Boolean,
autoValue: function() {
return this.tasks.length > 5;
}
},
...
}
For all your basic questions, I can strongly recommend to read the DiscoverMeteor Book (https://www.discovermeteor.com/). You can read it in like 1-2 days and it will explain all those basic questions in a really comprehensible way.
Best Regards,
There is a very good package to implement the solution you are looking for. It is created by David Burles and it's called "meteor-collection-helper". Here it the atmosphere link:
You should check the link to see the examples presented there but according to the description you could implement some of the functionality you mentioned like this:
// Define the collections
Clients = new Mongo.Collection('clients');
Tasks = new Mongo.Collection('tasks');
// Define the Clients collection helpers
Clients.helpers({
isVeryBusy: function(){
return this.tasks.length > 5;
}
});
// Now we can call it either on the client or on the server
if (Meteor.isClient){
var client = Clients.findOne({_id: 123});
if ( client.isVeryBusy() ) runSomeCode();
}
// Of course you can use them inside a Meteor Method.
Meteor.methods({
addTaskToClient: function(id, task){
var client = Clients.findOne({_id: id});
if (!client.isVeryBusy()){
task._client = id;
Tasks.insert(task, function(err, _id){
Clients.update({_id: client._id}, { $addToSet: { tasks: _id } });
});
}
}
});
// You can also refer to other collections inside the helpers
Tasks.helpers({
client: function(){
return Clients.findOne({_id: this._client});
}
});
You can see that inside the helper the context is the document transformed with all the methods you provided. Since Collections are ussually available to both the client and the server, you can access this functionality everywhere.
I hope this helps.

How to use a Database Generated Identity Key in Web Api OData

I've managed to create number of readonly Web Api OData services following the tutorials here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api. I'm therefore employing the ODataConventionModel builder to create the model from a set of entities (incidentally coming from a Telerik ORM). This all seems to work fine and I can happily issue queries, view the metadata and so forth on the service.
I've now tried to turn my attention to the other CRUD operations - firstly Create and have stumbled into a problem! Namely, the Post method fires correctly (CreateEntity) but the entity parameter is null - by doing a check against the ModelState.IsValid, it shows that the problem is a null ID (key) value. This is unsurprising because the database uses a Database Generated Identity for the ID column and therefore the ID would be created when the entity is saved into the database context.
I've therefore tried all sorts of ways of marking the ID column as database generated, but haven't managed to find anything. Strangely, I can't seem to find even one post of someone asking for this - surely I can't be the only one?!
I noted that when looking at the EF modelbuilder (for example here: http://forums.asp.net/t/1848984.aspx/1) there appears to be a means of affecting the model builder with a .HasDatabaseGeneratedOption property, but no similar option exists in the System.Web.Http.OData equivalent.
So the questions therefore are:
Is there a means of altering the model builder (or something else) so that the controller will accept the object and deserialize the entity even with a null key value?
If so, how can I do this?
If not, any suggestions as to other options?
I realise that I could potentially just populate the object with an (in this case) integer value from the client request, but this seems a) semantically wrong and b) won't necessarilly always be possible as a result of the client toolkit that might be used.
All help gratefully received!
Many thanks,
J.
You need to create a viewmodel for insert which does not contain the ID parameter. Use Automapper to map the properties of the incoming insert-model to your data entities.
The problem that you're having is that ID is a required attribute in your data model because it is your PK, except during insert, where it shouldn't be specified.
In my case, my database-generated key is a Guid.
As a work-around, in my TypeScript client code, I submit (via http POST) the object with an empty Guid like this: Note: ErrorId is the key column.
let elmahEntry: ELMAH_Error = {
Application: 'PTUnconvCost',
Host: this.serviceConfig.url,
Message: message,
User: that.userService.currentUserEmail,
AllXml: `<info><![CDATA[\r\n\r\n${JSON.stringify(info || {})}\r\n\r\n]]></info>`,
Sequence: 1,
Source: source,
StatusCode: 0,
TimeUtc: new Date(Date.now()),
Type: '',
ErrorId: '00000000-0000-0000-0000-000000000000'
};
Then, in my WebApi OData controller, I check to see if the key is the empty guid, and if so, I replace it with a new Guid, like this:
// POST: odata/ELMAH_Error
public IHttpActionResult Post(ELMAH_Error eLMAH_Error)
{
if (eLMAH_Error.ErrorId == Guid.Empty)
{
eLMAH_Error.ErrorId = Guid.NewGuid();
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.ELMAH_Error.Add(eLMAH_Error);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (ELMAH_ErrorExists(eLMAH_Error.ErrorId))
{
return Conflict();
}
else
{
throw;
}
}
return Created(eLMAH_Error);
}

Entity Framework saves duplicates of one side in a one to many

I am having a problem with Entity Framework in my MVC 3 application. I have a users table, which is only ever populated with a new user row when a machine entity is created by a user that hasn't created any machines before, i.e. it only creates users it hasn't seen before. Each user belongs to a sector (division of the company) which also must be set before the user and the machine are saved. I have a default sector that new users are assigned to (so that this may be changed later on).
I have some code in my machine controller class for the creation of new machines that looks like this:
[HttpPost]
public ActionResult Create(Machine machine)
{
if (ModelState.IsValid)
{
// work out if the user exists in the database already
var users = userRepository.All.Where(u => u.Username == machine.User.Username);
if (users.Count() == 0)
{
// if the user entry doesn't exist we have to create it assigning a default sector
Sector defaultSector = null;
var defaultSectors = sectorRepository.All.Where(s => s.IsDefaultForNewUsers);
if (defaultSectors.Count() == 0)
{
// jebus! no default sector, so create one
defaultSector = new Sector() { Name = "Default", IsDefaultForNewUsers = true };
sectorRepository.InsertOrUpdate(defaultSector);
sectorRepository.Save();
}
else
{
defaultSector = defaultSectors.First();
}
machine.User.Sector = defaultSector;
}
else
{
machine.User = users.First();
}
machineRepository.InsertOrUpdate(machine);
machineRepository.Save();
return RedirectToAction("Index");
}
else
{
ViewBag.PossibleInstalledOS = installedosRepository.All;
ViewBag.PossibleLicenceTypes = licencetypeRepository.All;
ViewBag.PossibleUsers = userRepository.All;
return View();
}
}
[Edit] Here is the body of the InsertOrUpdate method from my Machine repository:
public void InsertOrUpdate(Machine machine)
{
if (machine.MachineId == default(int)) {
// New entity
context.Machines.Add(machine);
} else {
// Existing entity
context.Entry(machine).State = EntityState.Modified;
}
}
The problem I'm having with this code is that when I save the machine, it keeps creating a new user even though that user is already in the system. The line that finds the user works and retrieves the user as I would expect but entity framework doesn't seem to understand that I wish to use this user that I've found and not create a new one. So at the moment I have multiple identical users (except ID of course) in my users table. I want a one to many here so that multiple machines are owned by the same user.
Does anyone have any idea how I force entity framework to respect that there is already a user there that I want to tie the new machine to?
You didn't post the code for your InsertOrUpdate method but I suspect that that is where the problem is. I bet in that method at some point you do something equivalent to:
context.Machines.Add(machine);
When you call DbSet.Add (or change the state of an entity to Added) you are actually adding the whole graph to the context. This process will stop when it encounters an object that is being tracked by the context. So if you have a machine object that references a user object and neither of these objects are being tracked by the context, then both the machine object and the user object will be added to the context and end up in an Added state. EF will then insert them both as new rows in the database.
What you need to do, which was alluded to in the other answer, is make sure than EF knows that an existing user object does exist in the database by making sure it's state is Unchanged (or possibly Modified) and not Added when you save.
There are various ways that you could accomplish this and it's hard to know which is best for you without seeing more of how your app and repository work. One way is to make sure that the context used to query for the user is the same context as is used to save. This way EF will already be tracking the existing user object and will know not to add it when you call Add.
Another way is to let your repository know somehow whether or not the user object is new. Often people use the primary key to determine this--a zero key indicates a new object, non-zero indicates an existing object. You could also pass a flag into your repository.
You can then call Add to add the graph, but then set the state of the User object to Unchanged (or Modified if it might have been changed since it was queried) if it is an existing user. This will prevent EF from inserting a new user into the database.
Can you double check that your repositories are using the same data context? If not, you are essentially adding a new User entity to the machineRepository. Alternatively you could attach the user to the context for the machine repository, but you'll likely keep running into bugs like this.

How do you handle data validation in the model using ColdFusion Model-Glue?

MVC best practices state that the model should handle input/data validation. Let's say that we have a model that creates new user accounts, with the following fields and constraints:
Username - not null, not already in DB
Password - not null, alphanumeric only
E-mail - not null, not already in DB, valid e-mail format
We have an AccountModel with a CreateNewUser() function:
component
{
public void function CreateNewUser(string username, string password, string email)
{
// create account
}
}
Then we have a controller that processes a form post and tells the model to create the account:
component
{
public void function NewUser()
{
var username = event.getValue("username");
var password = event.getValue("password");
var email = event.getValue("email");
var accountModel = new AccountModel();
accountModel.CreateNewUser(username, password, email);
event.addResult("UserCreated");
}
Now I want to add validation. If the user fails to provide input for all three fields, the application should show the user three validation error messages. This is easy enough to do in the controller:
// assumes that ValidateInput() is a function on the controller that returns an array
var validationErrors = ValidateInput(username, password, email);
// if there were any validation errors, return a ValidationError result
if (len(validationErrors)
{
event.setValue("validationerrors", validationErrors);
event.addResult("ValidationError");
}
else
{
event.addResult("UserCreated");
}
And the view will pull the validationerrors variable and display the error messages.
However, this logic is supposed to reside in the model. How do I do this? I can think of two ways:
Method 1: Move ValidateInput() from the controller to the model. Now the controller has to call ValidateInput() first before CreateNewUser(). If the programmer forgets to do this, CreateNewUser() will throw a validation exception. The downside to this is that now every data operation that requires validation will need an if/else block.
Method 2: Forego having to call ValidateInput() and just call CreateNewUser() directly. If there were any validation errors, an exception will be thrown and it will contain the error message array. This method would work in C#, but it looks like ColdFusion does not support returning of data with the exception, only an exception message and type. Also, every data operation will require a try/catch block.
Method 3: ??
How would you handle validation in this case? Is method 1 what most people do when it comes to validation in the model? Or do people typically use method 2? Or is there a method 3 that I'm not thinking of?
I don't think you should couple the validation of the user's data entry to the creation of the user's account: they are too different things.
If you couple the two together, it means you're basically doing form validation every time you create an account, which doesn't seem right to me.
I see form validation as a UI concern, more than a concern of the objects that might be ultimately created from that data. Obviously your createNewUser() method has its own business rules (which will probably closely mirror that of the validation for a create-new-user form, but they are still separate concerns).
It is possibly a bit unorthodox, but I will put a validateUserData() (or something) method in my User CFC which the form-validation model then calls to help with the validation. This means the business rules for a user are in the same place, and can be called separately. Thereafter, createNewUser() works on a garbage-in/garbage-out principle.
Sidenote: createNewUser() is a bit of a tautological name, innit? What other sort of user would you be creating other than a new one? Also, if it's in your Account.cfc; is it a new user or a new account that's being created? If an account and a user are not synonymous and an account might exist without a user (or vice-versa), perhaps you ought to have a User.cfc too. Then again, this code you've given us could simply be for the purposes of illustration, and you've got all this covered.

Auto-populate fields in email form in Dynamics CRM 2011

Imagine, you want to add an email to a case. The email form opens and the "To" field is auto-populated with the case's customer account.
I want to change the behavior in order to auto-populate the content of "To" with a custom property of the related case.
My first approach was to register a JavaScript for the OnLoad event of the form and let the script change the field. That would work, but I am wondering if there is a smarter way to achieve this. There is already some logic, which fills the "To" field. Is it possible to configure this existing feature?
Any hints are appreciated.
I do not believe that this specific scenario can be done more effectively than how you've already worked it out. I would've suggested looking at the data mappings (left-nav item when you pop open the relationship in the entity's customizations, same concept as discussed in this Dynamics CRM 4.0 article http://www.dynamicscare.com/blog/index.php/modifying-mapping-behavior-between-parent-child-records-in-microsoft-dynamics-crm/), but it does not appear to be applicable to this relationship.
This might help you:
DataService.EntityReference toEntityRef = new DataService.EntityReference();
toEntityRef.LogicalName = "contact";
toEntityRef.Id = Guid.Parse(to_id);
Entity toParty = new Entity();
toParty["partyid"] = toEntityRef;
toParty.LogicalName = "activityparty";
Entity emailEntity = new Entity();
emailEntity.LogicalName = "email";
EntityCollection toCollection = new EntityCollection();
toCollection.Entities = new ObservableCollection<Entity>();
toCollection.Entities.Add(toParty);
emailEntity["to"] = toCollection;
IOrganizationService soapService = ServerUtility.GetSoapService();
IAsyncResult res = soapService.BeginCreate(emailEntity, OnCreateComplete, soapService);
Call Back method:
private void OnCreateComplete(IAsyncResult result)
{
Guid emailId = (IOrganizationService)result.AsyncState).EndCreate(result);
}
Another approach would be to replace the Add Email buttons in the ribbon in order to call a custom JavaScript function. This function could open the mail window with window.open and initialize the To: field by setting the extraqs parameter to configure an ActivityParty for the email about to create. This can be done by setting:
partyid to the id of an allowed entity's instance
partyname to the name of the entity instance
partytype to 2 (for the recipent field, see here for details)
But the extraqs parameter is limited: You can set only one receiver and no other field (from, cc, bcc, ...). Moreover, replacing the buttons would bypass built-in functionality, which may change in future versions,
So I prefer the handling of the OnLoad event of the form.

Resources