How to get a GitHub repository using Octokit C# - c#

I'm trying to integrate GitHubAPI in a web app that I am developing. I heard that the best way to do this in C# is using Octokit library but I can't find a very explicit documentation. Now, first of all I am trying to get a repo from GitHub. When I started, I manually accessed the endpoints as shown in the GitHub API. However, it seems that doing a commit that way is not possible so I switched to Octokit but I have some problems here.
Here is my OctokitGitService class:
*imports here*
namespace Web.Git.Api.Services;
public class OctokitGithubClient
{
private readonly Config _config;
private readonly GitHubClient _gitClient;
public List<Repository> Repositories { get; set; }
public OctokitGithubClient(IOptions<Config> options)
{
_config = options.Value;
var ghe = new Uri(_config.apiUrl);
_gitClient= new GitHubClient(new ProductHeaderValue("web-app-v1"), ghe);
var tokenAuth = new Credentials(_config.PAT.git);
_gitClient.Credentials = tokenAuth;
}
public async Task<List<Repository>> getGithubRepository()
{
var data = _gitClient.Repository.GetAllForCurrent();
Repositories = new List<Repository>(data);
return Repositories;
}
}
And I get this error message:
error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List<Octokit.Repository>' to 'Octokit.Repository' [/root/projects/WebAppProject/src/Web.Git.Api/Web.Git.Api.csproj]
Can someone please give me a hint? I was looking through Octokit doc but I can't find API documentations. Ty!

You can read more about asynchronous programming in C#. Here is a good place to start.
GetAllForCurrent() signature is Task<IReadOnlyList<Repository>> GetAllForCurrent(); If we want to use this asynchronously, we use await and then we convert IReadOnlyList<Repository> to List<Repository> using ToList()
public async Task<List<Repository>> GetGithubRepositoryAsync()
{
var repos = await _gitClient.Repository.GetAllForCurrent();
return repos.ToList();
}
If we want to use it synchronously, one way is to use Result of the Task<>
public List<Repository> GetGithubRepository()
{
return _gitClient.Repository.GetAllForCurrent().Result.ToList();
}
As mentioned in this link using Result property is not recommended
The Result property is a blocking property. If you try to access it before its task is finished, the thread that's currently active is blocked until the task completes and the value is available. In most cases, you should access the value by using await instead of accessing the property directly.

Related

CopyBatchRequestProperties and DeleteRequestContainer replacement in AspNetCore

I got a code which used the HttpRequest from Microsoft.AspNet.Http namespace and after updating the code, I am now using the Microsoft.AspNetCore.Http namespace along with Microsoft.AspNetCore.OData v8.0.12.
In the code I had invoked these methods for OData batch operation:
changeSetContext.Request.CopyBatchRequestProperties(request);
changeSetContext.Request.DeleteRequestContainer(false);
After the namespace change, these methods are no longer defined. Is there a replacement in the AspNetCore namespace version > 8 for what these methods were doing?
You could check the documents:
CopyBatchRequestProperties Method in Asp.Net
CopyBatchRequestProperties Method in Asp.Net Core
DeleteRequestContainer Method in Asp.Net
DeleteRequestContainer Method in Asp.Net Core
The request should be type of Microsoft.AspNetCore.Http.HttpRequest instead of System.Net.Http.HttpRequestMessage
So I have some bad news. There is no direct equivalent in v8.0.12. So you will need to find an alternate solution or write your own implementation. The following is how you might create your own implementation:
CopyBatchRequestProperites:
public static class HttpRequestExtensions
{
public static void CopyBatchRequestProperties(this HttpRequest targetRequest, HttpRequest sourceRequest)
{
foreach (var property in sourceRequest.GetType().GetProperties())
{
var value = property.GetValue(sourceRequest);
property.SetValue(targetRequest, value);
}
}
}
using:
var sourceRequest = changeSetContext.Request;
var targetRequest = new HttpRequest();
targetRequest.CopyBatchRequestProperties(sourceRequest);
for deleting a request container you can try setting its value to null.
DeleteRequestContainer:
You can make the example an extension method of the HttpRequest class:
public static class HttpRequestExtensions
{
public static void DeleteRequestContainer(this HttpRequest request, bool deleteRequest)
{
if(deleteRequest)
{
var request = changeSetContext.Request;
request.Headers.Remove("OData-Version");
request.Headers.Remove("Content-Type");
request.Content = null;
}
}
}
using
var request = changeSetContext.Request;
request.DeleteRequestContainer(false);
This might not work for your exact case but should help to give you an idea of what you could probably do. Might even be worth looking at the source code in version 7.5.8 to see if you can figure out how to recreate it for yourself.

How do I stop my Azure Redis implementation from causing a Newtonsoft Self referencing loop error?

I'm currently trying to implement a solution to read and write data to an Azure Redis cache. For the most part the implementation is complete however, when I attempt to read the redis cache I get an error of:
An unhandled host error has occured Newtonsoft.Json: Self referencing
loop detected for property 'target' with type
'System.Runtime.CompilerServices.AsyncTaskMethodBuilder'
1+AsyncStateMachineBox'1[System.String,
MyProject.Service.CarData.RedisCachService+d__2]'.
Path 'moveNextAction'.
Here is my code so far.
Method
private readonly IRedisService _redisService;
public Car(IRedisService redisService)
{
_redisService = redisService;
}
[FunctionName("GetCarData")]
public async Task<IActionResult> GetCarData([HttpTrigger(AuthorisationLevel.Anonymous, "get", Route = "v1/car/get")] HttpRequest req)
{
var data = _redisService.GetCarDataAsync("testId")
return new OkObjectResult(data)
}
Interface
public interface IRedisService
{
Task<string> GetCarDataAsync(string key)
}
Service
private readonly IConnectionMultiplexer _conn;
public RedisService(IConnectionMultiplexer conn)
{
_conn = conn;
}
public async Task<string> GetCarDataAsync(string key)
{
var data = _conn.GetDatabase();
return await db.StringGetAsync(key);
}
Startup
// Redis Information
services.AddSingleton<IRedisService, RedisService>();
services.AddSingleton<IConnectionMultiplexer>(x =>
ConnectionMultiplexer.Connect("Azure Redis host],abortConnection=false,ssl=true,password=[Primary Access Key]")
)
As this is a very basic function that simply requests data based on a key and also my first time implementing this, I'm not sure exactly what is causing the problem. Typically I've seen similar with models which reference each other but this is not the same. Has anyone had this before or can point me in the right direction?

In C# , How should I change the method syntax when I want to move it from my controller to a repository file?

I am trying to create a method in a repository file that will call things from my query and model files, I tried to solve the errors by using directive, it got rid of most of the errors but I still have two persistent ones. The method was previously in my controller, and since I want to move it to my repo folder, I am guessing the syntax will change somewhat. I am new to C# and I am not sure how to approach this.
Error 1
'GetSportsView': I get an error saying "not all code paths return a value"
Error 2
'QueryView>' :
I get "object does not contain a definition for 'QueryView' and no accessible extension method(are you missing a directive or assembly reference)"
Please see the code below
public class SportsRepository : ISportsRepository
{
private readonly IHttpConnectionManager httpConnectionManager;
private readonly string sportsGrandViewUrl;
private readonly Guid playerId;
private SearchCriteria searchCriteria;
private object sportsGameRepository;
public SportsRepository(taDomainCommunicationSettings taDomainCommunicationSettings, IHttpConnectionManager httpConnectionManager)
{
this.sportsGrandViewUrl = taDomainCommunicationSettings.SportsGrandViewUrl;
this.httpConnectionManager = httpConnectionManager;
}
public async Task<IActionResult> GetSportsView(Guid playerId, [FromQuery]SearchCriteria searchCriteria)
{
var query = new GetSportsTransactionsQuery(playerId, searchCriteria);
var result = await this.sportsGameRepository.QueryView<IEnumerable<SportsTransactionModel>>(query);
}
public Task<T> QueryView<T>(IAPIQuery qry)
{
return this.httpConnectionManager.PostInQueryWrapperAsync<T>(this.sportsGrandViewUrl, qry);
}
}
}
How should I approach the syntax style to make it work the same way it worked in my controller?
Seems like you have mixed up controller's code with repository's. Like FromQuery is used with request's query parameters, it should not be used here in repository.
When I first began with web development using repository pattern, this article helped me lot : https://chsakell.com/2015/02/15/asp-net-mvc-solution-architecture-best-practices/.
Basically your controller should call service, and your service will interact with your repository. This helps in keeping both code and logic modularized.

Asp.net Core DI: Using SemaphoreSlim for write AND read operations with Singleton

I am re-tooling an ASP.NET CORE 2.2 app to avoid using the service locator pattern in conjunction with static classes. Double bad!
The re-tooling is involving the creation and injection of Singleton object as a repository for some global data. The idea here to avoid hits to my SQL server for some basic/global data that gets used over and over again in requests. However, this data needs to be updated on an hourly basis (not just at app startup). So, to manage the situation I am using SemaphoreSlim to handle one-at-a-time access to the data objects.
Here is a paired down sketch of what what I'm doing:
namespace MyApp.Global
{
public interface IMyGlobalDataService
{
Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1);
Task LoadMyImportantDataListAsync();
}
public class MyGlobalDataService: IMyGlobalDataService
{
private MyDbContext _myDbContext;
private readonly SemaphoreSlim myImportantDataLock = new SemaphoreSlim(1, 1);
private List<ImportantDataItem> myImportantDataList { get; set; }
public async Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1)
{
List<ImportantDataItem> list;
myImportantDataLock.WaitAsync();
try
{
list = myImportantDataList.Where(itm => itm.Prop1 == prop1).ToList();
}
finally
{
myImportantDataLock.Release();
}
return list;
}
public async Task LoadMyImportantDataListAsync()
{
// this method gets called when the Service is created and once every hour thereafter
myImportantDataLock.WaitAsync();
try
{
this.MyImportantDataList = await _myDbContext.ImportantDataItems.ToListAsync();
}
finally
{
myImportantDataLock.Release();
}
return;
}
public MyGlobalDataService(MyDbContext myDbContext) {
_myDbContext = myDbContext;
};
}
}
So in effect I am using the SemaphoreSlim to limit to one-thread-at-a-time access, for both READ and UPDATING to myImportantDataList. This is really uncertain territory for me. Does this seem like an appropriate approach to handle my injection of a global data Singleton throughout my app? Or should I expect insane thread locking/blocking?
The problem with using SemaphoreSlim is scalability.
As this is in a web application, it's fair to assume that you want to have the potential for more than one reader to access the data simultaneously. However, you are (understandably) limiting the number of requests for the semaphore that can be generated concurrently to 1 (to prevent concurrent read and write requests). This means you will serialize all reads too.
You need to use something like ReaderWriterLockSlim to allow multiple threads for reading, but ensure exclusive access for writing.
Creyke's answer hit the nail on the head for me: using ReaderWriterLockSlim. So I've marked it as the accepted answer. But I am posting my revised solution in case it might be helpful to anyone. Important to note that I'm using the following package to provide async functionality to ReaderWriterLockSlim: https://www.nuget.org/packages/Nito.AsyncEx/
using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Text;
namespace MyApp.Global
{
public interface IMyGlobalDataService
{
Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1);
Task LoadMyImportantDataListAsync();
}
public class MyGlobalDataService : IMyGlobalDataService
{
private MyDbContext _myDbContext;
private readonly AsyncReaderWriterLock myImportantDataLock = new AsyncReaderWriterLock();
private List<ImportantDataItem> myImportantDataList { get; set; }
public async Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1)
{
List<ImportantDataItem> list;
using (await myImportantDataLock.ReaderLockAsync())
{
list = myImportantDataList.Where(itm => itm.Prop1 == prop1).ToList();
}
return list;
}
public async Task LoadMyImportantDataListAsync()
{
// this method gets called when the Service is created and once every hour thereafter
using (await myImportantDataLock.WriterLockAsync())
{
this.MyImportantDataList = await _myDbContext.ImportantDataItems.ToListAsync();
}
return;
}
public MyGlobalDataService(MyDbContext myDbContext)
{
_myDbContext = myDbContext;
};
}
}

MVVM - How to use a given REST-API dll

I'm starting to learn MVVM in C# with mvvmlight. From an other project, I have a given (but self-made) REST-API as a dll. This dll I'm trying to use in this new project. It is completly based on this API. I'm unsure about the "model"-part and don't want to repeat myself while coding.
Problem is like this: The application has several "plugins/sections". Let's look at a sample like the "groups" section. Now the API defines a groups-Entity-Class with all properties like "groupname, permissions, members". Then, the Application must have a groups model and also a groupsViewModel and a GroupsView for the UI. There, I also must list/show "groupname, permissions, members".
Question is: Do I have to redeclare all the properties from the dll-entity-class in the mvvm-model-class? Or what is the best way to use a rest api? Is there any pattern. I read about the Extension Object Pattern. But I have no clue.
Hint: Since the API is written by myself (only classes, no mvvm or other frameworks), I could modify it as needed.
Thanks in advance!
Take a look at the Repository Pattern: https://msdn.microsoft.com/en-us/library/ff649690.aspx. Basically you create a repository which does the API calls, and transforms the entities to a format you can use in your view.
If the objects from the API are already sufficient you can simply return them, otherwise convert them in the Repository to something more useful.
In the ViewModel you then simply ask the repository to return the model, and need not to care how and in what format it got them.
Small Example:
interface IApiRepository
{
Task<ObservableCollection<ApiModel>> GetApiModelsAsync();
}
class ApiRepository : IApiRepository
{
private async Task<ObservableCollection> GetApiModelsAsync()
{
var myCollection = new ObservableCollection<ApiModel>();
var result = await DoMyApiCall();
foreach (result as item)
{
var newModel = new ApiModel();
newModel.fillFromApi(item);
myCollection.Add(newModel);
}
return myCollection;
}
}
class MyViewModel : ViewModelBase
{
private readonly IApiRepository _apiRepository;
public MyViewModel(IApiRepository apiRepository)
{
_apiRepository = apiRepository;
InitializeViewModel();
}
private ObservableCollection<ApiModel> _apiModels;
public ObservableCollection<ApiModel> ApiModels
{
get { return _apiModels; }
set { Set(ref _apiModels, value); }
}
private async void InitializeViewModel()
{
//as soon as the repo is finished ApiModels will raise the RaisePropertyChanged event
ApiModels = await _apiRepository.GetApiModelsAsync();
}
}
//in you ViewModelLocator
SimpleIoC.Default.Register<IApiRepository, ApiRepository>();
SimpleIoC.Default.Register<MyViewModel>();
//construct your viewmodel (with the injected repository)
var vm = SimpleIoc.Default.GetInstance<MyViewModel>();
with the help of this pattern you can do some other awesome things: 
if you add a new data source you need only to change the repository and not the ViewModel.
if you want to test your view (for example in Design Mode) you can inject a MockApiRepository which returns sample data instead of doing an API call

Categories