having a little trouble figuring out why my test class is returning System.DmlException: Upsert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Name]: [Name]
I have required field Name on the Account object in SFDC, but Since I'm mocking the Account with a Name attribute shouldn't my test class save function work?
Below is my Apex Class
public with sharing class QuoteAccountController {
// Define VariableType and VariableName
public ApexPages.StandardController standardContactController;
public Account Account{get;set;}
public Contact Contact{get;set;}
public Account selectedAccount{get;set;}
public Boolean displayProjectInformation{get;set;}
public Boolean projectNameError{get;set;}
public Boolean projectValidationsPassed{get;set;}
//Page Constructor/Initializer
public QuoteAccountController(ApexPages.StandardController StandardController) {
Account = new Account();
Contact = new Contact();
displayProjectInformation = true;
projectNameError = false;
projectValidationsPassed = true;
}
public pageReference save() {
projectValidations();
if (projectValidationsPassed) {
upsert Account;
Contact.accountId = Account.id;
upsert Contact;
Contact = new Contact();
Account = new Account();
ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM,'Record Created Successfully.Thank you!'));
return null;
} else {
return null;
}
}
public void projectValidations(){
if (Account.Subscription_Type__c == 'Project' && String.isBlank(Account.Project_Name__c)) {
projectValidationsPassed = false;
ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Project Name is required field'));
} else if (Account.Subscription_Type__c == 'Project' && String.isBlank(Account.Project_Type__c)) {
projectValidationsPassed = false;
ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Project Type is required field'));
} else if (Account.Subscription_Type__c == 'Project' && Account.Project_Start_Date__c == null) {
projectValidationsPassed = false;
ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Project Start Date is required field'));
} else if (Account.Subscription_Type__c == 'Project' && Account.Project_End_Date__c == null){
projectValidationsPassed = false;
ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Project End Date is required field'));
} else {
projectValidationsPassed = true;
}
}
Below is my Apex Test Class
#isTest
public class QuoteAccountControllerTest {
private static testmethod void testSave() {
Quote quote = new Quote();
Account testAcc = new Account();
Contact con = new Contact();
ApexPages.StandardController stdCont = new ApexPages.StandardController(testAcc);
QuoteAccountController quoteAccCont = new QuoteAccountController(stdCont);
PageReference page = new PageReference('/apex/zqu_QuoteAccount?quoteType=Subscription&stepNumber=1');
Test.setCurrentPage(page);
testAcc.Project_Name__c = 'Project Name';
testAcc.Name = 'Test Account';
testAcc.Project_Start_Date__c = Date.today();
testAcc.Project_End_Date__c = Date.today().addDays(2);
testAcc.Project_Type__c = 'Convention Center';
testAcc.Region__c = 'US';
testAcc.Subscription_Type__c = 'User';
Test.startTest();
quoteAccCont.save();
Test.stopTest();
}
}
Thanks!
Edit: Error message below.
System.DmlException: Upsert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Name]: [Name]
I don't have too much background in Salesforce but I believe that you need to insert the objects that you are creating with new before Test.startTest();
If this fails too, try to use System.debug() in your Apex Test class and use the Salesforce developer console and their logs to follow your error. Maybe this helps. Good luck!
You are trying to upsert an account. Upsert(Update/Insert) call would look for Id in the account you are upserting. If Id is found Upsert would work fine else it would Insert. So in your case it is trying to insert not update. That is why Name is mandatory.
Figured out solution. Need to assign the Account and Contact to the Controller. See modified code below.
#isTest
public class QuoteAccountControllerTest {
private static testmethod void testSave() {
Quote quote = new Quote();
ApexPages.StandardController stdCont = new ApexPages.StandardController(testAcc);
QuoteAccountController quoteAccCont = new QuoteAccountController(stdCont);
quoteAccCont.Account = new Account(ATTRIBUTES_HERE);
quoteAccCont.Contact = new Contact(ATTRIBUTES_HERE
PageReference page = new PageReference('/apex/zqu_QuoteAccount?quoteType=Subscription&stepNumber=1');
Test.setCurrentPage(page);
Test.startTest();
quoteAccCont.save();
Test.stopTest();
}
}
Related
Please I need your help to solve FluentValidation issue. I have an old desktop application which I wrote a few years ago. I used FluentValidation Ver 4 and Now I'm trying to upgrade this application to use .Net framework 4.8 and FluentValidation Ver 10, but unfortunately, I couldn't continue because of an exception that I still cannot fix.
I have this customer class:
class Customer : MyClassBase
{
string _CustomerName = string.Empty;
public string CustomerName
{
get { return _CustomerName; }
set
{
if (_CustomerName == value)
return;
_CustomerName = value;
}
}
class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(obj => obj.CustomerName).NotEmpty().WithMessage("{PropertyName} is Empty");
}
}
protected override IValidator GetValidator()
{
return new CustomerValidator();
}
}
This is my base class:
class MyClassBase
{
public MyClassBase()
{
_Validator = GetValidator();
Validate();
}
protected IValidator _Validator = null;
protected IEnumerable<ValidationFailure> _ValidationErrors = null;
protected virtual IValidator GetValidator()
{
return null;
}
public IEnumerable<ValidationFailure> ValidationErrors
{
get { return _ValidationErrors; }
set { }
}
public void Validate()
{
if (_Validator != null)
{
var context = new ValidationContext<Object>(_Validator);
var results = _Validator.Validate(context); **// <======= Exception is here in this line**
_ValidationErrors = results.Errors;
}
}
public virtual bool IsValid
{
get
{
if (_ValidationErrors != null && _ValidationErrors.Count() > 0)
return false;
else
return true;
}
}
}
When I run the application test I get the below exception:
System.InvalidOperationException HResult=0x80131509 Message=Cannot
validate instances of type 'CustomerValidator'. This validator can
only validate instances of type 'Customer'. Source=FluentValidation
StackTrace: at
FluentValidation.ValidationContext1.GetFromNonGenericContext(IValidationContext context) in C:\Projects\FluentValidation\src\FluentValidation\IValidationContext.cs:line 211 at FluentValidation.AbstractValidator1.FluentValidation.IValidator.Validate(IValidationContext
context)
Please, what is the issue here and How can I fix it?
Thank you
Your overall implementation isn't what I'd consider normal usage however the problem is that you're asking FV to validate the validator instance, rather than the customer instance:
var context = new ValidationContext<Object>(_Validator);
var results = _Validator.Validate(context);
It should start working if you change it to:
var context = new ValidationContext<object>(this);
var results = _Validator.Validate(context);
You're stuck with using the object argument for the validation context unless you introduce a generic argument to the base class, or create it using reflection.
Problem
How to call Action Result on another Action Result ?
I have two Action Result PostUserLogins and Action Result GetBranches
Can I call ActionResult getbranches inside ActionResult postlogin ?
[HttpPost(Contracts.ApiRoutes.Login.UserLogin)]
public IActionResult PostUserLogins([FromBody] Users user)
{
if (LoginStatus == 1)
{
// for Invalid Username Or password
dynamic request_status = new JObject();
request_status.Status = "failed";
request_status.Code = LoginStatus;
request_status.Message = errorMessage;
request_status.Branches = ????? How to call GetBranches Action;
// call action result to get GetBranches(Users user) as json;
JsonResults = "request_status" + JsonConvert.SerializeObject(request_status);
}
}
[HttpGet(Contracts.ApiRoutes.Login.GetBranches)]
public IActionResult GetBranches([FromRoute] string UserId)
{
List<Branches> branchesList = new List<Branches>();
for (int i = 0; i < dtBranches.Rows.Count; i++)
{
Branches branch = new Branches();
branch.BranchCode = Utilities.ObjectConverter.ConvertToInteger(dtBranches.Rows[i]["BranchCode"]);
branch.BranchName = Utilities.ObjectConverter.ConvertToString(dtBranches.Rows[i]["BranchAraName"]);
branchesList.Add(branch);
}
JsonResults = "request_status" + JsonConvert.SerializeObject(branchesList);
return Ok(JsonResults);
}
Regardless whether you could or not, you shouldn't.
The simplest way is to extract that logic to another method:
[HttpPost(Contracts.ApiRoutes.Login.UserLogin)]
public IActionResult PostUserLogins([FromBody] Users user)
{
if (LoginStatus == 1)
{
// for Invalid Username Or password
dynamic request_status = new JObject();
request_status.Status = "failed";
request_status.Code = LoginStatus;
request_status.Message = errorMessage;
request_status.Branches = GetBrancesImpl();
JsonResults = "request_status" + JsonConvert.SerializeObject(request_status);
}
}
[HttpGet(Contracts.ApiRoutes.Login.GetBranches)]
public IActionResult GetBranches([FromRoute] string UserId)
{
JsonResults = "request_status" + JsonConvert.SerializeObject(GetBrancesImpl());
return Ok(JsonResults);
}
private IEnumerable<Branches> GetBrancesImpl()
{
from branch in dtBranches.Rows
select new new Branches
{
BranchCode = Utilities.ObjectConverter.ConvertToInteger(dtBranches.Rows[i]["BranchCode"]),
BranchName = Utilities.ObjectConverter.ConvertToString(dtBranches.Rows[i]["BranchAraName"]),
};
}
Best would be to move this logic to a service class that holds the logic and can easily be tested.
If they are in the same controller,you could call it directly in PostUserLogins like:
public IActionResult PostUserLogins([FromBody] Users user)
{
//other logic
var result = GetBranches("myUserID") as OkObjectResult;
var json = result.Value.ToString().Substring(14);//remove the first "request_status" in the string to make it a valid json be deserialized later
request_status.Branches = JsonConvert.DeserializeObject<List<Branch>>(json);//get the Branch list
JsonResults = "request_status" + JsonConvert.SerializeObject(request_status);
}
Good morning,
I have a solution consisting of two projects. One is a class library, containing common classes that will be used in other projects. The other is a WebAPI 2.1 project.
I am generating the help files for the API by using the automatic help page generator, but I've noticed that when it references classes in the Common project, it doesn't use the summaries.
Is there any way of making it do this? I've searched online but I can't find any solution to this. I've also tried installing the help page generator in the Common project, but to no avail.
I had the same problem and this is just because the documentation provider takes only one xml document which is the one generated from current project (if you followed the instructions you may remember adding:
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/[YOUR XML DOCUMENT]"));
The rest of your classes and their metadata is added to a different xml document. What I did is I modified the xml documentation provider to accept multiple xml document path and search through each document for metadata related to the class been enquired about. You would need to add the xml document from the various dlls you are referencing but this definitely solved my issue. See below for the variation of the XmlDocumentationProvider:
public class XmlMultiDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
{
private List<XPathNavigator> _documentNavigator;
private const string TypeExpression = "/doc/members/member[#name='T:{0}']";
private const string MethodExpression = "/doc/members/member[#name='M:{0}']";
private const string PropertyExpression = "/doc/members/member[#name='P:{0}']";
private const string FieldExpression = "/doc/members/member[#name='F:{0}']";
private const string ParameterExpression = "param[#name='{0}']";
/// <summary>
/// Initializes a new instance of the <see cref="XmlDocumentationProvider"/> class.
/// </summary>
/// <param name="documentPath">The physical path to XML document.</param>
public XmlMultiDocumentationProvider(params string[] documentPath)
{
if (documentPath == null)
{
throw new ArgumentNullException("documentPath");
}
_documentNavigator = new List<XPathNavigator>();
foreach (string s in documentPath)
{
XPathDocument xpath = new XPathDocument(s);
_documentNavigator.Add(xpath.CreateNavigator());
}
}
public string GetDocumentation(HttpControllerDescriptor controllerDescriptor)
{
XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType);
return GetTagValue(typeNode, "summary");
}
public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor)
{
XPathNavigator methodNode = GetMethodNode(actionDescriptor);
return GetTagValue(methodNode, "summary");
}
public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
{
ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor;
if (reflectedParameterDescriptor != null)
{
XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor);
if (methodNode != null)
{
string parameterName = reflectedParameterDescriptor.ParameterInfo.Name;
XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName));
if (parameterNode != null)
{
return parameterNode.Value.Trim();
}
}
}
return null;
}
public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
{
XPathNavigator methodNode = GetMethodNode(actionDescriptor);
return GetTagValue(methodNode, "returns");
}
public string GetDocumentation(MemberInfo member)
{
string memberName = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name);
string expression = member.MemberType == MemberTypes.Field ? FieldExpression : PropertyExpression;
string selectExpression = String.Format(CultureInfo.InvariantCulture, expression, memberName);
XPathNavigator propertyNode = null;
foreach(XPathNavigator navigator in _documentNavigator )
{
XPathNavigator temp = navigator.SelectSingleNode(selectExpression);
if (temp != null)
{
propertyNode = temp;
break;
}
}
return GetTagValue(propertyNode, "summary");
}
public string GetDocumentation(Type type)
{
XPathNavigator typeNode = GetTypeNode(type);
return GetTagValue(typeNode, "summary");
}
private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor)
{
ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor;
if (reflectedActionDescriptor != null)
{
string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo));
foreach (XPathNavigator navigator in _documentNavigator)
{
XPathNavigator temp = navigator.SelectSingleNode(selectExpression);
if (temp != null)
{
return temp;
}
}
}
return null;
}
private static string GetMemberName(MethodInfo method)
{
string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(method.DeclaringType), method.Name);
ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length != 0)
{
string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray();
name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames));
}
return name;
}
private static string GetTagValue(XPathNavigator parentNode, string tagName)
{
if (parentNode != null)
{
XPathNavigator node = parentNode.SelectSingleNode(tagName);
if (node != null)
{
return node.Value.Trim();
}
}
return null;
}
private XPathNavigator GetTypeNode(Type type)
{
string controllerTypeName = GetTypeName(type);
string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName);
foreach (XPathNavigator navigator in _documentNavigator)
{
XPathNavigator temp = navigator.SelectSingleNode(selectExpression);
if (temp != null)
{
return temp;
}
}
return null;
}
private static string GetTypeName(Type type)
{
string name = type.FullName;
if (type.IsGenericType)
{
// Format the generic type name to something like: Generic{System.Int32,System.String}
Type genericType = type.GetGenericTypeDefinition();
Type[] genericArguments = type.GetGenericArguments();
string genericTypeName = genericType.FullName;
// Trim the generic parameter counts from the name
genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`'));
string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray();
name = String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, String.Join(",", argumentTypeNames));
}
if (type.IsNested)
{
// Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax.
name = name.Replace("+", ".");
}
return name;
}
}
You can get the idea or simply use the whole class at your discretion. Just remember to replace in your HelpPageConfig -> SetDocumentationProvider call the class name and add the path to the various xml documents.
I am creating an application with MVC4 and entity framework 5. How do can I implement this?
I have looked around and found that I need to override SaveChanges .
Does anyone have any sample code on this? I am using code first approach.
As an example, the way I am saving data is as follows,
public class AuditZoneRepository : IAuditZoneRepository
{
private AISDbContext context = new AISDbContext();
public int Save(AuditZone model, ModelStateDictionary modelState)
{
if (model.Id == 0)
{
context.AuditZones.Add(model);
}
else
{
var recordToUpdate = context.AuditZones.FirstOrDefault(x => x.Id == model.Id);
if (recordToUpdate != null)
{
recordToUpdate.Description = model.Description;
recordToUpdate.Valid = model.Valid;
recordToUpdate.ModifiedDate = DateTime.Now;
}
}
try
{
context.SaveChanges();
return 1;
}
catch (Exception ex)
{
modelState.AddModelError("", "Database error has occured. Please try again later");
return -1;
}
}
}
There is no need to override SaveChanges.
You can
Trigger Context.ChangeTracker.DetectChanges(); // may be necessary depending on your Proxy approach
Then analyze the context BEFORE save.
you can then... add the Change Log to the CURRENT Unit of work.
So the log gets saved in one COMMIT transaction.
Or process it as you see fit.
But saving your change log at same time. makes sure it is ONE Transaction.
Analyzing the context sample:
I have a simple tool, to Dump context content to debug output so when in debugger I can use immediate window to check content. eg
You can use this as a starter to prepare your CHANGE Log.
Try it in debugger immediate window. I have FULL dump on my Context class.
Sample Immediate window call. UoW.Context.FullDump();
public void FullDump()
{
Debug.WriteLine("=====Begin of Context Dump=======");
var dbsetList = this.ChangeTracker.Entries();
foreach (var dbEntityEntry in dbsetList)
{
Debug.WriteLine(dbEntityEntry.Entity.GetType().Name + " => " + dbEntityEntry.State);
switch (dbEntityEntry.State)
{
case EntityState.Detached:
case EntityState.Unchanged:
case EntityState.Added:
case EntityState.Modified:
WriteCurrentValues(dbEntityEntry);
break;
case EntityState.Deleted:
WriteOriginalValues(dbEntityEntry);
break;
default:
throw new ArgumentOutOfRangeException();
}
Debug.WriteLine("==========End of Entity======");
}
Debug.WriteLine("==========End of Context======");
}
private static void WriteCurrentValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.CurrentValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.CurrentValues[cv]);
}
}
private static void WriteOriginalValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.OriginalValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.OriginalValues[cv]);
}
}
}
EDIT: Get the changes
I use this routine to get chnages...
public class ObjectPair {
public string Key { get; set; }
public object Original { get; set; }
public object Current { get; set; }
}
public virtual IList<ObjectPair> GetChanges(object poco) {
var changes = new List<ObjectPair>();
var thePoco = (TPoco) poco;
foreach (var propName in Entry(thePoco).CurrentValues.PropertyNames) {
var curr = Entry(thePoco).CurrentValues[propName];
var orig = Entry(thePoco).OriginalValues[propName];
if (curr != null && orig != null) {
if (curr.Equals(orig)) {
continue;
}
}
if (curr == null && orig == null) {
continue;
}
var aChangePair = new ObjectPair {Key = propName, Current = curr, Original = orig};
changes.Add(aChangePair);
}
return changes;
}
edit 2 If you must use the Internal Object tracking.
var context = ???// YOUR DBCONTEXT class
// get objectcontext from dbcontext...
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
// for each tracked entry
foreach (var dbEntityEntry in context.ChangeTracker.Entries()) {
//get the state entry from the statemanager per changed object
var stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(dbEntityEntry.Entity);
var modProps = stateEntry.GetModifiedProperties();
Debug.WriteLine(modProps.ToString());
}
I decompiled EF6 . Get modified is indeed using private bit array to track fields that have
been changed.
// EF decompiled source..... _modifiedFields is a bitarray
public override IEnumerable<string> GetModifiedProperties()
{
this.ValidateState();
if (EntityState.Modified == this.State && this._modifiedFields != null)
{
for (int i = 0; i < this._modifiedFields.Length; ++i)
{
if (this._modifiedFields[i])
yield return this.GetCLayerName(i, this._cacheTypeMetadata);
}
}
}
I'm a new user in LINQ to SQL and I have some problems using it.
I've used LINQ to SQL Designer and I have created my classes, mapped on the DB tables.
In particular, I have one class, named voice:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.voce")]
public partial class voce : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _id_voce;
... other private fields;
private int _category;
private EntityRef<category> _category1;
public voce()
{
this._riepilogo = new EntitySet<riepilogo>(new Action<riepilogo>(this.attach_riepilogo), new Action<riepilogo>(this.detach_riepilogo));
this._hera = default(EntityRef<hera>);
this._category1 = default(EntityRef<category>);
OnCreated();
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_id_voce", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int id_voce
{
get
{
return this._id_voce;
}
set
{
if ((this._id_voce != value))
{
this.Onid_voceChanging(value);
this.SendPropertyChanging();
this._id_voce = value;
this.SendPropertyChanged("id_voce");
this.Onid_voceChanged();
}
}
}
......
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_category", DbType="Int NOT NULL")]
public int category
{
get
{
return this._category;
}
set
{
if ((this._category != value))
{
if (this._category1.HasLoadedOrAssignedValue)
{
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
this.OncategoryChanging(value);
this.SendPropertyChanging();
this._category = value;
this.SendPropertyChanged("category");
this.OncategoryChanged();
}
}
}
As you can see, voce class has a field named category that refers to a table named category.
When I add a new voce to my database, I create a new voce istance and, using the DataContext, i simply add it, using:
voce v = new voce(){...field, category1 = //create or retrieve category};
In particular, the category field is retrieved from the DB if already exists or, if not, it is inserted, before I insert the voice.
The problem is that when I add the voice in the database:
datacontext.InsertOnSubmit(v);
datacontext.SubmitChanges();
it inserts the category again, failing with the unique contraint.
How can I add a voice without adding every nested object?
Thank you and sorry for my bad English.
internal category GetCategoryFromDescription (string desc, Utility.VOICE_MODALITY mode)
{
bool type = mode == Utility.VOICE_MODALITY.ENTRATA ? true : false;
var query = from cat in dc.category
where cat.description == desc && cat.type == type
select cat;
if (query.Count() == 0)
{
category newC = new category() { description = desc };
dc.category.InsertOnSubmit(newC);
dc.SubmitChanges();
return newC;
}
else
return query.Single();
}