MVC 3 - Entity Framework & POCO - Many to Many REMOVAL on ActionLink() - entity-framework

I have Many to Many Relationships for:
Company -----> CompanyVenue <------
Venue
I can currently add a Venues to a Companies and Companies to Venues.
The Add Venue link takes the user to this view: (using this controller logic):
var venue = context.Companies.Select(x => x.Venues.Where(xy => xy.VenueId == id));
var company = venue.Select(x => x.);
ViewData["CompanyFor"] = company;
ViewData["VenueFor"] = venue;
Once the User Selects the Venue and Clicks the ADD VENUE Button, it passes the ViewModel back to Controller:
Company c = context.Companies.Single(x => x.CompanyId == model.CompanyId);
Venue v = context.Venues.Single(x => x.VenueId == model.VenueId);
c.Venues.Add(v);
context.SaveChanges();
So That is how I implimented the Many-Many Add. But, I Am looking for some help with Removal Controller Code / View (If Needed) / Possibly A View Model (but don't really see the need for what is essential a simple action)
In the Company Details View I am trying to get REMOVE Link underneath each Venue Item to Remove the [CompanyVenue] (many2many) Relationship.
If it is possible to pass 2 Values instead of one using this type of ActionLink():
#Html.ActionLink("Remove", "VenueRemove", new { Id = venue.VenueId }, new { Cid = model.CompanyId } )
it would be easy, but I have not figured out a way (load method) to pass both Values ( Id & Cid ) back to a VenueRemove() Controller Action, that would then return a RedirectToAction() back to the Company Details view.
If someone knows how to do that Please let me know. (Thank You Darin for the awnser to that)
I went ahead and got the Remove Working Using a View.
But is there anyway to get the Remove to work by just clicking the REMOVE Link/button and have it remove the venue with out going to the view?
Maybe an AJAX request or something.... ???

You can pass two parameters to the VenueRemove action like this:
#Html.ActionLink(
"Remove",
"VenueRemove",
new {
Id = venue.VenueId,
Cid = model.CompanyId
}
)

Related

How to get rid of lots of soql queries using maps

Im finding it really hard to understand maps, I have read lots of examples but they always seem not to relate to what I need to do - I've just about understood how to use oldmap.trigger in a very simple use case.
So anyways I have a trigger on a object Data_ProtectionIA , its parent is Data Agreement, and then its parent is Data request.
I need to update Data agreement and Data request when DPIA gets updated, I have the below so far which I know is a very long winded way to do it, but now I also have to add further updates for when DPIA gets updated to something else and for me to do this like I have done would mean doubling this code again when it is already unnecesarily long.
If someone could please simplify this code into maps then I think I would be able to understand maps properly, I just cant find examples of what I need to do. Many thanks in advance
(So at the moment I am getting the grandchild record and then creating a list of parent records to loop through and then getting a list of all their parent records and then looping through that. - even if you could just show me from child to parent (not even, child parent to grandparent that would be very helpful)
public static void setDRStatus(List<Data_Protection_IA__c> dpia, Map<ID,Data_Protection_IA__c> oldMap ){
List<ID> childDAID = new List<ID>();
for(Data_Protection_IA__c pDA: dpia){
if(pDA.DPIA_Status__c == 'Fully Approved' && oldMap.get(pDA.Id).DPIA_Status__c != 'Fully Approved'){
childDAID.add(pDA.DataProcessingAgreement__c);
}
}
List<Data_Agreement__c> childDA = new List<Data_Agreement__c>();
ChildDA = [Select id, Data_Sharing_Status__c from Data_Agreement__c where id in:childDAID];
List<Data_Agreement__c> listToUpdate = new List <Data_Agreement__c>();
List<ID> dAId = new List <ID>();
system.debug('ChildDA'+ childDA);
for(Data_Agreement__c cda : ChildDA){
cda.Data_Sharing_Status__c = 'DPIA Fully Approved';
listToUpdate.add(cda);
dAId.add(cda.id);
}
update listToUpdate;
List<Data_Request__c> dRList = [SELECT id, Data_Sharing_Status__c,Existing_Data_Agreement__c
FROM Data_Request__c where Existing_Data_Agreement__c in:dAId];
List<Data_Request__c> listToUpdatedr = new List <Data_Request__c>();
system.debug('DRList'+ dRList);
for(Data_Request__c dr :dRList){
dr.Data_Sharing_Status__c = 'DPIA Approved';
listToUpdatedr.add(dr);
}
update listToUpdatedr;
}
----------------------------------------
Here is an example I had previously tried
So I started following an example and got to here
Set<Id> daIds = new Set<Id> {};
for (Data_Protection_IA__c p : dpia)
{
daIds.add(p.DataProcessingAgreement__c );
}
Map<ID, Data_Agreement__c> m = new Map<ID, Data_Agreement__c>(
[select id, from Data_Agreement__c where id in : daIds]);
list<Data_Agreement__c> dAupdates = new List <Data_Agreement__c>();
list<Contact> contactupdates = new List <Contact>();
for (Data_Protection_IA__c p1 :dpia)
{
m.get(p1.Data_Agreement__c) = 'DPIA Fully Approved');
}
but at the last line I think this map is tying to get the field Data_agreement from dpia object, but there is no data agreement field on dpia object. as dpia is the child. there is only a field for dpia on the data agreement object

Get Line Items in an Invoice logic hook in SuiteCRM

Via a logic hook I'm trying to update fields of my products, after an invoice has been saved.
What I understand so far is, that I need to get the invoice related AOS_Products_Quotes and from there I could get the products, update the required fields and save the products. Does that sound about right?
The logic hook is being triggered but relationships won't load.
function decrement_stocks ( $bean, $event, $arguments) {
//$bean->product_value_c = $bean->$product_unit_price * $bean->product_qty;
$file = 'custom/modules/AOS_Invoices/decrement.txt';
// Get the Invoice ID:
$sInvoiceID = $bean->id;
$oInvoice = new AOS_Invoices();
$oInvoice->retrieve($sInvoiceID);
$oInvoice->load_relationship('aos_invoices_aos_product_quotes');
$aProductQuotes = $oInvoice->aos_invoices_aos_product_quotes->getBeans();
/*
$aLineItemslist = array();
foreach ($oInvoice->aos_invoices_aos_product_quotes->getBeans() as $lineitem) {
$aLineItemslist[$lineitem->id] = $lineitem;
}
*/
$sBean = var_export($bean, true);
$sInvoice = var_export($oInvoice, true);
$sProductQuotes = var_export($aProductQuotes, true);
$current = $sProductQuotes . "\n\n\n------\n\n\n" . $sInvoice . "\n\n\n------\n\n\n" . $sBean;
file_put_contents($file, $current);
}
The invoice is being retrieved just fine. But either load_relationship isn't doing anything ($sInvoice isn't changing with or without it) and $aProductQuotes is Null.
I'm working on SuiteCRM 7.8.3 and tried it on 7.9.1 as well without success. What am I doing wrong?
I'm not familiar with SuiteCRM specifics, however I'd always suggest to check:
Return value of retrieve(): bean or null?
If null, then no bean with the given ID was found.
In such case $oInvoice would stay empty (Your comment suggests that's not the case here though)
Return value of load_relationship(): true (success) or false (failure, check logs)
And I do wonder, why don't you use $bean?
Instead you seem to receive another copy/reference of $bean (and calling it $oInvoice)? Why?
Or did you mean to receive a different type bean that is somehow connected to $bean?
Then its surely doesn't have the same id as $bean, unless you specifically coded it that way.

EF6 Cannot Delete/Update - An object with the same key already exists in the ObjectStateManager

I am using EF 6 Code First and I need to delete an item and then also update a different item within a collection of Entities. If I try to delete one item and then modify a completely different item I get the error message "An object with the same key already exists in the ObjectStateManage" This is inaccurate because there are two objects with completely different PK IDs but when the update happens it throws the error. If I comment out the code to delete then the update works just fine with multiple items to update. Why would it complain about the "same key" when the keys are different?
foreach (var phone in phones)
{
if (!_isValidPhone(phone))
{
if(phone.PhoneId != 0)
{
var deletePhone = _db.Phones.FirstOrDefault(r => r.PhoneId == phone.PhoneId);
_db.Entry(deletePhone).State = EntityState.Deleted;
continue;
}
}
if (_isNewPhone(phone))
{
AddNewPhone(phone, _person);
}
else
{
UpdatePhoneData(phone, _person.Phones.FirstOrDefault(r => r.Order == phone.Order));
}
}
private void UpdatePhoneData(Phone phoneFrom, Phone phoneTo)
{
phoneTo.Note = phoneFrom.Note;
phoneTo.PhoneNumber = phoneFrom.PhoneNumber;
phoneTo.Order = phoneFrom.Order;
_db.Entry(phoneTo).State = EntityState.Modified;
}
If a phone is not valid and has id you try to add it to the context in two ways:
While deleting it:
_db.Entry(deletePhone).State = EntityState.Deleted;
Besides, when checking if it's new or not you either add or update it. Thats the problem.
So, what you need to do is wrap the add or update part inside an else, to add or update only if the phone has not been deleted.
This is not really a logic issue, the phone that was being updated is completely different than the phone that was being deleted. The issue is in the object statemanager. Because I was doing this in the delete
var deletePhone = _db.Phones.FirstOrDefault();
And then later I had a separate list of Phones where I try to set one of them to modified
_db.Entry(phoneTo).State = EntityState.Modified;
Well the object state manager now has each phone loaded twice basically. So if I just use the _person.Phones for both my delete and modify, then the Phones list is only loaded once and there are now duplicate keys.
_db.Entry(_person.Phones.FirstOrDefault(r => r.PhoneId == phone.PhoneId)).State = EntityState.Deleted;

Crm 2011 - How to set a default form depending on attribute value (without using Javascript)?

I have a little requirement that is making me crazy:
We have 8 different forms for the Contact Entity.
We also have a pick list with 8 options.
The idea is that based on the option selected we could open that Contact record showing by default a particular form WITHOUT USING JAVASCRIPT in order to avoid performance problems (each record has to be loaded twice). Example:
Forms:
Form 1
Form 2
Form 3
Pick List Values - Default Form:
Form 1
Form 2
Form 3
If Form 3(pick list value) is selected then, the next time I open that record, Form 3 should be displayed by default.
If Form 1(pick list value) is selected then, the next time I open that record, Form 1 should be displayed by default.
I've trayed registering a plugin at the systemform entity, in RetrieveFilteredForms message, updating the userentityuisettings table and I've been able to set a "DEFAULT" that is displayed every time the records is opened regardless the last opened form.
I've trayed registering a plugin at the contact entity, in Retrieve message, updating the userentityuisettings table but I found that Crm only consults the table Once if there is no attribute updated, the following times Crm take the the default form to open value from the cache.
This is an old question but since it's coming up in my searches for this problem I wanted to add my solution.
We use Dynamics CRM 2013. To my knowledge, later versions of 2011 also support this technique.
The form that is displayed when an entity is opened is determined by a few things - the default form, the security roles and fallback settings for a form and the last form used by the current user for that entity. We had a similar problem to the asker where we wanted a different account form displayed based on the value of a form. We were also tired of the constant reload/refresh that javascript techniques are subject to.
I found some blog posts (in particular this one: http://gonzaloruizcrm.blogspot.com/2014/11/avoiding-form-reload-when-switching-crm.html) which mentioned that it is possible to write a plugin to the Retrieve of the entity that allows you to read out the value (LastViewedFormXml) from UserEntityUISettings that stores which form was last used. If it's not the form you want, you can write in the desired value. This avoids the javascript form refreshing.
I had to modify some code from the samples I found to get it to work, but I'm happy with the results. You need to generate an entity class using CrmSvcUtil and include it in the project. You can get your form guids from the url of the form editor.
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Client;
namespace CRM.Plugin.AccountFormSwitcher
{
public class Plugin : IPlugin
{
public enum accountType
{
Customer = 100000000,
Vendor = 100000001,
Partner = 100000002,
Other = 100000003
}
public const string CustomerAccountFormId = "00000000-E53C-4DF4-BC99-93856EDD168C";
public const string VendorAccountFormId = "00000000-E49E-4197-AB5E-F353EF0E806E";
public const string PartnerAccountFormId = "00000000-B8C6-4E2B-B84E-729AA11ABE61";
public const string GenericAccountFormId = "00000000-8F42-454E-8E2A-F8196B0419AF";
public const string AccountTypeAttributeName = "cf_accounttype";
public void Execute(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
{
throw new ArgumentNullException("serviceProvider");
}
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
var pluginContext = (IPluginExecutionContext)context;
if (pluginContext.Stage == 20) //pre-operation stage
{
var columns = (ColumnSet)pluginContext.InputParameters["ColumnSet"];
if (!columns.Columns.Contains(AccountTypeAttributeName))
columns.AddColumn(AccountTypeAttributeName);
}
else if (pluginContext.Stage == 40) //post-operation stage
{
EntityReference currentEntity = (EntityReference)context.InputParameters["Target"];
if (currentEntity == null)
return;
var query = new QueryExpression(Account.EntityLogicalName);
query.Criteria.AddCondition("accountid", ConditionOperator.Equal, currentEntity.Id);
query.ColumnSet = new ColumnSet(AccountTypeAttributeName);
var accounts = service.RetrieveMultiple(query).Entities;
Account currentAccount = (Account)accounts[0];
SetForm(currentAccount, service, context.UserId);
}
}
private void SetForm(Account account, IOrganizationService service, Guid userId)
{
var query = new QueryExpression(UserEntityUISettings.EntityLogicalName);
query.Criteria.AddCondition("ownerid", ConditionOperator.Equal, userId);
query.Criteria.AddCondition("objecttypecode", ConditionOperator.Equal, Account.EntityTypeCode);
query.ColumnSet = new ColumnSet("lastviewedformxml");
var settings = service.RetrieveMultiple(query).Entities;
// Some users such as SYSTEM have no UserEntityUISettings, so skip.
if (settings == null || settings.Count != 1 || account.cf_AccountType == null) return;
var setting = settings[0].ToEntity<UserEntityUISettings>();
string formToUse;
switch ((accountType)account.cf_AccountType.Value)
{
case accountType.Customer:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", CustomerAccountFormId);
break;
case accountType.Vendor:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", VendorAccountFormId);
break;
case accountType.Partner:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", PartnerAccountFormId);
break;
case accountType.Other:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", GenericAccountFormId);
break;
default:
formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", GenericAccountFormId);
return;
}
// Only update if the last viewed form is not the one required for the given opportunity type
if (!formToUse.Equals(setting.LastViewedFormXml, StringComparison.InvariantCultureIgnoreCase))
{
var s = new UserEntityUISettings { Id = setting.Id, LastViewedFormXml = formToUse };
service.Update(s);
}
}
}
}
And to address the asker's issue with it only consulting the UserEntityUISettings once, I'm not sure why that's happening. But why not use javascript to change the form when the triggering attribute is changed? That what I do and I haven't ran into any problems with the plugin not displaying the desired for.
EDIT: the OP specified after that he needs a solution without javascript, I leave this reply for future references.
You can use javascript, inside the OnLoad event you check the picklist value and navigate to the desired form. Check this code as example
var value = Xrm.Page.getAttribute("new_optionset").getValue();
switch(value) {
case 100000000:
Xrm.Page.ui.formSelector.items.get(0).navigate();
break;
case 100000001:
Xrm.Page.ui.formSelector.items.get(1).navigate();
break;
case 100000002:
Xrm.Page.ui.formSelector.items.get(2).navigate();
break;
/// ... other cases here
default:
// default form to open when there is no value
Xrm.Page.ui.formSelector.items.get(0).navigate();
}
This is an oldy but a goody... I use CRM Rules to generate Hide Tab and Show Tab actions that essentially show each user role a different form.
Steps:
Create one, massive form with all of the fields you want to display (if you already have many forms, this would include all fields across all forms).
Organize the form into TABS, with each tab showing 'one form' worth of data. (You can also have many TABS for each user group). Typically, I create one 'General' tab that has the key option set that will set the rest of the form up, and any fields that are common across roles / user groups / forms, like status, name, etc...
3) Hide all of the tabs except the General tab by unchecking the visible box on those tab form property forms in the Admin UI.
4) Using CRM Rules (crm-rules.com), you can then bring in the metadata, with the form, and all of the tabs and sections in there. Then you just have to write one rule for each 'form' you are trying to show... Each rule is of this format:
IF User_Role contains 'Sales' THEN Show Tab: Sales
IF User_Role contains 'Marketing' THEN Show Tab: Marketing
You can also do this, of course, with an option set, or any field on the form as the condition... One of the benefits of this approach is that if users cross role boundaries (or some users were part of security roles that could access multiple forms), this technique shows them both forms at once...
HTH somebody, CRM Rules (www.crm-rules.com) generates the JavaScript to make this happen...

How to populate zend form field using session?

I am using sessions to populate a multi select box with options in my Zend application.
The user selects one or more options and fills in other fields on the form and then submits. If the user didn't select all of the options in the multi select then the form is displayed again but the multi select only has the options that the user did not select the last time. This process goes on until there are no more options from the multi select left to process.
Here is the code I use to get rid of the options that have already been processed so that they are not used to populate the multi select box:
if($form_successful){
// TODO remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
print_r($keyword_names);
if(is_array($keyword_names) && !empty($keyword_names)){
// save updated $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
// set flag to false so that we display form again
$form_successful = false;
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
print_r($keyword_names); displays the correct options, however when the form is loaded when the user submits, the multi select displays the options that were there from the begining ie the options the user has just selected and submitted are not being taken out of the multi select, it is only when the user submits the form again then the multi select box updates.
Appreciate the help.
Solved the issue by making use of URL parameters. Here is the code (might differ a lot from what I posted first because some big changes were made):
// after successful form submission
if($form_successful){
// remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
// save remaining $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
if(is_array($keyword_names) && !empty($keyword_names)){
// redirect to the same step again - to ensure that the form will reflect (in select lists) newly created AdGroup and/or Campaign
// GET parameteres ($params_array) provide a way to remember user's choice
$params_array = array();
if(!empty($post['match_type_id'])){
$params_array['match_type_id'] = $post['match_type_id'];
}
if(!empty($post['with_permutations'])){
$params_array['with_permutations'] = $post['with_permutations'];
}
if(!empty($ad_group_id)){
$params_array['ad_group_id'] = $ad_group_id;
}
$this_step_url = UrlUtils::assemble('', $this->getRequest()->getActionName(), $this->getRequest()->getControllerName(), $this->getRequest()->getModuleName(), $params_array);
$this->_redirect($this_step_url);
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
So you don't have any code about Zend_Form object here. How do you populate the form element? If you post your class code which extends Zend_Form (or any other code dials with your form) then I may help. But in any case you can populate your multiselectbox with setMultiOptions() method or addMultiOption() for each item in multiselectbox.