I am calling a custom action from my view with a $.post and sending a couple user generated parameters with it:
$("#submitReport").click(function () {
var carData = $("#car-select").val();
var bikeData = $("#bike-select").val();
if (carData && bikeData !== null) {
$.post('/Reporting/ExportToExcel', $.param({ carData: carData, bikeData: bikeData }, true), function(data) {
console.log(data);
});
}
});
Csv Action Result:
[HttpPost]
public CsvActionResult ExportToExcel(string[] carData, string[] bikeData)
{
var dt = new DataTable();
// Add all the stuff into the datatable
return new CsvActionResult(dt) { FileDownloadName = "MyReport.csv" };
}
And the most important part, the CsvActionResult class:
public sealed class CsvActionResult : FileResult
{
private readonly DataTable _dataTable;
public CsvActionResult(DataTable dataTable)
: base("text/csv")
{
_dataTable = dataTable;
}
protected override void WriteFile(HttpResponseBase response)
{
var outputStream = response.OutputStream;
using (var memoryStream = new MemoryStream())
{
WriteDataTable(memoryStream);
outputStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
}
private void WriteDataTable(Stream stream)
{
var streamWriter = new StreamWriter(stream, Encoding.Default);
WriteHeaderLine(streamWriter);
streamWriter.WriteLine();
WriteDataLines(streamWriter);
streamWriter.Flush();
}
private void WriteHeaderLine(StreamWriter streamWriter)
{
foreach (DataColumn dataColumn in _dataTable.Columns)
{
WriteValue(streamWriter, dataColumn.ColumnName);
}
}
private void WriteDataLines(StreamWriter streamWriter)
{
foreach (DataRow dataRow in _dataTable.Rows)
{
foreach (DataColumn dataColumn in _dataTable.Columns)
{
WriteValue(streamWriter, dataRow[dataColumn.ColumnName].ToString());
}
streamWriter.WriteLine();
}
}
private static void WriteValue(StreamWriter writer, String value)
{
writer.Write("\"");
writer.Write(value.Replace("\"", "\"\""));
writer.Write("\",");
}
}
When I look in the console, I can see that the data is being returned, but it doesn't prompt a file download in the browser. So I actually need to do something with the data when it is returned. I thought it would prompt a file download automatically.
Any help would be appreciated.
Related
I'm trying to use PHPickerController and access PHAsset to get file name and file size but the PHAsset are null
var config = new PHPickerConfiguration(PHPhotoLibrary.SharedPhotoLibrary) {
Filter = PHPickerFilter.ImagesFilter,
SelectionLimit = 1
};
var picker= new PHPickerViewController(config) {
ModalPresentationStyle = UIModalPresentationStyle.Popover,
Delegate = new ImagePickerDelegate((fileSize, fileName, url) => {
})
};
ViewController.PresentViewController(picker, true, null);
public class ImagePickerDelegate : PHPickerViewControllerDelegate
{
public ImagePickerDelegate(Action<int, string, string> action)
{
Action = action;
}
public Action<int, string, string> Action { get; }
public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results)
{
picker.DismissViewController(true, null);
foreach (var result in results)
{
var asset = PHAsset.FetchAssets(result.AssetIdentifier, null)?.firstObject as PHAsset;
// The asset are null
var fileSize = asset.ValueForKey((NSString)"fileSize");
}
}
}
As you can see in the image the request dialog show and code are not pause on following line
var asset = PHAsset.FetchAssets(result.AssetIdentifier, null)?.firstObject as PHAsset;
and return null
You could use FetchAssetsUsingLocalIdentifiers method to get PHAsset object, then it will return value.
Sample code as follows:
public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results)
{
picker.DismissViewController(true, null);
foreach (var result in results)
{
var refID = result.AssetIdentifier;
string[] refIDs = new string[] { refID };
var asset = PHAsset.FetchAssetsUsingLocalIdentifiers(refIDs, null)?.firstObject as PHAsset;
// var fileSize = asset.ValueForKey((NSString)"fileSize");
}
}
Also could have a look at this native code link.
How to read a CSV file from SFTP and use CSVHelper to parse the content without saving CSV locally?
Is this possible, or do we have to save it locally, parse and delete the file?
I am using SSH.Net and CSVHelper.
It needs to rely on Stream-processing of file:
public async Task ProcessRemoteFilesAsync()
{
var credentials = new Credentials("host", "username", "password");
var filePaths = new List<string>();
// initializing filePaths ..
var tasks = filePaths
.Select(f => ParseRemoteFileAsync(credentials, f))
.ToArray();
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
// traverse through results..
}
public async Task<FileContent> ParseRemoteFileAsync(Credentials credentials, string filePath)
{
using (var sftp = new SftpClient(credentials.host, credentials.username, credentials.password))
{
sftp.Connect();
try
{
using (var remoteFileStream = sftp.OpenRead(filePath))
{
using (var reader = new StreamReader(remoteFileStream))
{
using (var csv = new CsvReader(reader))
{
/*
// Example of CSV parsing:
var records = new List<Foo>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = new Foo
{
Id = csv.GetField<int>("Id"),
Name = csv.GetField("Name")
};
records.Add(record);
}
*/
}
}
}
}
finally {
sftp.Disconnect();
}
}
}
Modified version that uses pool of SftpClient
See C# Object Pooling Pattern implementation.
Implementation of pool borrowed from How to: Create an Object Pool by Using a ConcurrentBag:
/// <summary>
/// Implementation borrowed from [How to: Create an Object Pool by Using a
/// ConcurrentBag](https://learn.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-create-an-object-pool).
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObjectPool<T> : IDisposable
where T : IDisposable
{
private readonly Func<T> _objectGenerator;
private readonly ConcurrentBag<T> _objects;
public ObjectPool(Func<T> objectGenerator)
{
_objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator));
_objects = new ConcurrentBag<T>();
}
public void Dispose()
{
while (_objects.TryTake(out var item))
{
item.Dispose();
}
}
public T GetObject()
{
return _objects.TryTake(out var item) ? item : _objectGenerator();
}
public void PutObject(T item)
{
_objects.Add(item);
}
}
The simplest Pool-based implementation (it doesn't care about exception processing, retry-policies):
internal class SftpclientTest
{
private readonly ObjectPool<SftpClient> _objectPool;
public SftpclientTest(Credentials credentials)
{
_objectPool = new ObjectPool<SftpClient>(() =>
{
var client = new SftpClient(credentials.host, credentials.username, credentials.password);
client.Connect();
return client;
});
}
public void GetDirectoryList()
{
var client = _objectPool.GetObject();
try
{
// client.ListDirectory() ..
}
finally
{
if (client.IsConnected)
{
_objectPool.PutObject(client);
}
}
}
public async Task ProcessRemoteFilesAsync()
{
var filePaths = new List<string>();
// initializing filePaths ..
var tasks = filePaths
.Select(f => ParseRemoteFileAsync(f))
.ToArray();
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
// traverse through results..
}
public Task<FileContent> ParseRemoteFileAsync(string filePath)
{
var client = _objectPool.GetObject();
try
{
using (var remoteFileStream = client.OpenRead(filePath))
{
using (var reader = new StreamReader(remoteFileStream))
{
using (var csv = new CsvReader(reader))
{
// ..
}
}
return Task.FromResult(new FileContent());
}
}
finally
{
if (client.IsConnected)
{
_objectPool.PutObject(client);
}
}
}
}
I used to work with browser-based applications. for example Angular simple repository.
function getSomeData(params) {
...
return $http({
url: conf.urlDev + 'some/rest-url',
method: "GET",
params: params,
cache: true
}).then(getDataComplete);
function getDataComplete(response) {
return response.data;
}
}
How it will look the same in c# (XAMARIN for example)?
i try :
public class BaseClient
{
protected Http _client = null;
protected string _urlObj;
protected string _basePath;
public BaseClient ()
{
_client = new Http(new HttpClientHandler());
}
public string Path
{
set
{
_urlObj = value;
}
}
public async Task<Result<IList<T>>>getList<T>(Dictionary<string,object> parametrs = null)
{
if (parametrs != null)
{
foreach(KeyValuePair<string, object> keyValue in parametrs)
{
_urlObj = _urlObj.SetQueryParam(keyValue.Key, keyValue.Value);
}
}
var response = await _client.GetAsync(_urlObj.ToString());
if (response.IsSuccessStatusCode)
{
return new Result<IList<T>>()
{
Success = true,
Value = JsonConvert.DeserializeObject<IList<T>>(await response.Content.ReadAsStringAsync())
};
}
else
{
var error = new Result<IList<T>>()
{
Error = response.StatusCode.ToString(),
Message = response.ReasonPhrase,
Success = false
};
return error;
}
}
in my service:
public async Task<IList<News>> GetAllNewsByParams(DateTime from,
string orderBy = "-published",
DateTime to = new DateTime(),
int page = 1, int category = 0)
{
_client.Path = _config.NewsPath;
var dict = new Dictionary<string, object> {
{"from", from.ToString("s")},
{"order_by", orderBy.ToString()},
{"to", to.ToString("s")},
{"page", page.ToString()}
};
if (category != 0)
{
dict.Add("category", category.ToString());
}
var res = await _client.getList<News>(dict);
return res.Value;
}
and im ny viewmodel
foreach (var item in await _newsService.GetAllNewsByParams(
_To,
_OrderBy,
_From, _Page,
selectedTag == null ? _SeletedNewsTagId : selectedTag.Id))
{
NewsList.Add(item);
}
Is his query executed synchronously ?
How do I make it an asynchronous?
First of all I would really encourage you to use RestSharp, it really simplifies making HTTP requests and deserialise them. Add a RestSharp nuget package to your project. Here is how your code will look like using RestSharp.
public class BaseClient
{
protected IRestClient _client = null;
protected string _urlObj;
protected string _basePath;
public BaseClient()
{
_client = new RestClient();
}
public async Task<Result<IList<T>>> GetList<T>(string path, Dictionary<string, object> parametrs = null)
{
var request = new RestRequest(path, Method.GET);
if (parametrs != null)
{
foreach (var keyValue in parametrs)
{
request.AddQueryParameter(keyValue.Key, keyValue.Value);
}
}
var response = await _client.Execute<List<T>>(request);
if (response.IsSuccess)
{
return new Result<IList<T>>()
{
Success = true,
Value = response.Data
};
}
else
{
var error = new Result<IList<T>>()
{
Error = response.StatusCode.ToString(),
Message = response.StatusDescription,
Success = false
};
return error;
}
}
}
In your service
public async Task<IList<News>> GetAllNewsByParams(DateTime from,
string orderBy = "-published",
DateTime to = new DateTime(),
int page = 1, int category = 0)
{
var dict = new Dictionary<string, object> {
{"from", from.ToString("s")},
{"order_by", orderBy.ToString()},
{"to", to.ToString("s")},
{"page", page.ToString()}
};
if (category != 0)
{
dict.Add("category", category.ToString());
}
var res = await _client.GetList<News>(_config.NewsPath, dict);
return res.Value;
}
And in your viewmodel
var news = await _newsService.GetAllNewsByParams(
_To,
_OrderBy,
_From, _Page,
selectedTag == null ? _SeletedNewsTagId : selectedTag.Id);
foreach (var item in news)
{
NewsList.Add(item);
}
This will be 100% asynchronous.
Is there more efficient way to build HTML table than the one I'm trying on right now?
I'm getting an object and it has some list of entities in it. So I need to pass through each of them and build first a cell and then add it to an row and finally adding it in table.
The thing I'm trying on is totally messy, kind of works, but it has too much of redundant code.
public static string CreateNotificationMailMessage(NotificationMailMessage mailMessageObject)
{
var table = new HtmlTable();
var mailMessage = new StringBuilder();
string html;
if (mailMessageObject.InvalidCompanies.Any())
{
HtmlTableRow row;
HtmlTableCell cell;
foreach (var invalidCompany in mailMessageObject.InvalidCompanies)
{
row = new HtmlTableRow();
cell = new HtmlTableCell();
cell.InnerText = invalidCompany.BusinessName;
row.Cells.Add(cell);
cell.InnerText = invalidCompany.SwiftBIC;
row.Cells.Add(cell);
cell.InnerText = invalidCompany.IBAN;
row.Cells.Add(cell);
table.Rows.Add(row);
}
}
using (var sw = new StringWriter())
{
table.RenderControl(new HtmlTextWriter(sw));
html = sw.ToString();
}
mailMessage.AppendFormat(html);
return mailMessage.ToString();
}
At the end I want to return text version of created HTML table.
The problem is that I have much more properties than those 3 (BusinessName, SwiftBIC and IBAN) and plus I have one more list of objects inside of mailMessageObject, so the code would be terrible.
Anybody has an idea how to solve this in simpler and cleaner way?
I would just like to supplement Steve Harris' answer with a class library that is a little more built out. His answer is a totally elegant solution that made a windows service I was creating not have to reference System.Web for no good reason!
Classes Defined:
public static class Html
{
public class Table : HtmlBase, IDisposable
{
public Table(StringBuilder sb, string classAttributes = "", string id = "") : base(sb)
{
Append("<table");
AddOptionalAttributes(classAttributes, id);
}
public void StartHead(string classAttributes = "", string id = "")
{
Append("<thead");
AddOptionalAttributes(classAttributes, id);
}
public void EndHead()
{
Append("</thead>");
}
public void StartFoot(string classAttributes = "", string id = "")
{
Append("<tfoot");
AddOptionalAttributes(classAttributes, id);
}
public void EndFoot()
{
Append("</tfoot>");
}
public void StartBody(string classAttributes = "", string id = "")
{
Append("<tbody");
AddOptionalAttributes(classAttributes, id);
}
public void EndBody()
{
Append("</tbody>");
}
public void Dispose()
{
Append("</table>");
}
public Row AddRow(string classAttributes = "", string id = "")
{
return new Row(GetBuilder(), classAttributes, id);
}
}
public class Row : HtmlBase, IDisposable
{
public Row(StringBuilder sb, string classAttributes = "", string id = "") : base(sb)
{
Append("<tr");
AddOptionalAttributes(classAttributes, id);
}
public void Dispose()
{
Append("</tr>");
}
public void AddCell(string innerText, string classAttributes = "", string id = "", string colSpan = "")
{
Append("<td");
AddOptionalAttributes(classAttributes, id, colSpan);
Append(innerText);
Append("</td>");
}
}
public abstract class HtmlBase
{
private StringBuilder _sb;
protected HtmlBase(StringBuilder sb)
{
_sb = sb;
}
public StringBuilder GetBuilder()
{
return _sb;
}
protected void Append(string toAppend)
{
_sb.Append(toAppend);
}
protected void AddOptionalAttributes(string className = "", string id = "", string colSpan = "")
{
if (!id.IsNullOrEmpty())
{
_sb.Append($" id=\"{id}\"");
}
if (!className.IsNullOrEmpty())
{
_sb.Append($" class=\"{className}\"");
}
if (!colSpan.IsNullOrEmpty())
{
_sb.Append($" colspan=\"{colSpan}\"");
}
_sb.Append(">");
}
}
}
Usage:
StringBuilder sb = new StringBuilder();
using (Html.Table table = new Html.Table(sb, id: "some-id"))
{
table.StartHead();
using (var thead = table.AddRow())
{
thead.AddCell("Category Description");
thead.AddCell("Item Description");
thead.AddCell("Due Date");
thead.AddCell("Amount Budgeted");
thead.AddCell("Amount Remaining");
}
table.EndHead();
table.StartBody();
foreach (var alert in alertsForUser)
{
using (var tr = table.AddRow(classAttributes: "someattributes"))
{
tr.AddCell(alert.ExtendedInfo.CategoryDescription);
tr.AddCell(alert.ExtendedInfo.ItemDescription);
tr.AddCell(alert.ExtendedInfo.DueDate.ToShortDateString());
tr.AddCell(alert.ExtendedInfo.AmountBudgeted.ToString("C"));
tr.AddCell(alert.ExtendedInfo.ItemRemaining.ToString("C"));
}
}
table.EndBody();
}
return sb.ToString();
As I've recently come to play with creating IDisposable classes, I think this would be both efficient for this specific task, and much easier to read:
Create some very simple classes
/// <summary>
/// https://stackoverflow.com/a/36476600/2343
/// </summary>
public class Table : IDisposable
{
private StringBuilder _sb;
public Table(StringBuilder sb, string id = "default", string classValue="")
{
_sb = sb;
_sb.Append($"<table id=\"{id}\" class=\"{classValue}\">\n");
}
public void Dispose()
{
_sb.Append("</table>");
}
public Row AddRow()
{
return new Row(_sb);
}
public Row AddHeaderRow()
{
return new Row(_sb, true);
}
public void StartTableBody()
{
_sb.Append("<tbody>");
}
public void EndTableBody()
{
_sb.Append("</tbody>");
}
}
public class Row : IDisposable
{
private StringBuilder _sb;
private bool _isHeader;
public Row(StringBuilder sb, bool isHeader = false)
{
_sb = sb;
_isHeader = isHeader;
if (_isHeader)
{
_sb.Append("<thead>\n");
}
_sb.Append("\t<tr>\n");
}
public void Dispose()
{
_sb.Append("\t</tr>\n");
if (_isHeader)
{
_sb.Append("</thead>\n");
}
}
public void AddCell(string innerText)
{
_sb.Append("\t\t<td>\n");
_sb.Append("\t\t\t"+innerText);
_sb.Append("\t\t</td>\n");
}
}
}
Then you can define your table using:
StringBuilder sb = new StringBuilder();
using (Html.Table table = new Html.Table(sb))
{
foreach (var invalidCompany in mailMessageObject.InvalidCompanies)
{
using (Html.Row row = table.AddRow())
{
row.AddCell(invalidCompany.BusinessName);
row.AddCell(invalidCompany.SwiftBIC);
row.AddCell(invalidCompany.IBAN);
}
}
}
string finishedTable = sb.ToString();
It is a decent approach, and just 'what it takes' to output something as complicated as HTML - unless you want to do it using plain strings (which is just as messy, if not worse).
One improvement: do not use the same cell object multiple times, you run the risk of getting incorrect output. Improved code:
row.Cells.Add(new HtmlTableCell { InnerText = invalidCompany.BusinessName });
row.Cells.Add(new HtmlTableCell { InnerText = invalidCompany.SwiftBIC });
row.Cells.Add(new HtmlTableCell { InnerText = invalidCompany.IBAN });
Of course you can also create your own helpers for creating cells, for creating a row full of cells, etc. There are also good libraries for this, e.g. see https://www.nuget.org/packages/HtmlTags/.
I think maybe you can add a function to get all the properties from your object. And then just iterate over them. Also you can create a list of properties that need to be displayed in your message.
private static PropertyInfo[] GetProperties(object obj)
{
return obj.GetType().GetProperties();
}
// -------
foreach (var invalidCompany in mailMessageObject.InvalidCompanies)
{
var properties = GetProperties(invalidCompany);
foreach (var p in properties)
{
string name = p.Name;
if(propertiesThatNeedToBeDisplayed.Contains(name)
{
cell.InnerText = p.GetValue(invalidCompany, null);
row.Cells.Add(cell);
table.Rows.Add(row);
}
}
}
I am trying pass an argument to a NUnit test after reading a CSV file i.e.
[Test, TestCaseSource(typeof(RegistrationData), "GetTestData")]
public void RegisterUserTest(RegistrationData registrationData)
{
RegisterNewUser registration = new RegisterNewUser(this.driver);
this.driver.Navigate().GoToUrl(baseURL + "/mercuryregister.php");
registration.registerNewUser(registrationData);
}
but I get the error:
System.InvalidCastException : Unable to cast object of type
'RegisterUser.RegistrationData' to type
'System.Collections.IEnumerable'.RegisterUser.UserRegistrationTest.RegisterUserTest
private RegistrationData GetTestData()
{
DataTable dt = DataTable.New.ReadCsv(#"C:\datafolder\regdata.csv");
RegistrationData registrationData = new RegistrationData();
foreach (Row row in dt.Rows)
{
registrationData.setfirstName(row["FirstName"]);
registrationData.setfirstName(row["LastName"]);
registrationData.setPhone(row["Phone"]);
registrationData.setUserName(row["UserName"]);
registrationData.setAddress1(row["Add1"]);
registrationData.setAddress2(row["Add2"]);
registrationData.setCity(row["City"]);
registrationData.setState(row["State"]);
registrationData.setPostalcode(row["Postalcode"]);
registrationData.setCountry(row["Country"]);
registrationData.setEmail(row["Email"]);
registrationData.setPassword(row["Password"]);
registrationData.setConfimPassword(row["Cpassword"]);
}
// return new RegistrationData[][] { { registrationData } };
return registrationData;
}
Example of the ModelTestCaseSource:
public class ModelTestCaseSource
{
public IEnumerable<TestCaseData> GetTestCases()
{
DataTable dt = DataTable.New.ReadCsv(#"C:\datafolder\regdata.csv");
foreach (Row row in dt.Rows)
{
var registrationData = new RegistrationData();
registrationData.setfirstName(row["FirstName"]);
registrationData.setfirstName(row["LastName"]);
registrationData.setPhone(row["Phone"]);
registrationData.setUserName(row["UserName"]);
registrationData.setAddress1(row["Add1"]);
registrationData.setAddress2(row["Add2"]);
registrationData.setCity(row["City"]);
registrationData.setState(row["State"]);
registrationData.setPostalcode(row["Postalcode"]);
registrationData.setCountry(row["Country"]);
registrationData.setEmail(row["Email"]);
registrationData.setPassword(row["Password"]);
registrationData.setConfimPassword(row["Cpassword"]);
yield return new TestCaseData(new object[] { registrationData });
}
}
}
Usage:
[Test, TestCaseSource(typeof(ModelTestCaseSource), "GetTestCases")]
public void RegisterUserTest(RegistrationData registrationData)
{
RegisterNewUser registration = new RegisterNewUser(this.driver);
this.driver.Navigate().GoToUrl(baseURL + "/mercuryregister.php");
registration.registerNewUser(registrationData);
}