Error occurred when removing the user in EpiServer - ArgumentNullException: The type cannot be null - episerver

I have used,
CommunitySystem.CurrentContext.DefaultSecurity.RemoveUser(IUser user);
To remove user (Just as the answer posted in this issue).
However, this works fine. But for one user it gives an error. The exception throws from the RemoveUser(IUser user).
The exception details are here as below,
System.ArgumentNullException: The type cannot be null. Parameter name: type
at EPiServer.Data.Entity.Internal.EntityTypeResolver.GetTypeName(Type type, Boolean ignoreOverrideAttribute)
at EPiServer.Common.ImplementationModule.HandleCommentAddUpdateRemove(IComment comment, SupportedOperations operation)
at EPiServer.Common.Comments.CommentHandler.OnCommentUpdated(IComment comment)
at EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.<>c__DisplayClass32_0.b__0()
at EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.<>c__DisplayClass33_0`1.b__0()
at EPiServer.Common.Comments.CommentHandler.UpdateComment(IComment comment)
at EPiServer.Common.Data.FrameworkFactoryBase.CascadeUpdateStatus(IEntityStatus entity, ICollection`1 children)
at EPiServer.Common.Data.CommentFactory.CascadeAuthorStatus(IAuthor author)
at EPiServer.Common.AuthorEventHandler.Invoke(EntityEventArgs args)
at EPiServer.Common.AuthorHandler.OnAuthorUpdated(IAuthor author)
at EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.<>c__DisplayClass32_0.b__0()
at EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.<>c__DisplayClass33_0`1.b__0()
at EPiServer.Common.AuthorHandler.UpdateAuthor(IAuthor author)
at EPiServer.Common.Data.FrameworkFactoryBase.CascadeUpdateStatus(IEntityStatus entity, ICollection`1 children)
at EPiServer.Common.Security.Data.SecurityFactory.<>c__DisplayClass57_0.b__0()
at EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.<>c__DisplayClass32_0.b__0()
at EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.<>c__DisplayClass33_0`1.b__0()
at EPiServer.Data.Providers.SqlTransientErrorsRetryPolicy.Execute[TResult](Func`1 method)
at EPiServer.Common.Security.SecurityHandler.RemoveUser(IUser user)
I have tried to extract the Type and Type Name and it worked fine.
IUser user = getUser(id);
Type type = user.AccessRightsType;
string typeName = new EPiServer.Data.Entity.Internal.EntityTypeResolver().GetTypeName(type, true); // Not null. This value is as same as on other users
Update: I have decompiled the EPiServer.Common.ImplementationModule and this is what the HandleCommentAddUpdateRemove(..) has.
private static void HandleCommentAddUpdateRemove(IComment comment, SupportedOperations operation) {
EntityReference commentedEntity = comment.CommentedEntity;
IComment comment2 = null;
CommentEventData commentEventData;
if ((int) operation == 4) {
comment2 = (IComment) comment.get_Master();
if (comment2 == null || comment2.Status == comment.Status) {
return;
}
commentedEntity = comment2.CommentedEntity;
commentEventData = new CommentEventData(IEntityTypeResolverExtensions.GetTypeName(ServiceLocator.get_Current().GetInstance < IEntityTypeResolver > (), commentedEntity.get_Type()), commentedEntity.get_ID(), comment2.Status, -1);
m_commentEvent.Raise(m_commentId, (object) commentEventData);
}
commentEventData = new CommentEventData(IEntityTypeResolverExtensions.GetTypeName(ServiceLocator.get_Current().GetInstance < IEntityTypeResolver > (), commentedEntity.get_Type()), commentedEntity.get_ID(), comment.Status, ((int) operation != 8) ? 1 : (-1));
m_commentEvent.Raise(m_commentId, (object) commentEventData);
}
What causes this to happen ?
It seems like the below code returns 'Null'
commentedEntity.get_Type();
Any way to delete this user?
Additional info:
EPiServer.CommonFramework version=9.0.1
EPiServer.CMS.Core version=11.11.0
EPiServer.Community version=9.0.0

Related

Apex, SOQL, add results from database to the List, error

I would like to add results from database to the List. But there is a bug.
String str = '\'AOC\',\'BPD\',\'CRE\'';
List<String> lstString = str.split(',');
List<Shop_Product__c> productList = new List<Shop_Product__c>();
Integer i = 0;
for (String record: lstString) {
System.debug('record[' + i + ']: ' + record);
if ((record != null) && (productList != null)) {
productList.add([SELECT Id, Brand__c
FROM Shop_Product__c
WHERE Brand__c = :record
LIMIT 10]);
System.debug('productList[' + i + ']: ' + productList);
System.debug('Here in for ----------------------------------------');
}
++i;
}
Error is System.QueryException: List has no rows for assignment to SObject.
Here is it the explain, but I don't understand what I should do.
The List method add() takes a single sObject. That's why you get the QueryException, just like in the examples you link to.
You can use the addAll() method to create a List<sObject> context, which avoids the exception if there are no responsive records.

Salesforce APEX method not bulkified

I have written an APEX Class that sends an email when a client is released. There is a method that I thought I had bulkified but I was told it does not. This is because this method calls another function which actually does the actual email creation and that is not bulkified. Can someone guide me as to how the SOQL queries can be taken out of the method?
global class LM_ChangeAccountRT {
private static final Profile sysAdmin = [select id from profile where name='System Administrator'];
#AuraEnabled
public static String resendEmails(List<String> accountIdList) {
String response = null;
try {
//Only send emails if user is either an ARMS Administor or System Administrator
if (System.label.ARMS_Administrator_Profile_Id == userinfo.getProfileId() ||
sysAdmin.Id == userinfo.getProfileId()) {
List<Account> accList = [SELECT Id,Client_Released__c, RecordTypeId,Client_Number__c, Client_Released__c, Email_Sent__c FROM Account WHERE Id IN:accountIdList];
for(Account acc: accList){
if (acc.Client_Number__c != null && acc.Client_Released__c && acc.Email_Sent__c == true) {
sendpdfgenerationEmails(acc); //this is the method thats not bulkified.
acc.Email_Sent__c = false;
response = 'Email Sent';
}else {
response= 'Access Denied';
}
}
update accList;
}
}catch(Exception e) {
System.debug(e.getMessage());
response = 'Error sending emails';
}
return response;
}
public static void sendpdfgenerationEmails(Account acc){
system.debug('start of confirmation card and pdf generation');
//Logic to find which VF template is used to send an email.
list<EmailTemplate> templateId = new list<EmailTemplate>();
string temppartner;
String partner_opt_in_attachment;
boolean sendFCAmail;
List<Dealership_PDF_Generation__c> custsettingdata = Dealership_PDF_Generation__c.getall().values();
System.debug('custom setting size = ' + custsettingdata.size());
// Fetch State
if(acc.Dealership_State__c!=null && acc.Dealership_Partner__c!=null)
{
for(Dealership_PDF_Generation__c tempcustsetting :custsettingdata)
{
if(acc.Dealership_Partner__c == tempcustsetting.Dealership_Partner__c && acc.Dealership_State__c==tempcustsetting.State__c && tempcustsetting.State__c=='WA' && acc.Dealership_State__c=='WA'){
//For WA State
// temppartner= '%' + tempcustsetting.TEMPLATE_Unique_name__c + '%';
temppartner= tempcustsetting.TEMPLATE_Unique_name__c;
if(acc.Dealership_Spiff_Payment__c == '% premium'){
partner_opt_in_attachment=tempcustsetting.opt_in_form_premium__c;
}else{
partner_opt_in_attachment=tempcustsetting.opt_in_form_nonpremium__c;
}
}
else if(acc.Dealership_Partner__c == tempcustsetting.Dealership_Partner__c && acc.Dealership_State__c==tempcustsetting.State__c && tempcustsetting.State__c=='TX' && acc.Dealership_State__c=='TX'){
//For TX State
//temppartner= '%' + tempcustsetting.TEMPLATE_Unique_name__c + '%';
temppartner= tempcustsetting.TEMPLATE_Unique_name__c;
if(acc.Dealership_Spiff_Payment__c == '% premium'){
partner_opt_in_attachment=tempcustsetting.opt_in_form_premium__c;
}else{
partner_opt_in_attachment=tempcustsetting.opt_in_form_nonpremium__c;
}
}
else if(acc.Dealership_Partner__c == tempcustsetting.Dealership_Partner__c && acc.Dealership_State__c!=tempcustsetting.State__c && tempcustsetting.State__c!='TX' && acc.Dealership_State__c!='TX' && acc.Dealership_State__c!='WA' &&tempcustsetting.State__c!='WA' ){
//For Non TX State
//temppartner= '%' + tempcustsetting.TEMPLATE_Unique_name__c + '%';
temppartner= tempcustsetting.TEMPLATE_Unique_name__c;
if(acc.Dealership_Spiff_Payment__c == '% premium'){
partner_opt_in_attachment=tempcustsetting.opt_in_form_premium__c;
}else{
partner_opt_in_attachment=tempcustsetting.opt_in_form_nonpremium__c;
}
system.debug('grabbed template: ' + temppartner);
}
if(acc.Dealership_Partner__c != null && temppartner!=null ){
templateId.add([Select id,DeveloperName from EmailTemplate where DeveloperName = :temppartner]); //This will probably cause governor limit issues. First problem
}
if (partner_opt_in_attachment != null) {
StaticResource sr = [Select s.Name, s.Id, s.Body From StaticResource s where s.Name =: partner_opt_in_attachment]; //'static_resource' is the name of the static resource PDF. This is another SOQL query that will cause problems
Blob tempBlob = sr.Body;
Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
efa.setBody(tempBlob);
efa.setFileName('Opt-in.pdf');
List<Messaging.EmailFileAttachment> attachments = new List<Messaging.EmailFileAttachment>();
attachments.add(efa);
// add attachment to each email
for (Messaging.SingleEmailMessage email : emails) {
email.setFileAttachments(attachments);
}
}
system.debug('email sent: ' + emails.size());
Messaging.sendEmail(emails);
}
}
}
The reason why I am trying to bulkify this is because I have written a APEX scheduler that calls the resendemails method everyday at 7am to check which records need to have an email sent. I am afraid that if there are more than a 100 clients then it will cause problems and not send the emails. Any suggestions on how I can optimize the sendpdfemailgeenration() method?
Thank you
Yes, you are right - your's resendEmails() method is not bulkified.
Firstly, let me explain you why is that:
SOQL to get Accounts
Loop 1 on List of Account records
Call sendpdfgenerationEmails() method
Retrieve list of Dealership_PDF_Generation__c records
Loop 2 on List of Dealership_PDF_Generation__c records
SOQL to get StaticResources - Very bad! It's inside double loop!
Call Messaging.sendEmail() method - Very bad! It's inside double loop!
Update on List of Account records
You need to remember that:
1. You should never do SOQLs in loops! - Limit 100 SOQLs per transaction
2. You should never call Messaging.sendEmail() in loops! - Limit 10 calls per transaction
Now let me guide you how to refactor this method:
#AuraEnabled
public static String resendEmails(List<String> accountIdList) {
// 1. SOQL for List of Account records
// 2. Retrieve list of Dealership_PDF_Generation__c records
// 3. SOQL for List of StaticResources for all Names from Dealership_PDF_Generation__c records
// 4. Declaration of new List variable for Messaging.SingleEmailMessage objects
// 5. Loop 1 on List of Account records
// 6. Call new "prepareEmailsForAccount()" method, which prepares and returns list of Messaging.SingleEmailMessage objects
// 7. Add returned Messaging.SingleEmailMessage objects to list from point 4
// 8. End of loop 1
// 9. Call "Messaging.sendEmail()" method with list from point 4
// 10. Update on List of Account records
}
With this you will avoid SOQLs and calling Messaging.sendEmail() method in loops.

How to remove a record from the entity - MVVM, WPF

I have the following code in ViewModel, I would like to remove a record from an entity but it doesn't work. Can someone please shed some light...?
Usertable users = new Usertable();
users.User_ID = Entity.User_ID;
users.user_role = "Admin";
Entity.CompanyRoles.Remove(users);
Instead if I replace the Remove with Add, it will add one record to the entity.
Only Remove is a concern to me.
First you need to fetch the entity you are about to remove, then remove it, then save your changes to the datacontext:
var userToRemove = Entity.CompanyRoles.Single(cr => cr.user_role == "Admin");
Entity.CompanyRoles.DeleteObject(userToRemove);
Entity.SaveChanges();
Objects are compared by reference, not values, so unless Entity.CompanyRoles contains the exact reference in memory as the object you're trying to remove, it won't find the object in the collection and remove it.
There are two ways to fix it.
The best way is to get a reference to the object in the collection like Paul suggests
var userToRemove = Entity.CompanyRoles.Single(cr =>
cr.user_role == "Admin" && cr.User_ID = Entity.User_ID);
Entity.CompanyRoles.Remove(userToRemove);
Or another method that works is to overwrite the .Equals() of the object to consider them equal if the data is the same, regardless of the memory reference. I would not usually recommend this except in special cases since it affects any operation that compares one copy of this object to another using .Equals()
public override bool Equals(object obj)
{
if (obj == null || !(obj == MyClass))
return false;
var obj2 = obj as MyClass;
return obj2.user_role == this.user_role && obj2.User_ID == this.User_ID;
}

Build dynamic Expression for search

I'm in trouble, can't figure out seems a very simple thing that in plain SQL can be done within 1 minute, it's been several hours so far. Here is the situation:
I have single field where user may enter as many words as he/she likes
I need to build Expression to find match
Let's say there are 3 fields in database: firstname, middlename, lastname
I need to split the search entry and compare against those 3 fields
I'm dealing with Silverlight RIA, EF
Once again search entry contains UNKNOWN number of words
Here under what I'm trying to accomplish, return type is mandatory:
public Expression<Func<MyEntity, bool>> GetSearchExpression(string text)
{
Expression<Func<MyEntity, bool>> result;
var keywords = text.Trim().Split(" ");
foreach(var keyword in keywords)
{
// TODO:
// check whether 'OR' is required (i.e. after second loop)
// (firstname = 'keyword'
// AND
// middlename = 'keyword'
// AND
// lastname = 'keyword')
// OR
// (firstname like '%keyword%'
// AND
// middlename like '%keyword%'
// AND
// lastname like '%keyword%')
}
return result;
}
Thanks in advance!
The simplest thing would be to use Joe Albahari's PredicateBuilder to do something like this:
var predicate = PredicateBuilder.False<MyEntity>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (
p => p.FirstName.Contains (temp) &&
p.LastName.Contains (temp) &&
p.MiddleName.Contains (temp));
}
return predicate;
I left out the equality-checks because "Contains" (i.e. like '%...%') will cover that possibility anyway.
I do have to point out, though, that your conditions don't make any sense, from a business logic standpoint. Under what circumstances do you want to find someone whose first, last, and middle name all contain "John"? I suspect what you really want is something more like this:
var predicate = PredicateBuilder.True<MyEntity>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.And (
p => p.FirstName.Contains (temp) ||
p.LastName.Contains (temp) ||
p.MiddleName.Contains (temp));
}
return predicate;
One final note: Because PredicateBuilder requires you to call .AsExpandable() when you are creating your query, I don't know whether this will work for you. You might have to resort to building your own expressions, which can be somewhat tedious. This can get you started, though:
var pParam = Expression.Parameter(typeof(MyEntity), "p");
var predicate = Expression.Constant(true);
foreach (string keyword in keywords)
{
var keywordExpr = Expression.Constant(keyword);
// TODO: create an expression to invoke .FirstName getter
// TODO: create an expression to invoke string.Contains() method
//TODO: do the same for lastname and middlename
predicate = Expression.And(predicate,
Expression.Or(
Expression.Or(firstNameContainsKeyword,
middleNameContainsKeyword),
lastNameContainsKeyword));
}
return Expression.Lambda<Func<MyEntity, bool>>(predicate, pParam);

From .NET can I get the full SQL string generated by a SqlCommand object (with SQL Parameters)?

From the .NET environment can I get access to the full SQL string that is generated by a SqlCommand object?
Note: The full SQL string shows up in Intellisense hover, in VisualStudio, while in debug mode.
I'm willing to use reflection techniques if I must. I'm sure somebody here knows a way to get at it.
Update 1:
I'm calling a stored procedure having parameters with cmd.CommandType = CommandType.StoredProcedure and am trying to acquire the full SQL generated and run.
I wonder if the cmd.Prepare() method might not prove useful in this circumstance, if it might store the full string in a state field or something like that.
Update 2:
In light of answers below (and referenced) that indicate no complete SQL string is generated internally during preparation or execution, I did a bit of poking around using .NET Reflector. Even the internal connection classes seem to pass objects rather than boiling them down to strings, for example:
internal abstract void AddPreparedCommand(SqlCommand cmd);
Declaring Type: System.Data.SqlClient.SqlInternalConnection
Assembly: System.Data, Version=2.0.0.0
In general, thanks to everybody for the level of detail you got into to prove what can be done and show what's actually happening. Much appreciated. I like thorough explanations; they add surety and lend credence to the answers.
A simple loop replacing all the parameter names with their values will provide you with something similar to what the end result is, but there are several problems.
Since the SQL is never actually rebuilt using the parameter values, things like newlines and quotes don't need to be considered
Parameter names in comments are never actually processed for their value, but left as-is
With those in place, and taking into account parameter names that starts with the same characters, like #NAME and #NAME_FULL, we can replace all the parameter names with the value that would be in the place of that parameter:
string query = cmd.CommandText;
foreach (SqlParameter p in cmd.Parameters.OrderByDescending(p => p.ParameterName.Length))
{
query = query.Replace(p.ParameterName, p.Value.ToString());
}
there is one problem left with this, however, and that is if a parameter is a string, then the SQL that initially looks like this:
SELECT * FROM yourtable WHERE table_code = #CODE
will look like this:
SELECT * FROM yourtable WHERE table_code = SOME CODE WITH SPACES
This is clearly not legal SQL, so we need to account for some parameter-types as well:
DbType[] quotedParameterTypes = new DbType[] {
DbType.AnsiString, DbType.Date,
DbType.DateTime, DbType.Guid, DbType.String,
DbType.AnsiStringFixedLength, DbType.StringFixedLength
};
string query = cmd.CommandText;
var arrParams = new SqlParameter[cmd.Parameters.Count];
cmd.Parameters.CopyTo(arrParams, 0);
foreach (SqlParameter p in arrParams.OrderByDescending(p => p.ParameterName.Length))
{
string value = p.Value.ToString();
if (quotedParameterTypes.Contains(p.DbType))
value = "'" + value + "'";
query = query.Replace(p.ParameterName, value);
}
There have been a couple of similar questions here.
The most compelling answer was provided to this question: How to get the generated SQL-Statment from a SqlCommand-Object?
and the answer was:
You can't, because it does not
generate any SQL.
The parameterized query (the one in
CommandText) is sent to the SQL Server
as the equivalent of a prepared
statement. When you execute the
command, the parameters and the query
text are treated separately. At no
point in time a complete SQL string is
generated.
You can use SQL Profiler to take a
look behind the scenes.
The CommandText property (or calling ToString()) on your command will give you all of the SQL, with a small exception. It will definitely give you anything you see in the debugger. Note that this won't give you parameter values, but it will give you the actual command.
The only caveat is that when CommandType is Text, the ADO.NET framework will often (in fact, almost always) use sp_executesql to execute the command rather than executing the command directly against the connection. In that sense, it's not possible to obtain the exact SQL that gets executed.
I haven't tried this, but you may be able to use Capture Mode if you are willing to use SMO:
http://msdn.microsoft.com/en-us/library/ms162182(v=sql.120).aspx
I like Jesus Ramos answer, but I needed support for output parameters. (I also used a string builder to generate the content.)
Declare Parameter for output parameters
foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
{
// todo : I only needed a couple of types supported, you could add addition types
string dbtype = string.Empty;
switch (p.DbType)
{
case DbType.Guid:
dbtype = "uniqueidentifier";
break;
case DbType.Int16:
case DbType.Int64:
case DbType.Int32:
dbtype = "int";
break;
case DbType.String:
dbtype = "varchar(max)";
break;
}
query.Append(string.Format(" Declare {0}_ {1}\n", p.ParameterName, dbtype));
}
Build Main Parameter Area
foreach (SqlParameter p in arrParams)
{
bool isLast = p == last;
string value = p.Value.ToString();
if (quotedParameterTypes.Contains(p.DbType))
value = "'" + value + "'";
if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output)
{
query.Append(string.Format("{0} = {0}_ out{2}\n", p.ParameterName, value, isLast ? "" : ","));
}
else
{
query.Append(string.Format("{0} = {1}{2}\n", p.ParameterName, value, isLast ? "" : ","));
}
}
List Output Parameter results
foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
{
query.Append(string.Format(" select {0}_ {1}\n", p.ParameterName, p.ParameterName.Substring(1)));
}
Full Code:
public static string GetProcedureDebugInformation(SqlCommand cmd, [System.Runtime.CompilerServices.CallerMemberName] string caller = null, [System.Runtime.CompilerServices.CallerFilePath] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumber] int? lineNumber = null)
{
// Collection of parameters that should use quotes
DbType[] quotedParameterTypes = new DbType[] {
DbType.AnsiString, DbType.Date,
DbType.DateTime, DbType.Guid, DbType.String,
DbType.AnsiStringFixedLength, DbType.StringFixedLength
};
// String builder to contain generated string
StringBuilder query = new StringBuilder();
// Build some debugging information using free compiler information
query.Append(filePath != null ? filePath : ""
+ (lineNumber.HasValue ? lineNumber.Value.ToString() : "")
+ (lineNumber.HasValue || !string.IsNullOrWhiteSpace(filePath) ? "\n\n" : ""));
query.Append("\n\n");
var arrParams = new SqlParameter[cmd.Parameters.Count];
cmd.Parameters.CopyTo(arrParams, 0);
// Declare Parameter for output parameters
foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
{
// todo : I only needed a couple of types supported, you could add addition types
string dbtype = string.Empty;
switch (p.DbType)
{
case DbType.Guid:
dbtype = "uniqueidentifier";
break;
case DbType.Int16:
case DbType.Int64:
case DbType.Int32:
dbtype = "int";
break;
case DbType.String:
dbtype = "varchar(max)";
break;
}
query.Append(string.Format(" Declare {0}_ {1}\n", p.ParameterName, dbtype));
}
// Set Exec Text
query.Append(string.Format("\n exec {0}\n", cmd.CommandText));
var last = arrParams.LastOrDefault();
//Build Main Parameter Area
foreach (SqlParameter p in arrParams.OrderByDescending(p => p.ParameterName.Length))
{
bool isLast = p == last;
string value = p.Value.ToString();
if (quotedParameterTypes.Contains(p.DbType))
value = "'" + value + "'";
if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output)
{
query.Append(string.Format("{0} = {0}_ out{2}\n", p.ParameterName, value, isLast ? "" : ","));
}
else
{
query.Append(string.Format("{0} = {1}{2}\n", p.ParameterName, value, isLast ? "" : ","));
}
}
// List Output Parameter results
foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
{
query.Append(string.Format(" select {0}_ {1}\n", p.ParameterName, p.ParameterName.Substring(1)));
}
return query.ToString();
}

Resources