Update the SQLite Database in C# - c#

In my Xamarin app, there is a Picker which gets the Bank List from Database, and this DB gets the Bank details through an API.
Everything works fine, but now, I want to update the DB, if the new Banks are added in API, I want to add them in the existing database.
I wrote this small code, but the problem is, it first output all the existing records from the database and then print the new records, many times as the total amount of records e.g. (if there are ten records, it will print every record ten times).
ViewModel.cs
// Load all Banks into a List
var getBanks = await App.Database.GetBankAsync();
// Update the Bank Details where new Banks doesn't exist in the Database
List<Bank> updateBanks = new List<Bank>();
foreach (Banks i in obj.banks)
{
foreach (var record in getBanks.Where(c => c.Name != i.alias.name))
{
updateBanks.Add(new Bank { Name = i.alias.name, Status = "Not Registered" });
}
}
foreach (var bank in updateBanks)
{
await App.Database.SaveBankAsync(bank);
}
// Load all Banks into a List
var getBanksUpdated = await App.Database.GetBankAsync();
// Load the Banks whose Status is "Not Registered"
foreach (var record in getBanks.Where(c => c.Status == "Not Registered")
{
_bankList.Add(record);
}
public class Alias
{
public string name { get; set; }
}
public class Banks
{
public Alias alias { get; set; }
public string id { get; set; }
}
public class RootObject
{
public List<Banks> banks { get; set; }
}
Data getting through API
{
success: true,
banks: [
{
alias: {
name: "Bank 1",
Address: "Earth"
},
endpoint: {
uri: "http://127.0.0.1:0000"
},
state: "Registered",
createdAtUtc: "2021-01-19T13:55:12.2318662",
updatedAtUtc: "2021-01-19T13:55:14.9944042"
},
]
}
Code to get the data from API
HttpClient httpClient = new HttpClient(new NativeMessageHandler());
Uri getDataBanks = new Uri(string.Format("http://127.0.0.1:0000/Banks"));
HttpResponseMessage restponseBanks = await httpClient.GetAsync(getDataBanks);
string contentBanks = await restponseBanks.Content.ReadAsStringAsync();
RootObject obj = JsonConvert.DeserializeObject<RootObject>(contentBanks);

// get the names of all existing banks
List<string> names= obj.banks.Select(u => u.Name).ToList();
// get the names of all banks taken from the API
List<string> names_new= getBanks.Select(u => u.Name).ToList();
foreach (var record in names_new)
{
//check if the bank is exists or not
if(! names.contains(record)
{
updateBanks.Add(new Bank { Name = record.alias.name, Status = "Not Registered" });
}
}

I'm not aware of your data, but you you have too foreach loops and you are checking the whole collection in every loop. SO, it's possible, that your condition catches every account on every loop.

Related

Handling Stripes payment_intent.succeeded Webhook if it competes with a post back from the client to create an entity in the DB

I need some advice on the workflow for my application when charging a credit card using Stripe.
Scenario 1 - I don't use any webhook for payment_intent.succeeded so when I call stripe.confirmCardPayment on the client side in Javascript
and receive the paymentIntent back I then post to my server and create an entry in a "Payment" table with some method called "SavePayment()", where all the details (card id, exp month, amount, etc) will be stored. Once I save to the DB, I can return the details to the client (points earned, payment successful message, etc). Then we're done!
Scenario 2 Client(user) closes the browser after Stripe is called to charge the card, but before it can post back to my server to add the "Payment" entity. So now I use a webhook for payment_intent.succeeded as others have recommended doing this for redundancy.
Problem -
Because the webhook is triggered immediately, after the card is charged by Stripe, my server could potentially receive two different entry points (client posting back to server to save a payment and Stripes webhook trigger event), to create a "Payment" entity in my DB.
Now this isn't a huge problem, because both entry points can query for the "Payment" entity based on it's unique identifier (PaymentIntentId) to see if it exists in the DB.
But let's say both entry points query and return a null, so now both entry points go ahead and create a new "Payment" entity and attempt to save it in the DB. One will succeed and one will now fail, frequently creating a unique identifier constraint exception being thrown by SQL Server.
Solution? - This doesn't seem like the ideal workflow/scenario, where multiple exceptions could be frequently thrown, for creating an entity in my DB. Is there a better workflow for this, or am I stuck implementing it this way?
Here is some of my code/suedo code to look at.
public class Payment : BaseEntity
{
public string PaymentIntentId { get; set; }
public int Amount { get; set; }
public string Currency { get; set; }
public string CardBrand { get; set; }
public string CardExpMonth { get; set; }
public string CardExpYear { get; set; }
public int CardFingerPrint { get; set; }
public string CardLastFour { get; set; }
public PaymentStatus Status { get; set; }
public int StripeFee { get; set; }
public int PointsAwarded { get; set; }
public int PointsBefore { get; set; }
public int PointsAfter { get; set; }
public string StripeCustomer { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
Here is some code from the client to call stripe and then post to my server
// submit button is pressed
// do some work here then call Stripe
from(this.stripe.confirmCardPayment(this.paymentIntent.clientSecret, data)).subscribe((result: any) => {
if (result.paymentIntent) {
let payment = {
paymentIntentId: result.paymentIntent.id,
amount: result.paymentIntent.amount,
currency: result.paymentIntent.currency,
// fill in other fields
};
this.accountService.savePayment(payment).subscribe(response => {
if (response.status === 'Success') {
// do some stuff here
this.alertService.success("You're purchase was successful");
this.router.navigateByUrl('/somepage');
}
if (response.status === 'Failed') {
this.alertService.danger("Failed to process card");
}
}, error => {
console.log(error);
this.alertService.danger("Oh no! Something happened, please contact the help desk.");
}).add(() => {
this.loadingPayment = false;
});
} else {
this.loadingPayment = false;
this.alertService.danger(result.error.message);
}
});
Here is the server controller to save a "Payment" entity
[HttpPost("savepayment")]
public async Task<ActionResult> SavePayment(StripePaymentDto paymentDto)
{
var userFromRepo = await _userManager.FindByEmailFromClaimsPrinciple(HttpContext.User);
if (userFromRepo == null)
return Unauthorized(new ApiResponse(401));
// this calls the Stripe API to get the PaymentIntent (just incase the client changed it)
var paymentIntent = await _paymentService.RetrievePaymentIntent(paymentDto.PaymentIntentId);
if (paymentIntent == null) return BadRequest(new ApiResponse(400, "Problem Retrieving Payment Intent"));
var payment = _mapper.Map<StripePaymentDto, StripePayment>(paymentDto);
payment.UserId = userFromRepo.Id;
if (paymentIntent.Status == "succeeded") {
// fill in all the necessary fields
// left out for brevity
} else if (paymentIntent.Status == "requires_payment_method") {
payment.Status = PaymentStatus.Failed;
_logger.LogInformation("Payment Intent is not successful. Status: " + paymentIntent.Status + " PaymentIntentId: " + paymentIntent.PaymentIntentId);
// send payment failure email
} else {
// don't know if this will be needed
payment.Status = PaymentStatus.Pending;
}
_unitOfWork.Repository<StripePayment>().Add(payment);
var success = await _unitOfWork.Complete();
if (success > 0) {
if (payment.Status == PaymentStatus.Success) {
// send email
}
return Ok(_mapper.Map<StripePayment, StripePaymentDto>(payment));
}
return BadRequest(new ApiResponse(400, "Failed to save payment"));
}
Here is the Stripe webhook
[HttpPost("webhook")]
public async Task<ActionResult> StripeWebhook()
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
// if this doesn't match we get an exception (sig with whSec)
var stripeEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], _whSecret);
PaymentIntent intent;
switch (stripeEvent.Type)
{
case "payment_intent.succeeded":
intent = (PaymentIntent)stripeEvent.Data.Object;
_logger.LogInformation("Payment Succeeded: ", intent.Id);
this.ProcessSuccess(intent);
// order = await _paymentService.UpdateOrderPaymentSucceeded(intent.Id);
// _logger.LogInformation("Order updated to payment received: ", order.Id);
break;
case "payment_intent.payment_failed":
intent = (PaymentIntent)stripeEvent.Data.Object;
_logger.LogInformation("Payment Failed: ", intent.Id);
// _logger.LogInformation("Payment Failed: ", order.Id);
break;
}
return new EmptyResult();
}
private async void ProcessSuccess(PaymentIntent paymentIntent) {
var spec = new PaymentsWithTypeSpecification(paymentIntent.Id);
var paymentFromRepo = await _unitOfWork.Repository<StripePayment>().GetEntityWithSpec(spec);
if (paymentFromRepo == null) {
// create one and add it
var payment = _mapper.Map<PaymentIntent, StripePayment>(paymentIntent);
payment.UserId = Convert.ToInt32(paymentIntent.Metadata["userid"]);
}
// finish work here and then save to DB
}
Great point below. I appreciate your goal. After some thought, my final analysis is that: in order to prevent duplicate records in the database from multiple sources, a unique index should be used. (which you are using)
Now by using a unique index the database will throw an exception which the code will have to handle gracefully. Hence the answer is that you are doing it the way I and others have done so for some years. Unfortunately, I'm not aware of any other means of avoiding an exception once you hit the database tier.
Great question even if the answer is not the one you were hoping for.

How can i update document in mongo db using .net(c#) driver without using Builders?

I am using MongoDb .net driver in which i have to update document based on certain condition.
Here is how my find query looks like in c# mongo driver
DbService.conversations.Find (new BsonDocument ("_id", new ObjectId ("obje-id-here"))).FirstOrDefault ();
How can i update specific field of document based on certain _id using Mongodb .net driver without using Builders?
**Note : **
I have tried this update query
var updateResultFromQuery = await DbService.conversations.UpdateOneAsync(Builders<RawBsonDocument>.Filter.Eq("_id", "5e01a89e5f317324780b7f83"),Builders<RawBsonDocument>.Update.Set("visitorName", "Guest41815"));
Console.WriteLine("after update response --- "+updateResultFromQuery.ToJson());
But it's not updating the value even i am receiving update response like this
{ "_t" : "Acknowledged" }
You could simply ReplaceOne object instead of updating it , that way you wont be forced to use builders but you will be doing a find and replace instead. 2 database operations instead of one. You can then update your object in memory instead.
Collection.ReplaceOne(filter, replacement, new UpdateOptions() { IsUpsert = false });
here's an example of create/update/find as requested:
using MongoDB.Entities; //Install-Package MongoDB.Entities
using MongoDB.Entities.Core;
using System;
using System.Linq;
namespace StackOverflow
{
public class Customer : Entity
{
public string Name { get; set; }
public Agent Agent { get; set; }
}
public class Agent
{
public string Name { get; set; }
public string Email { get; set; }
}
public class Program
{
private static void Main()
{
new DB("test", "localhost");
// create a customer with embedded agent
var customer = new Customer
{
Name = "Customer A",
Agent = new Agent { Name = "Agent Uno", Email = "uno#youknow.com" }
};
customer.Save();
// update customer name
DB.Update<Customer>()
.Match(c =>
c.ID == customer.ID &&
c.Agent.Email == "uno#youknow.com")
.Modify(c =>
c.Name, "Updated Customer")
.Execute();
// find updated customer
var cst = DB.Find<Customer>()
.Match(customer.ID)
.Execute()
.Single();
Console.WriteLine($"Customer Name: {cst.Name}");
Console.Read();
}
}
}
I achieved updating my document without using any third party library using these options
var filter = new BsonDocument(new Dictionary<string, dynamic> () {
{
"_id", new BsonObjectId("object-id-here")
},
{
"assigned_to.email" , agentEmail
}
});
var updateDoc = new BsonDocument(new Dictionary<string, dynamic> () {
{
"$set", new BsonDocument("assigned_to.$.avgResponseTime", Convert.ToDouble(obj.agentChatAvgResponseTime))
}
});
var updateQueryResult = DbService.conversations.UpdateOne(filter, updateDoc, new UpdateOptions {IsUpsert = false });

Create and Read List<> from cookies

I'm trying to display recently viewed products and so far I have already done that. I have a Product table that has many products stored. I have HomeController which has an Action method of Details() that display product details.
I have wrote AddRecentProduct method which stores Recently Viewed Products (10) in the Session
Now I want to store these recent viewed product list into cookies for atleast 30days on visitors computer, because session expires. Just like Imdb Recently Viewed.
Also If I create another table in my database RecentlyViewed with columns rv_id, userId, productId how will I save recentlyViewedList data in this ? The userId column will hold loggedIn user's id but what if a user is a Guest (not registered) what's the solution then ? Do I need to use GUID then ?
RecentProduct.cs
public class RecentProduct
{
public int ProductId { get; set; }
public string ProdutName { get; set; }
public string ImageUrl { get; set; }
public DateTime LastVisited { get; set; }
}
Controller
public void AddRecentProduct(List<RecentProduct> list, int id, string name, int maxItems)
{
var item = recentProductList.FirstOrDefault(t => t.ProductId == id);
if (item == null)
{
list.Add(new RecentProduct
{
ProductId = id,
ProdutName = name,
LastVisited = DateTime.Now,
});
}
while (list.Count > maxItems)
{
list.RemoveAt(0);
}
}
public ActionResult Details(int? id)
{
if (id == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
Product product = db.Products.Find(id);
if (product == null)
return HttpNotFound();
var list = Session["RecentProductList"] as List<RecentProduct>;
if (list == null)
{
list = new List<RecentProduct>();
Session["RecentProductList"] = list;
}
AddRecentProduct(list, id.Value, product.Name, 10);
ViewData["RecentProductList"] = list;
return View(product);
}
ProductDetails View Page
<div class="col-sm-9">
#{
var recentProductList = ViewData["RecentProductList"] as List<Project.Models.RecentProduct>;
}
#foreach (var recentProduct in recentProductList)
{
<p>#recentProduct.ProdutName (id: #recentProduct.ProductId) </p>
}
</div>
I am getting the desired result with session, Now I want to do this same with cookies.
This is what I'm trying Creating cookie:
List<RecentProduct> yourList = new List<RecentProduct>();
RecentProduct rc = new RecentProduct();
rc.ProdutName = product.Name;
rc.ProductId = product.ProductId;
rc.ImageUrl = product.ImagePath;
rc.LastVisited = DateTime.Now;
yourList.Add(rc);
var yourListString = String.Join(",", yourList);
// Create a cookie
HttpCookie yourListCookie = new HttpCookie("YourList", yourListString);
// The cookie will exist for 7 days
yourListCookie.Expires = DateTime.Now.AddDays(7);
// Write the Cookie to your Response
Response.Cookies.Add(yourListCookie);
and In ProductDetails View Page reading cookies like this:
#if (Request.Cookies["YourList"] != null)
{
// Your cookie exists - grab your value and create your List
List<string> yourList = Request.Cookies["YourList"].Value.Split(',').Select(x => Convert.ToString(x)).ToList();
// Use your list here
<p>#yourList</p>
}
I'm not getting any result. How can I read cookie and values ?
the best solution for you is using Html5 Web Storage. it lets you store up to 5mb in local browser. you can only read and write via javascript.
example:
$('#btnLocalStorage').click(function()
{
var txtName = $("#txtName").val();
//set item
localStorage.setItem("EmpName", txtName);
});
$('#btnLocalStorageRemove').click(function()
{
//remove item
localStorage.removeItem("EmpName");
});
$('#btnLocalStorageClear').click(function()
{
//clear Local Storage
localStorage.clear();
});
$('#btnLocalStorageLength').click(function()
{
var localStoragelength = localStorage.length;
alert("The length of Local storage is " + localStoragelength);
});
By the way it has no expire time and u dont need guid.
If you are using cookies, why do you need RecentlyViewed table then? Storing recent product ids in cookie will work for both logged-in and anonymous user.

Generating Sample Data

I am working on a school project using ASP.NET MCV4, EF6, Code-First models. Now, I am wondering how should I fill the database with sample data. I checked migrations, but I don't want to mess with the db structure. I just want to insert data, I tried this:
namespace Autokereskedes.Models
{
public class _SampleData : DropCreateDatabaseIfModelChanges<AutoDb>
{
protected override void Seed(AutoDb context)
{
new Autokereskedes.Models.SampleData.Users().List().ForEach(u=>context.Users.Add(u));
new Autokereskedes.Models.SampleData.Cars().List().ForEach(c => context.Cars.Add(c));
}
}
}
namespace Autokereskedes.Models.SampleData
{
public class Users
{
public List<User> List()
{
var crypto = new SimpleCrypto.PBKDF2();
var salt = Autokereskedes.Controllers.AccountController.PasswordSalt;
return new List<User>
{
new User {
UserId = Guid.NewGuid(),
Email = "admin#autoker.hu",
Password = crypto.Compute("admin",salt),
Phone = "+36 20 XXX YYZZ",
Banned = false,
Country = "Hungary",
City = "Szeged",
Street = "DivisonByZero street 1/0",
ZipCode = 1100,
RegistrationDate = DateTime.Now
},
new User {
UserId = Guid.NewGuid(),
Email = "user#autoker.hu",
Password = crypto.Compute("user",salt),
Phone = "+36 20 XXX YYZZ",
Banned = false,
Country = "Hungary",
City = "Szeged",
Street = "DivisonByZero street 2/0",
ZipCode = 1100,
RegistrationDate = DateTime.Now
}
};
}
}
}
It is working, I thought. But how should I insert data that has foreign keys? I saw a tutorial where they used a single file for all the List<>-s and in the foreign key field used something like this: Genre = genres.Single(g => g.Name == "Jazz"). I can't really copy that now.
namespace Autokereskedes.Models.SampleData
{
public class Cars
{
public List<Car> List()
{
return new List<Car>
{
new Car {
CarId = Guid.NewGuid(),
DepoId = now what
},
new Car {
}
};
}
}
}
When you seed the data, you need to account for the foreign key relationships... eventually.
return new List<Car>
{
new Car {
CarId = Guid.NewGuid(),
DepoId = now what // it depends, is this a required relationship?
},
new Car {
}
};
If DepoId is an optional relationship, you can just wait until you have a Depo & DepoId before setting up this property. Otherwise if it is required, you need to set it up before you insert it into the context.
protected override void Seed(AutoDb context)
{
new Autokereskedes.Models.SampleData.Users().List()
.ForEach(u=>context.Users.Add(u));
var cars = new Autokereskedes.Models.SampleData.Cars().List();
var depos = new Autokereskedes.Models.SampleData.Depos().List();
foreach (var car in cars)
{
car.DepoId = depos.FirstOrDefault(x => x.DepoId == ...?);
context.Cars.Add(car);
}
}
I suppose the question is, how do you decide which depo should be assigned to each car?
Another way to do it, since your Depo entities are a dependency and you need to resolve them first, would be to pass your depos to your cars list method:
var depos = new Autokereskedes.Models.SampleData.Depos().List();
depos.ForEach(d => context.Depos.Add(d));
//context.SaveChanges(); no need for this since your id's are Guid's
var cars = new Autokereskedes.Models.SampleData.Cars().List(depos);
public List<Car> List(IEnumerable<Depo> depos)
{
// now you have depos to look for id's in
return new List<Car>
{
new Car {
CarId = Guid.NewGuid(),
DepoId = depos.SingleOrDefault(x => [your predicate]),
},
new Car {
}
};
}
The process is called "seeding" the data.
You create an initializer object and override the Seed method:
public class MyDataContextDbInitializer : DropCreateDatabaseIfModelChanges<MyDataContext>
{
protected override void Seed(MagazineContext context)
{
context.Customers.Add(new Customer() { CustomerName = "Test Customer" });
}
}
Here is an article describing how to do it:
http://www.codeproject.com/Articles/315708/Entity-Framework-Code-First-Data-Initializers
I made an Nuget package, so you can generate random contact data (firstname, lastname, city, zipcode, birthdates, accountnumbers, etc)
In this version you can export to CSV, XML, JSON and SQL.
Open Visual Studio, Manage Nuget Packages
Search online for:
DSharp FrameWork: ContactGenerator
Any suggestions for extending functionality are welcome.

One Table, Several Views, one C# Class

I have a single c# class defined as
class HighScore
{
public int Id { get; set; }
[DataMember(Name = "PlayerName")]
public string PlayerName { get; set; }
[DataMember(Name = "PlayerCountry")]
public int PlayerCountry { get; set; }
[DataMember(Name = "PlayerTime")]
public double PlayerTime { get; set; }
[DataMember(Name = "PlayerBadge")]
public int PlayerBadge { get; set; }
}
And a table in my Azure mobile services SQL database that contains several records of this type. I have a number of views
select * from tellingthetime.HighScore where PlayerBadge=0
where the PlayerBadge is a number from 0 to 4. I also have a number of read scripts added to my Mobile Service that query the view and return the appropriate rows.
function read(query, user, request) {
mssql.query("select * from OneStarBadgeLeaderBoard", {
success: function(results) {
console.log(results);
request.respond(statusCodes.OK, results);
}
});
}
The above script is called OneStarBadgeLeaderBoard, but my class is called HighScore. The code below I call to Get the underlying table.
private IMobileServiceTable<HighScore> HighScoreTable = App.MobileService.GetTable<HighScore>();
Without creating a different class name, the defintions are all the same as the SQL returned data is the same, for each read script, how do I make this work so I can call any read script, which queries the appropriate view to retrieve the values I need?
Hope that make sense.
Many thanks,
Jason.
P.S. Of course, I could read the entire table and query it with LINQ on the client, but that will increase the amount of data to download.
Got this from Josh Twist
function read(query, user, request) {
var dispatch = {
op1 : operation1,
op2 : operation2,
}
if (request.parameters.operation && dispatch.hasOwnProperty(request.parameters.operation)) {
dispatch[request.parameters.operation](query, user, request);
return;
}
else
{
// default path for execution
request.execute();
}
}
function operation1(query, user, request) {
request.respond(200, "this result is from operation1");
}
function operation2(query, user, request) {
request.respond(200, "this result is from operation2");
}
http://www.thejoyofcode.com/Dispatching_to_different_query_functions_in_Mobile_Services.aspx
Also this code sends a filtered OData request that only retrieves the required records. Got it from the ToDo list Azure Mobile Service Tutorials
private async void RefreshTodoItems()
{
// This code refreshes the entries in the list view by querying the TodoItems table.
// The query excludes completed TodoItems
var results = await todoTable
.Where(todoItem => todoItem.Complete == false)
.ToListAsync();
items = new ObservableCollection<TodoItem>(results);
ListItems.ItemsSource = items;
}

Categories

Resources