How could I make a c# method more generic? [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I'm exporting my data to CSV, sometimes I'm exporting Products or Prices and sometimes I'm exporting Customers.
So right now I have 3 methods for each type, and I would like to own 1 method for example CSVExport instead of ArticlesCSVExport, CustomersCSVExport and so on..
public async Task<byte[]> ArticlesCSVExport(Request searchObject)
{
var columnHeaders = new string[]
{
"Article Name",
"Article Price",
"Article Type",
"Status"
};
var result = serviceMethod.GetAll(); // returning all articles..
var articles = (from Article in result
select new object[]
{ // Could get this values from column headers?
$"\"{Article.ArticleName}\"",
$"\"{Article.Price}\"",
$"\"{Article.ArticleType}\"",
$"{(Article.Active==true ? "Active" : "Inactive")}",
}).ToList();
var articlesCsv = new StringBuilder();
articles.ForEach(line =>
{
articlesCsv.AppendLine(string.Join(",", line));
});
byte[] buffer = Encoding.ASCII.GetBytes($"{string.Join(",", columnHeaders)}\r\n{articlesCsv.ToString()}");
return buffer;
}
I thought about extending parameters to receive maybe list of data for CSV, and maybe column headers? but than how could I escape proper value, because property names would be different for each class; maybe read it somehow from column headers?

In your code example, the only thing that would change if the class was Price or Customer instead of Article is this:
var articles = (from Article in result
select new object[]
{ // Could get this values from column headers?
$"\"{Article.ArticleName}\"",
$"\"{Article.Price}\"",
$"\"{Article.ArticleType}\"",
$"{(Article.Active==true ? "Active" : "Inactive")}",
}).ToList();
What changes is how you get an object[] from whatever the type is - Article, Product, etc.
It's not clear from the context whether you would want to make the class generic or the method generic. I'll demonstrate with the method, since the method is what you're showing. (It's also not clear where result and columnHeaders is declared.)
The signature would change to look like this:
public async Task<byte[]> ArticlesCSVExport<T>(
Request searchObject, Func<T, object[]> extractValuesFunction)
The generic argument T - allows the caller to specify what the type is (again Article, Product, etc.)
This next argument:
Func<T, object[]> extractValuesFunction
...represents passing in a function that takes an instance of T and returns a List<object>. In other words, instead of that code being part of the method, you're passing it as a parameter to the method.
Now you can replace that section of code with:
var lineElements = result.Select(item => extractValuesFunction(item)).ToList();
or simplify to
var lineElements = result.Select(extractValuesFunction).ToList();
I named the variable lineElements (?) instead of articles because now they could be anything, not just articles.
What we're saying is, "For each one of these things, execute this function which will convert it to an array of objects."
All the parts of the method that stay the same regardless of what T is are still part of the method. Whatever changes is moved outside of the method and passed in as an argument. Your original method would now look like this:
public async Task<byte[]> ArticlesCSVExport<T>(
Request searchObject, Func<T, object[]> extractValuesFunction)
{
var lineElements = result.Select(extractValuesFunction).ToList();
var csv = new StringBuilder();
lineElements.ForEach(line =>
{
csv.AppendLine(string.Join(",", line));
});
byte[] buffer = Encoding.ASCII.GetBytes($"{string.Join(",", columnHeaders)}\r\n{csv.ToString()}");
return buffer;
}
(This also assumes that result is a collection of T instead of a collection of Article, but that's unclear because result isn't declared in this method.)
Now, instead of having inline code saying how to take an Article and return an object[], you'll just execute that function for each item.
Calling the function could look like this:
var output = await ArticlesCSVExport<Article>(
searchObject,
article =>
new object[]
{
$"\"{article.ArticleName}\"",
$"\"{article.Price}\"",
$"\"{article.ArticleType}\"",
$"{(article.Active == true ? "Active" : "Inactive")}"
});
In that example we're passing in an anonymous function, but we can pass any function that has the correct signature.
Suppose we have a class like this with a static method:
public static class CsvFormatFunctions
{
public static object[] GetArticleValues(Article article)
{
return new object[]
{
$"\"{article.ArticleName}\"",
$"\"{article.Price}\"",
$"\"{article.ArticleType}\"",
$"{(article.Active == true ? "Active" : "Inactive")}"
});
}
}
...then we could pass that method as a parameter:
var output = await ArticlesCSVExport<Article>(
searchObject, CsvFormatFunctions.GetArticleValues);
Depending on details that I can't see, the class might need to be generic instead of the method. If that's the case then you would just remove the generic argument <T> from the method and put it on the class declaration instead.

Best way is to use strategy design pattern
interface IParser
{
Task<byte[]> Parse(Request searchObject);
}
public class CustomersCSVExport : IParser
{
public Task<byte[]> Parse(Request searchObject)
{
throw new System.NotImplementedException();
}
}
public class ArticlesCSVExport : IParser
{
public async Task<byte[]> Parse(Request searchObject)
{
// Defining file headers
var columnHeaders = new string[]
{
"Article Name",
"Price",
"Type",
"Status"
};
// Get the data from Article Service to export/download
var result = await ArticleService.Get(searchObject);
// Escaping ","
var articles = (from Article in result
select new object[]
{ // Could get this values from column headers?
$"\"{Article.ArticleName}\"",
$"\"{Article.Price}\"",
$"\"{Article.ArticleType}\"",
$"{(Article.Active==true ? "Active" : "Inactive")}",
}).ToList();
// Build the file content
var articlesCsv = new StringBuilder();
articles.ForEach(line =>
{
articlesCsv.AppendLine(string.Join(",", line));
});
byte[] buffer = Encoding.ASCII.GetBytes($"{string.Join(",", columnHeaders)}\r\n{articlesCsv.ToString()}");
return buffer;
}
}
class CsvParser
{
private IParser _parser;
public void SetParser(IParser parser)
{
_parser = parser;
}
public Task<byte[]> Parse(Request searchObject)
{
return _parser.Parse(searchObject);
}
}
class Client{
void Main()
{
var csvParser= new CsvParser();
csvParser.SetParser(new ArticlesCSVExport());
var articleResult =csvParser.Parse(new Request());
csvParser.SetParser(new CustomersCSVExport());
var customerResult = csvParser.Parse(new Request());
}
}

Try to see if you can parse using expressions. The mapping can be supplied by the user as mentioned in the example here: https://www.codeproject.com/Articles/685310/Simple-and-Fast-CSV-Library-in-Csharp

You can make a generic method, that works independendly of the type.
To define what columns are written, and what value is written for an item, you could require an array of column definitions.
Each column definition would contain the header of that column, and the means how to get the value for an item.
Then just build the header line, and for each item the values line.
Example:
public static string ToCsv<T>(this IEnumerable<T> items, params (string title, Func<T, string> valueProvider)[] columnDefinitions)
{
if (items == null)
{
throw new ArgumentNullException(nameof(items));
}
if (columnDefinitions == null || columnDefinitions.Length == 0)
{
throw new ArgumentException(nameof(columnDefinitions));
}
var builder = new StringBuilder();
foreach (var cd in columnDefinitions)
{
var title = cd.title;
builder.Append(PrepareCsvValue(cd.title));
builder.Append(',');
}
builder.AppendLine();
foreach (var item in items)
{
foreach (var cd in columnDefinitions)
{
builder.Append(PrepareCsvValue(cd.valueProvider(item)));
builder.Append(',');
}
builder.AppendLine();
}
return builder.ToString();
}
private static string PrepareCsvValue(string value)
{
value = value.Replace("\"", "\"\"");
if (value.Contains(','))
{
value = $"\"{value}\"";
}
return value;
}
Here is an example on how to use it:
public static void Example()
{
var items = new TimeSpan[]
{
new TimeSpan(0),
new TimeSpan(0, 0, 0, 1),
new TimeSpan(0, 0, 1, 1),
new TimeSpan(0, 1, 1, 1),
new TimeSpan(1, 1, 1, 1)
};
var csvText = items.ToCsv(
("Days", ts => ts.Days.ToString()),
("Hours", ts => ts.Hours.ToString()),
("Minutes", ts => ts.Minutes.ToString()),
("Seconds", ts => ts.Seconds.ToString()));
// Do something with the csv text.
}
This is just to get the csv text.
You can create another method that also exports it to a file, using the first method.
Note:
I used named types for the column definitions.
That makes for a nice formating in the method call, but can be confusing to some.
You can find more information on named tuples here.
Note 2:
I added code that handles special characters inside values.
As per specification, values may use delimiters, if they are surounded by the " character.
Existing " characters need to be escaped with another ", thus (a"b -> a""b).

Related

Replace property values in a class from List<Dictionary> values

I have a method that takes a List<Dictionary<string,object>> as a parameter. The plan is to use that parameter, but only update the values held in a particular class. Here is the (partially written) method
public async Task<Errors> UpdatePageForProject(Guid projectId, List<Dictionary<string, object>> data)
{
if (!IsValidUserIdForProject(projectId))
return new Errors { ErrorMessage = "Project does not exist", Success = false };
if (data.Count == 0)
return new Errors { ErrorMessage = "No data passed to change", Success = false };
var page = await _context.FlowPages.FirstOrDefaultAsync(t => t.ProjectId == projectId);
foreach (var d in data)
{
}
return new Errors { Success = true };
}
My original plan is to take each dictionary, check if the key and the property in page match and then alter the value (so I can pass in 1 dictionary or 8 dictionaries in the list and then alter page to save back to my entity database).
I'd rather not use reflection due to the speed hit (though C#9 is really fast, I'd still rather not use it), but I'm not sure how else this can be done. I did consider using AutoMapper to do this, but for now would rather not (it's a PoC, so it is possibly overkill)
If you want to do this without Reflection (which I agree is a good idea, not just for performance reasons) then you could use a "map" or lookup table with actions for each property.
var map = new Dictionary<string,Action<Page,object>>()
{
{ "Title", (p,o) => p.Title = (string)o },
{ "Header", (p,o) => p.Field1 = (string)o },
{ "DOB", (p,o) => p.DateOfBirth = (DateTime)o }
};
You can then iterate over your list of dictionaries and use the map to execute actions that update the page.
foreach (var dictionary in data)
{
foreach (entry in dictionary)
{
var action = map[entry.Key];
action(page, entry.Value);
}
}

CSharpScript: Dynamic script parameter names

I'm trying to use Roslyn to execute C# code that is defined by the user at runtime, similar to this example:
public class Globals
{
public int X;
public int Y;
}
var globals = new Globals { X = 1, Y = 2 };
Console.WriteLine(await CSharpScript.EvaluateAsync<int>("X+Y", globals: globals));
Example copied from here
My problem is that the variable names used in the script are unknown at compile time. In other words, I don't know what member-names I should use for my globals class and how many members (script-parameters) there will be.
I tried to use ExpandoObject to solve the problem but couldn't get it to work. Is ExpandoObject supposed to work in this context? Are there other ways to solve the problem?
Update
For my use case, the best solution is probably to use System.Linq.Dynamic:
//Expression typed in by the user at runtime
string Exp = #"A + B > C";
//The number of variables and their names are given elsewhere,
//so the parameter-array doesn't need to be hardcoded like in this example.
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[]
{
Expression.Parameter(typeof(double), "A"),
Expression.Parameter(typeof(double), "B"),
Expression.Parameter(typeof(double), "C")
},
null, Exp);
var Lambda = e.Compile();
//Fake some updates...
foreach (var i in Enumerable.Range(0,10))
{
Console.WriteLine(Lambda.DynamicInvoke(i, 3, 10));
}
If you can retrieve at runtime all member names, their count and their values that were passed from input you can generate execution code at runtime and evaluate it. As a simple example of execution code you can generate variable declarations for all input values and then sum all of them:
// here you should put retrieved member names and their values. Just for example, currently here exist a couple of args
var variablesAndValues = new Dictionary<string, object> { ["arg_1"] = 5, ["arg_2"] = 6, ["arg_3"] = 7 };
// and you should create an execution code looks like this:
// var arg_1 = value_1;
// ..
// var arg_n = value_n;
// arg_1 + .. + arg_n
StringBuilder builder = new StringBuilder();
foreach (var item in variablesAndValues)
{
builder.Append("var ").Append(item.Key).Append(" = ").Append(item.Value).AppendLine(";");
}
var variablesCount = variablesAndValues.Count;
foreach (var item in variablesAndValues.Keys)
{
builder.Append(item);
if (--variablesCount > 0)
{
builder.Append(" + ");
}
}
var scriptState = CSharpScript.RunAsync(builder.ToString()).Result;
var result = scriptState.ReturnValue;
Be careful, this example assumes that the all value types has sum_operation and them are known by default script options, else you will receive compile error when try to execute the code.
Upd.
If your cases are performance critically you may create a script that will sum all input arguments and then run this script repeatedly when you need it.
public class Globals
{
public int[] Args;
}
...
// create script that sum all input arguments
var script = CSharpScript.Create(#"var result = 0;
foreach (var item in Args)
{
result += item;
}
return result; ", globalsType: typeof(Globals));
// run it at twice on the user values that were received before
// also you can reuse an array, but anyway
var res1 = script.RunAsync(new Globals { Args = new[] { 5, 6, 7 } }).Result.ReturnValue;
var res2 = script.RunAsync(new Globals { Args = new[] { 7, 8, 9, 10} }).Result.ReturnValue;
This approach ignore in the script code the input variable names from an user, and seems that it doesn't matter in your cases.

Generic iteration through list

I've been building an email generator function. It started as a regex function, but quickly moved over to reflection to make it as generic as possible. The idea is to have the email generator function pull in information from a messagedata class. It started as a simple task, as the function only had to change out a few static items, but as the function got more complex and the templates being sent out needed tables of information, then the function I had built was not enough.
I've extended the function to use a foreach loop to run through the template and replace text based on a list from the messageData class. I've been trying to get the list created in messageData to implement it into the emailGenerator function.
I've got:
string value = match.Groups["value"].Value;
// Code removed to shorten length
var items = (IEnumerable) value;
But it's not gathering the information from the messageData class. I'm thinking that maybe I need to get the value into a list?
Here is the EmailGenerator function:
public class EmailGenerator : IEmailGenerator
{
private string mergeTemplate(string template, object obj)
{
var operationMatches = operationParser.Matches(template).Cast<Match>().Reverse().ToList();
foreach (var match in operationMatches)
{
string operation = match.Groups["operation"].Value;
string value = match.Groups["value"].Value;
var propertyInfo = obj.GetType().GetProperty(value);
object dataValue = propertyInfo.GetValue(obj, null);
if (operation == "endforeach")
{
string foreachToken = "$foreach " + value + "$";
var startIndex = template.LastIndexOf(foreachToken, match.Index);
var templateBlock = template.Substring(startIndex + foreachToken.Length, match.Index - startIndex - foreachToken.Length);
var items = (IEnumerable) value;
string blockResult = "";
foreach (object item in items)
{
blockResult += this.mergeTemplate(templateBlock, item);
}
template = template.Remove(startIndex, match.Index - startIndex).Insert(startIndex, blockResult);
}
}
}
And here is the messageData class. It gets the information from a DTO.
** EDIT: Removed unnecessary code.
public class messageData : IMailObject
{
public List<messageItemData> Items
{
get
{
var items = new List<messageItemData>();
foreach (var docDTO in this.recipientDTO.InfoList)
{
items.Add(new messageItemData(docDTO));
}
}
}
}
public class messageItemData
{
// Properties
}
What I'm trying to accomplish is that the emailGenerator function is made generic enough to be reusable for other email templates later down the road, gathering the replacement information from the messageData class and the list it contains.
So, I finally found the answer. The code was 99% working. It was as simple as changing var items = (IEnumerable) value; for var items = (IEnumerable) dataValue;

Sorting a class string field property with numerical value [duplicate]

This question already has answers here:
Sorting of list contained strings having alphabetic/numeric
(2 answers)
Closed 8 years ago.
I have a class with one property "Name" containing names like "1_[AnnualRevenue]","2_[ResellerType]","3_xxx"....
my class is like
class xxx
{
private string fileName;
public string FileName
{
get { return fileName; }
set { fileName = value; }
}
}
And I am assigning the values to the object of the class. like xxx.FileName="1_[AnnualRevenue]";
Now I have a list class. And now sort the list according to this class property.
Now I want to sort the field according to the numeric order, I mean 1 first 2 second and so on.
And then write it to filestream.
Could any body help me with this.
Thanks in advance.
Since the property is a String but you want to sort it numerically, probably the best way would be to implement IComparable on your class and then put your custom sort code in the CompareTo method. Then you don't have to write a more complex Lambda statement each time you want to Sort a list, you can just call the Sort() method on the list.
You can also handle cases where the FileName property does not contain an underscore or is null, rather than getting exceptions in your OrderBy code (which is what would happen with most of the other answers).
I made a couple of other changes also - override the ToString method so you can easily display the value to the console window, and used Automatic property syntax for the FileName property so we can remove the backing field:
class xxx : IComparable<xxx>
{
public string FileName { get; set; }
public int CompareTo(xxx other)
{
// Short circuit if any object is null, if the
// Filenames equal each other, or they're empty
if (other == null) return 1;
if (FileName == null) return (other.FileName == null) ? 0 : -1;
if (other.FileName == null) return 1;
if (FileName.Equals(other.FileName)) return 0;
if (string.IsNullOrWhiteSpace(FileName))
return (string.IsNullOrWhiteSpace(other.FileName)) ? 0 : -1;
if (string.IsNullOrWhiteSpace(other.FileName)) return 1;
// Next, try to get the numeric portion of the string to compare
int thisIndex;
int otherIndex;
var thisSuccess = int.TryParse(FileName.Split('_')[0], out thisIndex);
var otherSuccess = int.TryParse(other.FileName.Split('_')[0], out otherIndex);
// If we couldn't get the numeric portion of the string, use int.MaxValue
if (!thisSuccess)
{
// If neither has a numeric portion, just use default string comparison
if (!otherSuccess) return FileName.CompareTo(other.FileName);
thisIndex = int.MaxValue;
}
if (!otherSuccess) otherIndex = int.MaxValue;
// Return the comparison of the numeric portion of the two filenames
return thisIndex.CompareTo(otherIndex);
}
public override string ToString()
{
return FileName;
}
}
Now, you can just call Sort on your list:
List<xxx> list = new List<xxx>
{
new xxx {FileName = "13_a"},
new xxx {FileName = "8_a"},
new xxx {FileName = null},
new xxx {FileName = "1_a"},
new xxx {FileName = "zinvalid"},
new xxx {FileName = "2_a"},
new xxx {FileName = ""},
new xxx {FileName = "invalid"}
};
list.Sort();
Console.WriteLine(string.Join("\n", list));
// Output (note the first two are the empty string and the null value):
//
//
// 1_a
// 2_a
// 8_a
// 13_a
// invalid
// zinvalid
You can use LINQ to do that for you
List<xxx> orderedList = unOrderedList.OrderBy(o => Convert.ToInt32(o.FileName.Split('_').First())).ToList();
Editted the answer on behalf of the comments - pointing out that indeed we need to convert to integers to order correctly.
You can do like following to sort the list:
List<xxx> list = new List<xxx>
{
new xxx { FileName = "3_a" },
new xxx { FileName = "1_a" },
new xxx { FileName = "2_a" },
new xxx { FileName = "8_a" }
};
var sorted = list.OrderBy(it => Convert.ToInt32(it.FileName.Split('_')[0]));//using System.Linq;
And you can write the list to disk file as below:
using (TextWriter tw = new StreamWriter("C:\\FileNames.txt"))
{
foreach (var item in sorted)
{
tw.WriteLine(item.FileName.ToString());
}
}

Get all values of a NameValueCollection to a string

I have the following code:
string Keys = string.Join(",",FormValues.AllKeys);
I was trying to play around with the get:
string Values = string.Join(",", FormValues.AllKeys.GetValue());
But of course that doesn't work.
I need something similar to get all the values, but I don't seem to find the appropriate code to do the same.
P.S: I do not want to use a foreach loop since that beats the purpose of the first line of code.
var col = new NameValueCollection() { { "a", "b" }, { "1", "2" } }; // collection initializer
var values = col.Cast<string>().Select(e => col[e]); // b, 2
var str = String.Join(",", values ); // "b,2"
Also you can create an extension method:
public static string Join(this NameValueCollection collection, Func<string,string> selector, string separator)
{
return String.Join(separator, collection.Cast<string>().Select(e => selector(e)));
}
Usage:
var s = c.Join(e => String.Format("\"{0}\"", c[e]), ",");
Also you can easily convert NameValueCollection to more handy Dictionary<string,string> so:
public static IDictionary<string,string> ToDictionary(this NameValueCollection col)
{
return col.AllKeys.ToDictionary(x => x, x => col[x]);
}
Gives:
var d = c.ToDictionary();
As I found using Reflector, NameValueCollection.AllKeys internally performs a loop to gather all te keys, so it seems that c.Cast<string>() is more preferable.
string values = string.Join(",", collection.AllKeys.Select(key => collection[key]));
Following creates a string from URL parameter list.
string.Join(", ",
Request.QueryString
.AllKeys
.Select(key => key + ": " + Request.QueryString[key])
.ToArray())
i.e
page.aspx?id=75&page=3&size=7&user=mamaci
would be
id: 75, page: 3, size: 7, user: mamaci
string values =
string.Join(",", FormValues.AllKeys.SelectMany(key => FormValues.GetValues(key)));
Edit: The other answers may or may not be what you want. They appear simpler, but the results might not be what you are looking for in all circumstances, but then again, they might be (your mileage may vary).
Note that a NameValueCollection is not a 1:1 mapping like a dictionary. You can add multiple values for the same key, which is why a function like .GetValues(key) returns an array, not a single string.
If you have a collection where you have added
collection.Add("Alpha", "1");
collection.Add("Alpha", "2");
collection.Add("Beta", "3");
Retrieving collection["Alpha"] yields "1,2". Retrieving collection.GetValues("Alpha") yields { "1", "2" }. Now, it just so happens that you are using a comma to join your values together into a single string, so this disparity is hidden. However, if you were joining on another value, such as an exclamation point, the results of the other answers would be
"1,2!3"
And the code here would be
"1!2!3"
Use the snippet that demonstrates the behavior you prefer.
In cases where you have parsed the query string with System.Web.HttpUtility.ParseQueryString(...) you can just use ToString() and you don't have to re-invent the wheel.
Even though result is NameValueCollection, the underlying type is HttpValueCollection which has the necessary ToString() override to build back a query string.
I'm using Azure DocumentDB as my logging mechanism, hence writing a dynamic object, but you get the gist...
public class LogErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
int responseCode = new int();
// Has the exception been handled. Also, are custom errors enabled
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
return;
// Check if custom exception, if so get response code
if (filterContext.Exception is CustomException)
responseCode = (int)((CustomException)filterContext.Exception).Code;
// Log exception
string id = Logging.Write(LogType.Error, new
{
ResponseCode = responseCode,
Exception = new
{
Message = filterContext.Exception.Message,
Data = filterContext.Exception.Data,
Source = filterContext.Exception.Source,
StackTrace = filterContext.Exception.StackTrace,
InnerException = filterContext.Exception.InnerException != null ? new
{
Message = filterContext.Exception.InnerException.Message,
Data = filterContext.Exception.InnerException.Data,
Source = filterContext.Exception.InnerException.Source,
StackTrace = filterContext.Exception.InnerException.StackTrace
} : null
},
Context = filterContext.Controller != null ? new
{
RouteData = filterContext.Controller.ControllerContext.RouteData,
QueryString = filterContext.Controller.ControllerContext.HttpContext.Request.Url.Query,
FormParams = filterContext.Controller.ControllerContext.HttpContext.Request.Form != null ? string.Join(";#", filterContext.Controller.ControllerContext.HttpContext.Request.Form.AllKeys.Select(key => key + ":" + filterContext.Controller.ControllerContext.HttpContext.Request.Form[key])) : string.Empty,
Model = (filterContext.Controller is Controller) ? ((Controller)filterContext.Controller).ModelState : null,
ViewBag = filterContext.Controller.ViewBag,
ViewData = filterContext.Controller.ViewData
} : null,
ActionResult = filterContext.Result != null ? filterContext.Result : null,
Referrer = filterContext.HttpContext.Request.UrlReferrer != null ? filterContext.HttpContext.Request.UrlReferrer : null
}).Result;
// Mark exception as handled and return
filterContext.ExceptionHandled = true;
// Test for Ajax call
if (IsAjax(filterContext))
{
// Construct appropriate Json response
filterContext.Result = new JsonResult()
{
Data = new
{
code = responseCode,
id = id,
message = filterContext.Exception.Message
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
var result = new ViewResult();
result.ViewName = "_CustomError";
result.ViewBag.CorrelationId = id;
filterContext.Result = result;
}
}
/// <summary>
/// Determine if the request is from an Ajax call
/// </summary>
/// <param name="filterContext">The request context</param>
/// <returns>True or false for an Ajax call</returns>
private bool IsAjax(ExceptionContext filterContext)
{
return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
}
I have a CustomException where I check for a application-set response code.
Additionally, I take the querystring, form data, and the model so that I can see the values passed before and after the model binder.
If its and Ajax call, I return a Json-formatted response. Otherwise, I return a custom error page.
List<string> values = new List<string>();
values.AddRange(all.AllKeys.SelectMany(all.GetValues).Where(getValues => getValues != null));
string Values = string.Join(",", values.ToArray());
You can try something like the above.

Categories