Can I replace Document DB Generic Repository with Cosmos DB SDK 3.0? - azure-cosmosdb

I have created generic repository for CRUD(Create,Read,Update and Delete) operations with Document DB assembly. I want to replace this with Cosmos DB SDK 3.0 SQL API.
Here is my cosmos db generic repository:
public class CosmosDBRepository<T> : ICosmosDBRepository<T> where T : class
{
private readonly DocumentClient _client;
private readonly string DatabaseId = "FleetHub";
public static string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
public CosmosDBRepository()
{
var endpoint = CloudConfigurationManager.GetSetting("CosmoDbEndpoint");
var authkey = CloudConfigurationManager.GetSetting("CosmoDbAuthKey");
try
{
if (endpoint == null || authkey == null)
{
throw new ArgumentNullException("CosmoDbEndpoint or CosmoDbAuthKey could not be found in the config file, check your settings.");
}
if (_client == null)
{
_client = new DocumentClient(new Uri(endpoint), authkey, connectionPolicy: new ConnectionPolicy { EnableEndpointDiscovery = false });
}
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
catch (Exception e)
{
}
}
public async Task<T> GetItemAsync<T>(string id, string partitionkey) where T : class
{
try
{
Document document = await _client.ReadDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), new RequestOptions { PartitionKey = new PartitionKey(partitionkey) });
return (T)(dynamic)document;
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}
}
public async Task<Document> CreateItemAsync<T>(T item) where T : class
{
return await _client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), item);
}
public async Task<IEnumerable<T>> GetItemsAsync<T>(Expression<Func<T, bool>> predicate, Expression<Func<T, object>> orderByDesc, int takeCount = -1)
where T : class
{
var criteria = _client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), new FeedOptions { EnableCrossPartitionQuery = true })
.Where(predicate)
.OrderByDescending(orderByDesc)
.AsDocumentQuery();
IDocumentQuery<T> query = criteria;
List<T> results = new List<T>();
while (query.HasMoreResults)
{
if (takeCount > -1 && results.Count >= takeCount)
{
break;
}
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
public async Task<Document> UpdateItemAsync<T>(string id, T item) where T : class
{
return await _client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), item);
}
private async Task CreateDatabaseIfNotExistsAsync()
{
try
{
await _client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseId));
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
await _client.CreateDatabaseAsync(new Database { Id = DatabaseId });
}
else
{
throw;
}
}
}
private async Task CreateCollectionIfNotExistsAsync()
{
try
{
await _client.ReadDocumentCollectionAsync(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId));
}
catch (DocumentClientException e)
{
}
}
private string GetPartitionKeyAttributeCosmoDbCollection(Type t)
{
// Get instance of the attribute.
CosmoDBCollection attribute =
(CosmoDBCollection)Attribute.GetCustomAttribute(t, typeof(CosmoDBCollection));
if (attribute == null)
{
throw new Exception("The attribute CosmoDbCollection was not found.");
}
return attribute.PartitionKey;
}
private static string GetAttributeCosmoDbCollection<T>(Type t) where T : class
{
CosmoDBCollection attribute =
(CosmoDBCollection)Attribute.GetCustomAttribute(t, typeof(CosmoDBCollection));
return attribute.Name;
}
}
Can I Create generic repository with Cosmos DB SDK 3.0 SQL? Or I need to use Document DB assembly only for to create generic repository.
Any Thoughts?

Yes,you can.Please refer to these documents:
Quickstart: Build a .NET console app to manage Azure Cosmos DB SQL API resources
Azure Cosmos DB.NET V3 SDK (Microsoft.Azure.Cosmos) examples for the SQL API

Related

Task was canceled but the process continued

I am processing a set of records (batch) based on a ID.
Based on exception, I am collecting exception details and sending an email.
However recently I got a “A Task was canceled” exception, so this information was added and sent in an email. But even after the exception, the record made it to the database. There were other exception and those records didn't make it to the database.
This only happens sporadically. Most of the times the exception and the records getting into the database matches.
I am using Autofac.
To give an idea
internal class Program
{
public static void Main ( )
{
IContainer context = Program.BuildContainer( );
try{
context.Resolve<IBatchProvider>().DoAsynBatchProcess().Wait();
}
catch(Exception ex)
{
//log errors
}
}
private static IContainer BuildContainer( )
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<LogBuilder>.As<ILogBuilder>().InstancePerDependency();
builder.RegisterType<EmailSender>.As<IEmailSender>().InstancePerDependency();
builder.RegisterType<BatchProvider>As<IBatchProvider>().InstancePerDependency();
return builder.Build();
}
}
public class BatchProvider : IBatchProvider {
private readonly ILogBuilder _logBulider;
private readonly IEmailsender _emailSender;
public BatchProvider(ILogBuilder logBuilder, IEmailSender emailSender)
{
_logBuilder = logBuilder;
_emailSender = emailSender;
}
public async Task DoAsyncBatchProcess()
{
//Get ID from DB
….
await BatchProcessing (ID)
}
public async Task BatchProcessing (int ID)
{
//Get all records for this ID
//loop through the records and post it.
for (int index=0; i< dataRowArray.Length; ++ index)
{
try{
bool result = await ServiceClient.PostData(guid)
}
catch(Exception ex)
{
//log exception
}
finally
{
//log to file
}
}
await SendEmail ( )
}
private async Task SendEmail()
{
//email
}
private void LogToFile()
{ //log to file
}
}
public class ServiceClient
{
public static async Task<bool> PostData(string guid)
{
using( var client = new HttpClient(new HttpClientHandler() {useDefaultCredentials = true}))
{
string _baseAddress = “http://Mywebserver/“;
client.baseAddress = new Uri(_baseAddress)
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”);
string _method = “MyMethod”;
HttpResponseMessage response = await client.PostAsJsonAsync(_method, new ServiceRequest() {MyGuid = guid}).configureAwait(false);
if(response.IsSuccessStausCode)
{
var result = await response.Content.ReadAsAsync<ServiceResponse>();
return result.Status;
}
else
{
string message = await _response.Content.ReadAsStringAsync();
throw new Exception(message);
}
}
}
}
In my Main method does the .wait() is enough or should I do the following
Context.Resolve().DoAsyncBatchProcess().ConfigureAwait(false).GetAwaiter().GetResult();
Is there a way to ensure that the exception happen and it doesn't continue for that task but continue for the other tasks?
Thank you.

list view is unable to display data from cloud firestore

Here is my code behind
IIdentifiable is a interface that has Id as property.
I am getting a nullrefrence exceptions while extracting data.
public abstract class SalonService<T> : ISalonService<T> where T : IIdentifiable
{
public Task<T> GetSalonAsync(string id)
{
var tcs = new TaskCompletionSource<T>();
FirebaseFirestore.Instance
.Collection("salons")
.Document(id)
.Get()
.AddOnCompleteListener(new OnDocumentCompleteListener<T>(tcs));
return tcs.Task;
}
public Task<IList<T>> GetSalonsAsync()
{
var tcs = new TaskCompletionSource<IList<T>>();
var list = new List<T>();
FirebaseFirestore.Instance
.Collection("salons")
.Get()
.AddOnCompleteListener(new OnCollectionCompleteListener<T>(tcs));
return tcs.Task;
}
}
And the service listeners for collection of data
public class OnCollectionCompleteListener<T> : Java.Lang.Object, IOnCompleteListener
where T : IIdentifiable
{
private System.Threading.Tasks.TaskCompletionSource<IList<T>> _tcs;
public OnCollectionCompleteListener(System.Threading.Tasks.TaskCompletionSource<IList<T>> tcs)
{
_tcs = tcs;
}
public void OnComplete(Task task)
{
if (task.IsSuccessful)
{
var docsObj = task.Result;
if (docsObj is QuerySnapshot docs)
{
_tcs.TrySetResult(docs.Convert<T>());
}
}
}
}
Service listener to display document
public class OnDocumentCompleteListener<T> : Java.Lang.Object, IOnCompleteListener
where T : IIdentifiable
{
private TaskCompletionSource<T> _tcs;
public OnDocumentCompleteListener(TaskCompletionSource<T> tcs)
{
_tcs = tcs;
}
public void OnComplete(Task task)
{
if (task.IsSuccessful)
{
var docObj = task.Result;
if (docObj is DocumentSnapshot docRef)
{
_tcs.TrySetResult(docRef.Convert<T>());
return;
}
}
// something went wrong
_tcs.TrySetResult(default);
}
}
The Document extension as well
public static class DocumentReferenceExtensions
{
public static T Convert<T>(this DocumentSnapshot doc) where T : IIdentifiable
{
try
{
var jsonStr = Newtonsoft.Json.JsonConvert.SerializeObject(doc.Data.ToDictionary());
var item = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(jsonStr);
item.Id = doc.Id;
return item;
}
catch (Exception)
{
System.Diagnostics.Debug.WriteLine("EXCEPTION THROWN");
}
return default;
}
public static List<T> Convert<T>(this QuerySnapshot docs) where T : IIdentifiable
{
var list = new List<T>();
foreach (var doc in docs.Documents)
{
list.Add(doc.Convert<T>());
}
return list;
}
}
Please let me know what am I missing? I am stuck here

How to disable / invalidate JWT tokens?

This is how I create JWT tokens for my .NET Core API and it's working perfectly fine, but I'd like to implement the possibility to revoke, disable or invalidate JWT tokens when an HTTP request comes asking for it, with the token in the header.
I can think of a way where I would store the token in my database and have a boolean column indicating whether the token is active or not, but is there a way to do it without storing the token in the database at all?
public UserAuthenticationResponse CreateToken(UserAuthenticationRequest userAuth)
{
var user = // try to find user in database...
if (user == null)
{
return null;
}
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim("user_id", userAuth.Id),
}),
Expires = DateTime.UtcNow.AddMinutes(5),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var authenticatedUser = new UserAuthenticationResponse();
authenticatedUser.Id = user.Id;
authenticatedUser.Token = tokenHandler.WriteToken(token);
return authenticatedUser;
}
What I did in the end was create middleware which I put at the top of my pipeline. I held a List<string> that represented a list of blacklisted tokens (which I later clean up once they expire).
When I were validating the token myself, I was checking the BlacklistToken(token, timeout) method. If it returns true, I can blacklist the token and the next time the user tries to access something with that token, it won't let him do it. Then, sometime later, I call CleanupTokens(tokenProvider) which gets all tokens, checks if they're expired (thanks to the tokenProvider that gets the token expiration date) and if so, it removes them from the list.
public class TokenPair
{
[JsonProperty(PropertyName = "token")]
public string Token { get; set; }
[JsonProperty(PropertyName = "userId")]
public string UserID { get; set; }
}
public interface ITokenLocker
{
bool BlacklistToken(string token, int timeout);
void CleanupTokens(ITokenProvider tokenProvider);
bool IsBlacklisted(string token);
}
public class TokenLocker : ITokenLocker
{
private List<string> _blacklistedTokens;
public TokenLocker()
{
_blacklistedTokens = new List<string>();
}
public bool BlacklistToken(string token, int timeout)
{
lock (_blacklistedTokens)
{
if (!_blacklistedTokens.Any(x => x == token))
{
_blacklistedTokens.Add(token);
return true;
}
}
Thread.Sleep(timeout);
lock (_blacklistedTokens)
{
if (!_blacklistedTokens.Any(x => x == token))
{
_blacklistedTokens.Add(token);
return true;
}
else
return false;
}
}
public void CleanupTokens(ITokenProvider tokenProvider)
{
lock (_blacklistedTokens)
{
for (int i = 0; i < _blacklistedTokens.Count; i++)
{
var item = _blacklistedTokens[i];
DateTime expiration = tokenProvider.GetExpiration(item);
if (expiration < DateTime.UtcNow)
{
_blacklistedTokens.Remove(item);
i--;
}
}
}
}
public bool IsBlacklisted(string token)
{
return _blacklistedTokens.Any(tok => tok == token);
}
}
public class TokenMiddleware
{
private readonly RequestDelegate _next;
public TokenMiddleware(RequestDelegate next)
{
_next = next;
}
private string GetToken(HttpContext ctx)
{
ctx.Request.EnableBuffering();
using (var reader = new StreamReader(ctx.Request.Body, Encoding.UTF8, true, 1024, true))
{
var jsonBody = reader.ReadToEnd();
var body = JsonConvert.DeserializeObject<TokenPair>(jsonBody);
ctx.Request.Body.Position = 0;
if (body != null && body.Token != null)
{
return body.Token;
}
}
return string.Empty;
}
public async Task InvokeAsync(HttpContext context,
ITokenLocker tokenLocker
)
{
var ctx = context;
if (tokenLocker.IsBlacklisted(GetToken(ctx)))
{
int statusCode = (int)HttpStatusCode.Unauthorized;
ctx.Response.StatusCode = statusCode;
var response = FaziHttpResponse.Create(statusCode, "Unauthorized: Invalid / Expired token");
await context.Response.WriteAsync(JsonConvert.SerializeObject(response));
return;
}
await _next(context);
}
}

Issue getting NullReferenceException using Http with jwt Xamarin Forms Android

So my code works in PostMan querying the api to populate a listview locally in my Android app. But when I run it from within the app, I get NullReferenceException on the line "Items.Clear() in ShipViewModel.cs
I tried hardcoding the address rather than using my APIQueriable path, I tried generating new JWT, and I tried manually cleaning my /bin /obj folders to rule out code not compiling correctly.
ShipsViewModel.cs Xamarin.Forms/ViewModels
{
private GallogClient _gallogClient;
public ObservableCollection<ShipCatalog> Items { get; set; }
public Command LoadItemsCommand { get; }
public ShipsViewModel()
{
Title = "Ships";
_gallogClient = new GallogClient("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hcGkuZ2FsbG9nLmNvIiwiYXVkIjoiaHR0cDpcL1wvYXBpLmdhbGxvZy5jbyIsImlhdCI6MTM1Njk5OTUyNCwibmJmIjoxMzU3MDAwMDAwLCJkYXRhIjp7ImlkIjo1NywidXNlcm5hbWUiOiJQYXJhIiwiaGFuZGxlIjoiUGFyYSIsImVtYWlsIjoicGFyYWJvbGE5NDlAZ21haWwuY29tIn19.bRpI9hVy-Spky5pbZhJCkyN-MT9RA6ap_yD9ezRxCxo");
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand(), () => !IsBusy);
}
async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
try
{
LoadItemsCommand.ChangeCanExecute();
Items.Clear();
var items = await _gallogClient.GetItemsAsync<ShipList>();
foreach (var item in items.ships.ToList())
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
}
}
GallogClient.cs Gallog.API
{
internal readonly HttpClient Client = new HttpClient { BaseAddress = new Uri("https://api.gallog.co/api/") };
public string Jwt { get; set; }
public GallogClient()
{
}
public GallogClient(string jwt)
{
Jwt = jwt;
}
private StringContent JwtContent
{
get {
return new StringContent(JsonConvert.SerializeObject(new
{
jwt = Jwt
}), Encoding.UTF8, "application/json");
}
}
//...
public async Task<T> GetItemAsync<T>(string name) where T : ApiQueryable
{
return await PostAsync<T>($"{GetPath<T>()}/{name}");
}
public async Task<T> GetItemsAsync<T>() where T : ApiQueryable
{
return await PostAsync<T>($"{GetPath<T>()}");
}
internal string GetPath<T>()
{
if (typeof(T).GetCustomAttributes(
typeof(ApiPathAttribute), true
).FirstOrDefault() is ApiPathAttribute at)
{
return at.Path;
}
return null;
}
public async Task<T> PostAsync<T>(string path) where T : ApiQueryable
{
var response = await Client.PostAsync(path, JwtContent);
return JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
}
public async Task<T> PostAsync<T>(object body, string path) where T : ApiQueryable
{
var response = await Client.PostAsync(path,
new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"));
return JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
}
}
}
ShipList.cs Xamarin.Forms/Models
{
[ApiPath("ships")]
public class ShipList : ApiQueryable
{
public ShipCatalog[] ships { get; set; }
}
public class ShipCatalog
{
public int id { get; set; }
public string name { get; set; }
// ...etc etc
}
}
Items is null because you declared it but have never initialized it
public ShipsViewModel()
{
...
Items = new ObservableCollection<ShipCatalog>();
...
}

Error in Redis Connection in ASP.NET Core App Hosted on Azure

We are facing problems with Redis caching and it's causing crashes in our site.
The following is how we implemented it:
We used the following connection string:
"*******.redis.cache.windows.net:6380,password=*****=,ssl=True,abortConnect=False"
We created a service class:
using Microsoft.Extensions.Options;
using SarahahDataAccessLayer;
using StackExchange.Redis;
using System;
namespace Sarahah.Services
{
public class RedisService
{
private static Lazy<ConnectionMultiplexer> lazyConnection;
private readonly ApplicationSettings _settings;
public RedisService(IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
return ConnectionMultiplexer.Connect(_settings.RedisConnection);
});
}
public ConnectionMultiplexer Connection
{
get
{
return lazyConnection.Value;
}
}
}
}
Then in Startup.cs I use the following:
services.AddSingleton<RedisService>();
Then in controllers we use dependency injection and we assign to a multiplexer:
connectionMultiplexer = redisService.Connection;
This is how we get from the cache:
private async Task<string> GetFromCache(string key)
{
if (connectionMultiplexer.IsConnected)
{
var cache = connectionMultiplexer.GetDatabase();
return await cache.StringGetAsync(key);
}
else
{
return null;
}
}
This is how we delete:
private async Task DeleteFromCache(string subdomain)
{
if (connectionMultiplexer.IsConnected)
{
var cache = connectionMultiplexer.GetDatabase();
await cache.KeyDeleteAsync(subdomain).ConfigureAwait(false);
}
}
This is how we add:
{
if (connectionMultiplexer.IsConnected)
{
var cache = connectionMultiplexer.GetDatabase();
TimeSpan expiresIn;
// Search Cache
if (key.Contains("-"))
{
expiresIn = new TimeSpan(0, GetMessagesCacheExpiryMinutes, 0);
}
// User info cache
else
{
expiresIn = new TimeSpan(GetProfileCacheExpiryHours, 0, 0);
}
await cache.StringSetAsync(key, serializedData, expiresIn).ConfigureAwait(false);
}
However, we get the following error:
No connection is available to service this operation
Although we have a lot of users, we only see few connections in Azure portal:
Please note that we hosted the redis cache in the same region of the web app.
Your support is appreciated.
Each time your dependency injection calls instantiates the RedisService class, your code ends up assigning a new Lazy<ConnectionMultiplexer> to lazyConnection, thus resulting in a new connection as well as a connection leak as you are not calling Close() or Dispose() on the old lazyConnection.
Try changing your code like this:
In Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
.........<whatever you have here>
services.AddSingleton<RedisService>();
services.Configure<ApplicationSettings>(options => Configuration.GetSection("ApplicationSettings").Bind(options));
}
RedisService.cs
public class RedisService
{
private readonly ApplicationSettings _settings;
private static Lazy<ConnectionMultiplexer> lazyConnection;
static object connectLock = new object();
public RedisService(IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
if (lazyConnection == null)
{
lock (connectLock)
{
if (lazyConnection == null)
{
lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
return ConnectionMultiplexer.Connect(_settings.RedisConnection);
});
}
}
}
}
public static ConnectionMultiplexer Connection
{
get
{
return lazyConnection.Value;
}
}
}
ApplicationSettings.cs
public class ApplicationSettings
{
public string RedisConnection { get; set; }
}
appsettings.json
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"ApplicationSettings": {
"RedisConnection": "yourcachename.redis.cache.windows.net:6380,password=yourpassword,ssl=True,abortConnect=False,syncTimeout=4000"
}
}
HomeController.cs
public class HomeController : Controller
{
private RedisService redisService;
private ConnectionMultiplexer connectionMultiplexer;
public HomeController(IOptions<ApplicationSettings> settings)
{
redisService = new RedisService(settings);
connectionMultiplexer = RedisService.Connection;
}
public IActionResult Index()
{
AddToCache("foo1", "bar").GetAwaiter().GetResult();
return View();
}
private async Task<string> GetFromCache(string key)
{
if (connectionMultiplexer.IsConnected)
{
var cache = connectionMultiplexer.GetDatabase();
return await cache.StringGetAsync(key);
}
else
{
return null;
}
}
private async Task DeleteFromCache(string subdomain)
{
if (connectionMultiplexer.IsConnected)
{
var cache = connectionMultiplexer.GetDatabase();
await cache.KeyDeleteAsync(subdomain).ConfigureAwait(false);
}
}
private async Task AddToCache(string key, string serializedData)
{
var GetMessagesCacheExpiryMinutes = 5;
var GetProfileCacheExpiryHours = 1;
if (connectionMultiplexer.IsConnected)
{
var cache = connectionMultiplexer.GetDatabase();
TimeSpan expiresIn;
// Search Cache
if (key.Contains("-"))
{
expiresIn = new TimeSpan(0, GetMessagesCacheExpiryMinutes, 0);
}
// User info cache
else
{
expiresIn = new TimeSpan(GetProfileCacheExpiryHours, 0, 0);
}
await cache.StringSetAsync(key, serializedData, expiresIn).ConfigureAwait(false);
}
}

Resources