C# Octokit Update File if it Exists, Otherwise Create - c#

I'm wondering whether there is a clean way to create a new file using Octokit in case the file specified doesn't exist, or otherwise create it? I'm currently attempting to do this via a try-catch block. However, there needs to be a more straightforward way to do this except a try-catch block?
private IRepositoryContentsClient Content => _client.Repository.Content;
public void TransmitLog(string logFilePath)
{
var logContent = LogFileToString(logFilePath);
var githubPath = GenerateGithubPath(logFilePath);
try
{
UpdateLog(githubPath, logContent);
}
catch (NotFoundException)
{
CreateLog(githubPath, logContent);
}
catch (AggregateException)
{
CreateLog(githubPath, logContent);
}
}
private void UpdateLog(string githubPath, string logContent)
{
// MY APP FAILS HERE
var existingFile = Content.GetAllContentsByRef(
_owner, _repo, githubPath, _branch).Result;
// update the file
var relevantSha = existingFile.First().Sha;
var updateRequest = new UpdateFileRequest("Log update" + DateTime.UtcNow, logContent, relevantSha, _branch);
var updateChangeSet = Content.UpdateFile(_owner, _repo, githubPath, updateRequest);
}
private void CreateLog(string githubPath, string logFileContent)
{
// if file is not found, create it
var createRequest =
new CreateFileRequest("Log Creation" + DateTime.UtcNow, logFileContent, _branch);
var createChangeSet = Content.CreateFile(_owner, _repo, githubPath, createRequest);
}
EDIT: I initially had both CreateLog and UpdateLog declared as async void, which led to a whole set of other problems. I edited that out since what I'm really interested in is how to avoid this try/catch structure.

Related

Add BackgroundImage with EPPlus only allows path but cannot get path in Blazor WASM

This may not be 100% an EPPlus issue, but since it is Blazor WASM it appears I cannot get the file path to a static image in the wwwroot/images folder. I can get the url and paste it into a browser and that works, even adding that same path to the src attribute of an img works, neither of those helps me.
FYI "background" in this context means a watermark.
It appears that the EPPlus dev team only wants a drive path the file (ex. C:\SomeFolder\SomeFile.png), and I am not seeing how to get that within Blazor WASM. I can get the bytes of the file in c# and even a stream, but no direct path.
My code is the following:
using (var package = new ExcelPackage(fileName))
{
var sheet = package.Workbook.Worksheets.Add(exportModel.OSCode);
sheet.BackgroundImage.SetFromFile("https://localhost:44303/images/Draft.png");
...
}
This returns an exception:
Unhandled exception rendering component: Can't find file /https:/localhost:44303/images/Draft.png
Noticing that leading / I even tried:
sheet.BackgroundImage.SetFromFile("images/Draft.png");
Which returned the same error:
Unhandled exception rendering component: Can't find file /images/Draft.png
So, I am perhaps needing 1 of 2 possible answers:
A way to get a local drive path to the file so the .SetFromFile method is not going to error.
To have a way to set that BackgroundImage property with a byte array or stream of the image. There is this property BackgroundImage.Image but it is readonly.
Thanks to a slap in the face from #Panagiotis-Kanavos I wound up taking the processing out of the client and moving it to the server. With that, I was able to use Static Files to add the watermark with relatively little pain.
In case anyone may need the full solution (which I always find helpful) here it is:
Here is the code within the button click on the Blazor component or page:
private async Task GenerateFile(bool isFinal)
{
...
var fileStream = await excelExportService.ProgramMap(exportModel);
var fileName = "SomeFileName.xlsx";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await jsRuntime.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
That calls a client-side service that really just passes control over to the server:
public class ExcelExportService : IExcelExportService
{
private const string baseUri = "api/excel-export";
private readonly IHttpService httpService;
public ExcelExportService(IHttpService httpService)
{
this.httpService = httpService;
}
public async Task<Stream> ProgramMap(ProgramMapExportModel exportModel)
{
return await httpService.PostAsJsonForStreamAsync<ProgramMapExportModel>($"{baseUri}/program-map", exportModel);
}
}
Here is the server-side controller that catches the call from the client:
[Route("api/excel-export")]
[ApiController]
public class ExcelExportController : ControllerBase
{
private readonly ExcelExportService excelExportService;
public ExcelExportController(ExcelExportService excelExportService)
{
this.excelExportService = excelExportService;
}
[HttpPost]
[Route("program-map")]
public async Task<Stream> ProgramMap([FromBody] ProgramMapExportModel exportModel)
{
return await excelExportService.ProgramMap(exportModel);
}
}
And that in-turn calls the server-side service where the magic happens:
public async Task<Stream> ProgramMap(ProgramMapExportModel exportModel)
{
var result = new MemoryStream();
ExcelPackage.LicenseContext = LicenseContext.Commercial;
var fileName = #$"Gets Overwritten";
using (var package = new ExcelPackage(fileName))
{
var sheet = package.Workbook.Worksheets.Add(exportModel.OSCode);
if (!exportModel.IsFinal)
{
var pathToDraftImage = #$"{Directory.GetCurrentDirectory()}\StaticFiles\Images\Draft.png";
sheet.BackgroundImage.SetFromFile(pathToDraftImage);
}
...
sheet.Cells.AutoFitColumns();
package.SaveAs(result);
}
result.Position = 0; // Without this, data does not get written
return result;
}
For some reason, this next method was not needed when doing this on the client-side but now that it is back here, I had to add a method that returned a stream specifically and used the ReadAsStreamAsync instead of ReadAsJsonAsync:
public async Task<Stream> PostAsJsonForStreamAsync<TValue>(string requestUri, TValue value, CancellationToken cancellationToken = default)
{
Stream result = default;
var responseMessage = await httpClient.PostAsJsonAsync(requestUri, value, cancellationToken);
try
{
result = await responseMessage.Content.ReadAsStreamAsync(cancellationToken: cancellationToken);
}
catch (HttpRequestException e)
{
...
}
return result;
}
Lastly, in order for it to give the end-user a download link, this was used (taken from the Microsoft Docs):
window.downloadFileFromStream = async (fileName, contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
const anchorElement = document.createElement("a");
anchorElement.href = url;
anchorElement.download = fileName ?? "";
anchorElement.click();
anchorElement.remove();
URL.revokeObjectURL(url);
}

Get open extensions using Graph API C#

I am building a ASP.NET Core website, where the user signs in, and is able to save things like the theme of the page, which then triggers some js code:
var data = {
userName: userName,
key: "theme",
value: localStorage.getItem("theme")
}
var pendingRequest;
if (pendingRequest) {
pendingRequest.abort()
pendingRequest = null;
}
pendingRequest = $.post('/Account/setExtension', data, function (data) {
pendingRequest = null;
$('#settingsModal').modal('hide');
});
which calls the controller:
[HttpPost]
public void setExtension(string userName, string key, string value)
{
Dictionary<string, object> addData = new Dictionary<string, object>
{
{key, value }
};
GraphHelper.updateExtension(userName, "com.user.roamingSettings", addData).ConfigureAwait(false);
}
which calls the checks if the extension exists, and then decides to create it, or update the existing extension:
public static async Task<bool> extensionExistsAsync(string userName, string extensionName)
{
try
{
var graphClient = GetAuthenticatedClient();
// if extension doesnt exist
bool extensionFound = false;
var extensions = await graphClient.Users[userName].Extensions.Request().GetAsync();
foreach (var extension in extensions)
{
if (extension.Id == extensionName)
{
extensionFound = true;
}
}
return extensionFound;
}
catch (Exception e)
{
Debug.WriteLine(e.Message.ToString());
throw;
}
}
The problem is, the code just stops running on this line:
var extensions = await graphClient.Users[userName].Extensions.Request().GetAsync();
It doesn't throw or anything. Stepping through it line by line, it returns all the way to the assignment, and the output window is empty when it stops. Why is this? How can I get an extension by name, or get all extensions to see which ones exist, either by graph api, or calls?
When ever you use the below call
var extensions = await graphClient.Users[userName].Extensions.Request().GetAsync();
you will be getting UserExtensionsCollectionPage object which gives the list of extensions of a user.
This page doesn't have Id property, the extension objects that are present in this UserExtensionsCollectionPage object have it. So use the below code to print the id and type of the Extensions.
var extensions = await graphClient.Users["a1ee289f-4cab-4fc3-885f-d4cbefb48895"].Extensions.Request().GetAsync();
foreach(var data in extensions.CurrentPage)
{
Console.WriteLine("Id: " + data.Id + "Type: " + data.ODataType );
}
This will give you the data as below.

EntityState must be set to null, Created (for Create message) or Changed (for Update message)

In my C# console application I am trying to update an account in CRM 2016. IsFaulted keeps returning true.
The error message it returns when I drill down is the following:
EntityState must be set to null, Created (for Create message) or Changed (for Update message).
Also in case it might cause the fault I have pasted my LINQ query at the bottom.
The answers I get from Google states either that I am mixing ServiceContext and ProxyService (which am not, I am not using it in this context). The others says that I am using context.UpdateObject(object) incorrectly, which I am not using either.
Update: Someone just informed me that the above error is caused because I am trying to return all the metadata and not just the updated data. Still I have no idea how to fix the error, but this information should be helpful.
private static void HandleUpdate(IOrganizationService crmService, List<Entity> updateEntities)
{
Console.WriteLine("Updating Entities: " + updateEntities.Count);
if (updateEntities.Count > 0)
{
try
{
var multipleRequest = new ExecuteMultipleRequest()
{
// Assign settings that define execution behavior: continue on error, return responses.
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = true
},
// Create an empty organization request collection.
Requests = new OrganizationRequestCollection()
};
foreach (var account in updateEntities)
{
multipleRequest.Requests.Add(
new UpdateRequest()
{
Target = account
});
}
ExecuteMultipleResponse response = (ExecuteMultipleResponse)crmService.Execute(multipleRequest);
if (response.IsFaulted)
{
int failedToUpdateAccount = 0;
foreach (ExecuteMultipleResponseItem singleResp in response.Responses)
{
if (singleResp.Fault != null)
{
string faultMessage = singleResp.Fault.Message;
var account = ((UpdateRequest)multipleRequest.Requests[singleResp.RequestIndex]).Target;
Log.Error($"Error update acc.id: {account.Id}.Error: {singleResp.Fault.Message}.");
failedToUpdateAccount++;
}
}
Log.Debug($"Failed to update {failedToUpdateAccount} accounts.");
}
else
{
Log.Debug("Execute multiple executed without errors");
}
}
catch (Exception ex)
{
Log.Error($"Error while executing Multiplerequest", ex);
}
}
}
// LINQ below
private static List<Account> GetAllActiveCRMAccounts(CRM2011DataContext CRMcontext)
{
Console.WriteLine("Start Getting CRMExistingAccounts ....");
List<Account> CRMExisterendeAccounts = new List<Account>();
try
{
CRMExisterendeAccounts = (from a in CRMcontext.AccountSet
where a.StateCode == AccountState.Active
where a.anotherVariable == 1
select new Account()
{
my_var1 = a.myVar1,
my_var2 = a.myVar2,
AccountId = a.AccountId,
anotherVar = a.alsoThisVar,
}).ToList();
}
catch (FaultException ex)
{
Log.Debug($"GetCRMExistingAccounts Exception { ex.Message}");
Console.WriteLine("GetCRMExistingAccounts Exception " + ex.Message);
throw new Exception(ex.Message);
}
return CRMExisterendeAccounts;
}
And yes, my variables has different names in my system.
The query returns the object just fine with all the correct data.
You can work around this in one of two ways:
1) Create your CRM2011DataContext with the MergeOption set to MergeOption.NoTracking. Entities loaded from a context that is not tracking will have a null EntityState property.
2) You can create a copy of your Entity and save the copy.

C# - how to delete row in realm - android xamarin

i tried this method that I created but it prompts me an error:
Realms.RealmInvalidObjectException:This object is detached. Was it deleted from the realm?'
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
using (var transaction = Realm.GetInstance(config).BeginWrite())
{
Realm.GetInstance(config).Remove(denom_list[0]);
transaction.Commit();
}
}
what is the proper coding for deleting records from database in realm in C# type of coding?
You are doing it the right way. The error message you are getting indicates that the object was removed already. Are you sure it still exists in the realm?
UPDATE:
I decided to update this answer because my comment on the other answer was a bit hard to read.
Your original code should work fine. However, if you want deleteFromDatabase to accept lists with CashDenomination instances that either have been removed already or perhaps were never added to the realm, you would need to add a check. Furthermore, note that you should hold on to your Realm instance and use it in the transaction you created. In most cases, you want to keep it around even longer, though there is little overhead to obtaining it via GetInstance.
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
if (!denom_list[0].IsValid) // If this object is not in the realm, do nothing.
return;
var realm = Realm.GetInstance(config);
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom_list[0]);
transaction.Commit();
}
}
Now, if you want to use identifiers, you could look it up like you do, but still just use Remove:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.All<CashDenomination>().FirstOrDefault(c => c.denom_id == denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
Finally, if your CashDenomination has denom_id marked as PrimaryKey, you could look it up like this:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.ObjectForPrimaryKey<CashDenomination>(denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
public void deleteFromDatabase(Realm realm, long cashDenominatorId)
{
realm.Write(() =>
{
var cashDenominator = realm.All<Person>().Where(c => c.Id == cashDenominatorId);
Realm.RemoveRange<CashDenomination>(((RealmResults<CashDenomination>)cashDenominator));
});
}
Which you would call as
Realm realm = Realm.GetInstance(config);
var denom_list = ...
// ...
deleteFromDatabase(realm, denom_list[0].id);
I already made it having this code :) thanks to #EpicPandaForce 's answer.
public void deleteFromDatabase(int denom_ID, int form_ID)
{
//Realm realm;
//and
//RealmConfiguration config = new RealmConfiguration(dbPath, true);
//was initialized at the top of my class
realm = Realm.GetInstance(config);
realm.Write(() =>
{
var cashflow_denom = realm.All<CashDenomination>().Where(c => c.denom_id == denom_ID);
var cashflow_form = realm.All<CashForm>().Where(c => c.form_id == form_ID);
realm.RemoveRange(((RealmResults<CashDenomination>)cashflow_denom));
realm.RemoveRange(((RealmResults<CashForm>)cashflow_form));
});
}
it is now deleting my data without exception :)

what's best way to check if a S3 object exists?

Currently, I make a GetObjectMetaDataRequest, if the GetObjectMetaDataResponse throw an exception means the object doesn't exist. Is there a better way to check whether the file exists without downloading the file.
you can use S3FileInfo class and Exists method of this class it will hep you to check if file exists without download the file .see the example below I used the AWSSDK 3.1.6 .net(3.5) :
public static bool ExistsFile()
{
BasicAWSCredentials basicCredentials = new BasicAWSCredentials("my access key", "my secretkey");
AmazonS3Config configurationClient = new AmazonS3Config();
configurationClient.RegionEndpoint = RegionEndpoint.EUCentral1;
try
{
using (AmazonS3Client clientConnection = new AmazonS3Client(basicCredentials, configurationClient))
{
S3FileInfo file = new S3FileInfo(clientConnection, "mybucket", "FolderNameUniTest680/FileNameUnitTest680");
return file.Exists;//if the file exists return true, in other case false
}
}
catch(Exception ex)
{
return false;
}
}
If you are in a similar situation as myself and are using .Net Core and don't have access to Amazon.S3.IO (and S3FileInfo method), you can do the following using asynchronous GetObjectMetadataRequest method:
static private AmazonS3Client s3Client = new AmazonS3Client();
public static async Task<bool> FileExistsS3Async(string _bucket, string _key)
{
GetObjectMetadataRequest request = new GetObjectMetadataRequest()
{
BucketName = _bucket,
Key = _key
};
try
{
await s3Client.GetObjectMetadataAsync(request);
return true;
}
catch (AmazonS3Exception exception)
{
return false;
}
}
This function has worked for me when calling within a Unity game. You can also call the above function synchronously using the following:
bool exists = Task.Run(()=>FileExistsS3Async(_bucket, _key)).Result;
Try this solution, it works for me.
AmazonS3Client client = new AmazonS3Client(accessKey, secretKey, regionEndpoint);
S3FileInfo s3FileInfo = new S3FileInfo(client, bucketName, fileName);
return s3FileInfo.Exists;
There is no ListObjectRequest, but instead a ListObjectsRequest where you cannot specify the Key. You then have to go through all the objects to find the one you want. I am currently looking in to it since I seem to get time out errors whilst downloading the file. (If anyone has some idea how to solve that feel free to comment).
You could instead try the List Parts Request if you happen to know the upload id.
Other than that I have no idea. Would like to have a chat with the person who wrote the S3 api...
You're probably going to have to use the REST API yourself, as the method suggested, internally just does exactly the same thing (try...catch on the request)
You can use this code to check whether an object exist in S3 or not:
public class S3CheckFileExists
{
private readonly IAmazonS3 amazonS3;
public S3CheckFileExists(IAmazonS3 amazonS3)
{
this.amazonS3 = amazonS3;
}
public async Task<bool> S3ObjectExists(string bucketName, string keyLocation)
{
var listS3Objects = await this.amazonS3.ListObjectsV2Async(new ListObjectsV2Request
{
BucketName = bucketName,
Prefix = keyLocation, // eg myfolder/myimage.jpg (no / at start)
MaxKeys = 1
});
if (listS3Objects.S3Objects.Any() == false || listS3Objects.S3Objects.All(x => x.Key != keyLocation))
{
// S3 object doesn't exist
return false;
}
// S3 object exists
return true;
}
}
You'll need to register IAmazonS3 in your IoC (aka services) container though:
services.AddAWSService<IAmazonS3>();
Yes.
You can use a ListObjectsRequest. Use the Marker property, and retrieve only 1 element.

Categories