Permalänk
Medlem

designpatterns

hej jag har en uppgift att implementera minst 3 design patterns i c# jag har använt repository hittils och jag behöver ha hjälp att implementra singelton i en kod men jag behöver ha hjälp med att hitta platsen i koden för att göra det.
[code]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}

public ActionResult Parametrar()
{
var logger = new Logger();
logger.LogAction(Logger.Actions.ParametrarPage, "");

var model = new Parametrar();
model.CurrentRiksbankenStibor = InterestService.GetRiksbankensBaseRate();
return View(model);
}

[HttpGet]
public ActionResult ListCustomers()//...............................................
{
var model = new List<Customer>();
var logger = new Logger();
logger.LogAction(Logger.Actions.ListCustomersPage, "");

ICustomerRepository repository = GetRepository();
model = repository.GetCustomers();

return View(model);
}
private ICustomerRepository GetRepository()
{
return new FileCutomerRepository();
}

[HttpGet]
public ActionResult Customer(string PersonNummer)
{
var logger = new Logger();
logger.LogAction(Logger.Actions.ViewCustomerPage, PersonNummer);

var customer = FindCustomer(PersonNummer);
return View(customer);
}

[HttpGet]
public ActionResult Ringinstruktioner()
{
var logger = new Logger();
logger.LogAction(Logger.Actions.CallReceived, " some more useless info...");
var model = new CallInstructions();
return View(model);
}

void SaveToFile(Customer c)
{
string databas = Server.MapPath("~/customers.txt");
var allLines = System.IO.File.ReadAllLines(databas).ToList();
foreach (var line in allLines)
{
string[] parts = line.Split(';');
if (parts.Length < 1) continue;
if (parts[0] == c.PersonNummer)
return;
}
allLines.Add(c.PersonNummer);
System.IO.File.WriteAllLines(databas,allLines);
}

void SaveLoanToFile(Customer c, Loan l)
{
string databas = Server.MapPath("~/loans.txt");
var allLines = System.IO.File.ReadAllLines(databas).ToList();
foreach (var line in allLines)
{
string[] parts = line.Split(';');
if (parts.Length < 1) continue;
if (parts[0] == c.PersonNummer && parts[1] == l.LoanNo)
return;
}
allLines.Add($"{c.PersonNummer};{l.LoanNo};{l.Belopp};{l.FromWhen.ToString("yyyy-MM-dd")};{l.InterestRate}");
System.IO.File.WriteAllLines(databas, allLines);
}

[HttpPost]
public ActionResult NewLoan(CallInstructions model)
{
var logger = new Logger();

var c = FindCustomer(model.Personnummer);
if (c == null)
{
c = new Customer { PersonNummer = model.Personnummer };
SaveToFile(c);
logger.LogAction(Logger.Actions.CreatingCustomer, model.Personnummer);
SendEmailToBoss("New customer!",model.Personnummer);
}

var loan = new Loan
{
LoanNo = DateTime.Now.Ticks.ToString(),
Belopp = model.HowMuchDoYouNeed,
FromWhen = DateTime.Now,
InterestRate = model.RateWeCanOffer
};

c.Loans.Add(loan);
SaveLoanToFile(c, loan);
SendEmailToBoss("New loan!", model.Personnummer + " " + loan.LoanNo);
ReportNewLoanToFinansInspektionen(model.Personnummer, loan);

logger.LogAction(Logger.Actions.CreatingLoan, $"{model.Personnummer} {loan.LoanNo} {loan.Belopp}");

return View(loan);
}

void SendEmailToBoss(string subject, string message)
{
var mailer = new Mailer();
mailer.SendMail("harry@hederligeharry.se", subject, message);
}

void ReportNewLoanToFinansInspektionen(string personNummer, Loan loan)
{
var report = new FinansInspektionsRapportering.Report(FinansInspektionsRapportering.Report.ReportType.Loan,
personNummer, loan.LoanNo, 0, loan.Belopp, 0);
report.Send();
}

[HttpPost]
public ActionResult Ringinstruktioner(CallInstructions model)
{
var c = FindCustomer(model.Personnummer);
model.Result = true;
if (c == null)
model.Customer = c;

int age = GetAge(model.Personnummer);
decimal baseRate = InterestService.GetRiksbankensBaseRate();

if (c == null)
{
if (age < 18)
model.RateWeCanOffer = 30.22m + baseRate;
else if (age < 35)
model.RateWeCanOffer = 32.18m + baseRate;
else if (age < 65)
model.RateWeCanOffer = 22.30m + baseRate;
else
model.RateWeCanOffer = 45.30m + baseRate;
}
else
{
if (age < 18)
model.RateWeCanOffer = 29.32m + baseRate;
else if (age < 35)
model.RateWeCanOffer = 31.38m + baseRate;
else if (age < 65)
model.RateWeCanOffer = 21.20m + baseRate;
else
model.RateWeCanOffer = 41.12m + baseRate;

if(c.HasEverBeenLatePaying)
{
model.RateWeCanOffer += 10.0m;
}

}

return View(model);
}

int GetAge(string personnummer)
{
if (personnummer.Length == 10) //8101011234
return DateTime.Now.Year - 1900 - Convert.ToInt32(personnummer.Substring(0,2));

if (personnummer.Length == 12 && !personnummer.Contains("-")) //198101011234
return DateTime.Now.Year - Convert.ToInt32(personnummer.Substring(0, 4));

if (personnummer.Length == 11) //810101-1234
return DateTime.Now.Year - 1900 - Convert.ToInt32(personnummer.Substring(0, 2));

if (personnummer.Length == 13 ) //19810101-1234
return DateTime.Now.Year - Convert.ToInt32(personnummer.Substring(0, 4));

//Fake if not correct
return 50;
}

public void SetInvoicesForCustomer(Customer customer)
{
string databas = Server.MapPath("~/invoices.txt");
foreach (var line in System.IO.File.ReadAllLines(databas))
{
string[] parts = line.Split(';');
if (parts.Length < 2) continue;
var loan = customer.Loans.FirstOrDefault(r => r.LoanNo == parts[0]);
if (loan == null) continue;
var invoice = new Invoice
{
InvoiceNo = Convert.ToInt32(parts[1]),
Belopp = Convert.ToInt32(parts[2]),
InvoiceDate = DateTime.ParseExact(parts[3], "yyyy-MM-dd", CultureInfo.InvariantCulture),
DueDate = DateTime.ParseExact(parts[3], "yyyy-MM-dd", CultureInfo.InvariantCulture),
};
loan.Invoices.Add(invoice);
}

}

public void SetPaymentsForCustomer(Customer customer)
{
string databas = Server.MapPath("~/payments.txt");
foreach (var line in System.IO.File.ReadAllLines(databas))
{
string[] parts = line.Split(';');
if (parts.Length < 2) continue;
var invoice = customer.Loans.SelectMany(r => r.Invoices).FirstOrDefault(i => i.InvoiceNo == Convert.ToInt32(parts[0]));
if (invoice == null) continue;
var payment = new Payment
{
Belopp = Convert.ToInt32(parts[1]),
PaymentDate = DateTime.ParseExact(parts[2], "yyyy-MM-dd", CultureInfo.InvariantCulture),
BankPaymentReference = parts[3],
};
invoice.Payments.Add(payment);
}

}

public Customer FindCustomer(string personnummer)
{
Customer customer = null;
string databas = Server.MapPath("~/customers.txt");
foreach(var line in System.IO.File.ReadAllLines(databas))
{
string[] parts = line.Split(';');
if (parts.Length < 1) continue;
if (parts[0] == personnummer)
if(customer == null)
customer = new Customer { PersonNummer = personnummer };
}
if (customer == null) return null;
SetLoansForCustomer(customer);
SetInvoicesForCustomer(customer);
SetPaymentsForCustomer(customer);
return customer;
}

private void SetLoansForCustomer(Customer customer)
{
throw new NotImplementedException();
}

public ActionResult GenerateFakeData(int antal)
{
var rnd = new Random();
for(int i =0;i<antal;i++)
{
var persnr = rnd.Next(1934, 1999).ToString() +
rnd.Next(1, 12).ToString("00") +
rnd.Next(1, 28).ToString("00") +
rnd.Next(1000, 9999);

var c = FindCustomer(persnr);
if (c != null) continue;
c = new Customer { PersonNummer = persnr };
SaveToFile(c);

for(int l=0; l <= rnd.Next(1,7);l++ )
{
var loan = new Loan
{
LoanNo = DateTime.Now.AddDays(-rnd.Next(10,2000)).Ticks.ToString(),
Belopp = rnd.Next(3,200) * 100,
FromWhen = DateTime.Now.AddDays(-rnd.Next(10, 2000)),
InterestRate = Convert.ToDecimal(rnd.NextDouble() * (45 - 20) + 20)
};
SaveLoanToFile(c, loan);

}

}
return Content("Done");
}

}
}

[code]

Permalänk
Medlem

Mycket logik ligger controllern, detta bör flyttas ut i egna klasser. Ett bra ställe för singleton är då man ska skriva till filer, så att inte
flera trådar skriver över varandras saker i filerna. Så flytta det som skriver till filer till en egen klass och gör den till en singleton.

Edit:
var logger = new Logger(); Detta skapar du upp i alla metoder och den skriver till samma fil antagligen, det kan resultera i att flera trådar skriver till samma loggfil samtidigt, så att loggen kan bli korrupt. Loggning bör vara en singleton och sättas upp med DI.

Permalänk
Medlem

en singleton i detta context är t.ex. en Static class DoSomeThingClass....

Då Static garanterar att det bara är 1 instans av den koden/klassen som finns och körs.

Vilka metoder du lägger där i för skolexemplet är kanske inte så viktigt - utility funktioner brukar passa, så som t.ex. loggning, eller för den delen, fil-operationer

// LZ

Permalänk
Medlem

Kan bara Java spring och Node men i Spring om man inte säger annat så blir ens dependency injectade instanser automatiskt singletons, men det kanske inte räknas som att du har implementerat det i o f s.

Permalänk
Medlem
Skrivet av zaibuf:

Mycket logik ligger controllern, detta bör flyttas ut i egna klasser. Ett bra ställe för singleton är då man ska skriva till filer, så att inte
flera trådar skriver över varandras saker i filerna. Så flytta det som skriver till filer till en egen klass och gör den till en singleton.

Edit:
var logger = new Logger(); Detta skapar du upp i alla metoder och den skriver till samma fil antagligen, det kan resultera i att flera trådar skriver till samma loggfil samtidigt, så att loggen kan bli korrupt. Loggning bör vara en singleton och sättas upp med DI.

HEJ @zaibuf det har koden är gjord för vi ska försöka träna på patterns och det finns en class Logger i projektet som ser ut så här:
[code]
public class Logger
{
public enum Actions{
CallReceived,
ViewCustomerPage,
ListCustomersPage,
ParametrarPage,
CreatingCustomer,
CreatingLoan

};
public void LogAction(Actions action, string message)
{
System.IO.File.AppendAllText(HttpContext.Current.Server.MapPath("~/log.txt"), $"{action.ToString()} - {DateTime.Now.ToString("yyyy-MM-dd HH:mm:SS")} {message}\n");
}
}

[code]
ska jag skapa en singelton class och flytta alla metoder som skriver till samma fil?men vad vinner då på det om de fotfarande skriver till samma fil o kanske samtidigt (sorry varje gång jag hinner lära mig någonting bra så kommer nästa grej direkt)

Permalänk
Medlem
Skrivet av FadiAb:

HEJ @zaibuf det har koden är gjord för vi ska försöka träna på patterns och det finns en class Logger i projektet som ser ut så här:
[code]
public class Logger
{
public enum Actions{
CallReceived,
ViewCustomerPage,
ListCustomersPage,
ParametrarPage,
CreatingCustomer,
CreatingLoan

};
public void LogAction(Actions action, string message)
{
System.IO.File.AppendAllText(HttpContext.Current.Server.MapPath("~/log.txt"), $"{action.ToString()} - {DateTime.Now.ToString("yyyy-MM-dd HH:mm:SS")} {message}\n");
}
}

[code]
ska jag skapa en singelton class och flytta alla metoder som skriver till samma fil?men vad vinner då på det om de fotfarande skriver till samma fil o kanske samtidigt (sorry varje gång jag hinner lära mig någonting bra så kommer nästa grej direkt)

I alla moderna applikationer använder man dependency injection, detta är till och med inbyggt i .NET Core. Så egentligen finns det knappt någon användning av just singleton-pattern som lärs ut i design patterns. Men i utbildningssyfte så antar jag att det är det man ska implementera här. Annars registrerar du det i Startup containern som en Singleton.

Skillnaden är att om du gör var logger = new Logger(); i alla dina actionmetoder. Om du har 30 användare inne samtidigt i din webbapplikation och alla anropar dessa olika endpoints frekvent, så betyder det att du skapar upp flera instanser av loggningen och alla kommer försöka skriva samtidigt till filen. Om filen är låst då den andra instancen försöker komma åt och skriva kommer du få exceptions.

Har du istället detta som en singleton så finns det endast en instance av Logger i applikationen och alla trådar som skriver loggar kommer använda samma.

Permalänk
Medlem
Skrivet av FadiAb:

HEJ @zaibuf det har koden är gjord för vi ska försöka träna på patterns och det finns en class Logger i projektet som ser ut så här:
[code]
public class Logger
{
public enum Actions{
CallReceived,
ViewCustomerPage,
ListCustomersPage,
ParametrarPage,
CreatingCustomer,
CreatingLoan

};
public void LogAction(Actions action, string message)
{
System.IO.File.AppendAllText(HttpContext.Current.Server.MapPath("~/log.txt"), $"{action.ToString()} - {DateTime.Now.ToString("yyyy-MM-dd HH:mm:SS")} {message}\n");
}
}

[code]
ska jag skapa en singelton class och flytta alla metoder som skriver till samma fil?men vad vinner då på det om de fotfarande skriver till samma fil o kanske samtidigt (sorry varje gång jag hinner lära mig någonting bra så kommer nästa grej direkt)

Den avslutande kodtaggen behöver se ut såhär [/code] för att koden skall formatteras

Permalänk
Skrivet av Xenofonus:

Kan bara Java spring och Node men i Spring om man inte säger annat så blir ens dependency injectade instanser automatiskt singletons, men det kanske inte räknas som att du har implementerat det i o f s.

Spring kommer bara skapa en instans av en böna om inte annat sägs så bönan kommer vara "Singleton". Men det är sällan det som menas i dessa uppgifter.

Till TS: Att implementera en singleton är ingen jättegrej. Tänk bara "hur kan jag garantera att en instans av det här objektet skapas?"
https://en.wikipedia.org/wiki/Singleton_pattern#C#_implementa...