Transactions in business logic layer - c#

I stole the following code from https://stackoverflow.com/a/14988549/294022 .
This seems a great abstraction. However there is a single problem. What if you want to combine multiple service calls?
This works fine combining DAL calls. But for service? Is there a solution?
public class Foo //POCO for data access
{
//Add Attributes for Ormlite
public int Id { get; set; }
}
public class Bar //POCO for data access
{
//Add Attributes for Ormlite
public int Id { get; set; }
}
//your request class which is passed to your service
public class DeleteById
{
public int Id { get; set; }
}
public class FooBarService : MyServiceBase //MyServiceBase has resusable method for handling transactions.
{
public object Post(DeleteById request)
{
DbExec(dbConn =>
{
dbConn.DeleteById<Foo>(request.Id);
dbConn.DeleteById<Bar>(request.Id);
});
return null;
}
}
public class MyServiceBase : Service
{
public IDbConnectionFactory DbFactory { get; set; }
protected void DbExec(Action<IDbConnection> actions)
{
using (var dbConn = DbFactory.OpenDbConnection())
{
using (var trans = dbConn.OpenTransaction())
{
try
{
actions(dbConn);
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
}
}
}
}

I think you need to go toward implementing a UnitOfWork that can be passed/shared amongst those who wish to partake in a single 'transaction'.

Related

Microsoft Entity Framework is not saving changes

Im currently trying to implement CRUD functionality with a dbfactory and generics with microsoft EF, but while listing entries is working, making changes to the db is currently not working.
public class AbstractDataModel
{
[Key]
public Guid gid { get; set; }
}
Model
class SalesOrder : AbstractDataModel
{
public int salesOrderID { get; set; }
public int productID { get; set; }
public int customerID { get; set; }
public Guid createdBy { get; set; }
public string dateCreated { get; set; }
public string orderDate { get; set; }
public string orderStatus { get; set; }
public string dateModified { get; set; }
}
A DBCore with some other functionality besides the ones listed here, which are not relevant for the factory
public class DBCore : DbContext
{
public static string connectionString = "myConnectionStringToDb";
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(connectionString);
}
Data Service which calls factory
class SalesOrderService : DBCore
{
public DbSet<SalesOrder> SalesOrders { get; set; }
public OkObjectResult GetAllSalesOrders()
{
DBFactory factory = new DBFactory();
return new OkObjectResult(JsonConvert.SerializeObject(factory.GetAll(SalesOrders)));
}
public OkObjectResult AddSalesOrder(SalesOrder order)
{
order.gid = Guid.NewGuid();
return DBFactory.AddOne(order);
}
public OkObjectResult UpdateSalesOrder(SalesOrder order)
{
return DBFactory.UpdateOne(order);
}
public OkObjectResult DeleteSalesOrder(SalesOrder order)
{
return DBFactory.DeleteOne(order);
}
}
simple CRUD-Factory,
class DBFactory : DBCore
{
public DbSet<UserModel> Users { get; set; }
public DbSet<SalesOrder> SalesOrders { get; set; }
public List<T> GetAll<T>(DbSet<T> dbset) where T : class
{
using (this)
{
return dbset.ToList();
}
}
public static OkObjectResult AddOne<T>(T data)
{
using (DBFactory factory = new DBFactory())
{
factory.Add(data);
factory.SaveChanges();
return new OkObjectResult("Entry was sucessfully added");
}
}
public static OkObjectResult UpdateOne<T>(T data)
{
using (DBFactory factory = new DBFactory())
{
factory.Update(data);
factory.SaveChanges();
return new OkObjectResult("Entry was sucessfully updated");
}
}
public static OkObjectResult DeleteOne<T>(T data)
{
using (DBFactory factory = new DBFactory())
{
factory.Attach(data);
factory.Remove(data);
factory.SaveChanges();
return new OkObjectResult("Entry was sucessfully removed");
}
}
}
Edit: Following the advices i changed the code so it should SaveChanges for the Factory, which also contains the context as a property. But it still doesnt seem to work for all database operations except listing all entries
Editv2: Thanks for the adivces it seems i have solved that problem, but a new one appeared :D
I can now do database operations like deleting entries, but now i cant list the entries anymore because the following error occurs, although the code there didnt really change:
"Executed 'GetAllOrders' (Failed, Id=5fb95793-572a-4545-ac15-76dffaa7a0cf, Duration=74ms)
[2020-10-23T14:33:43.711] System.Private.CoreLib: Exception while executing function: GetAllOrders. Newtonsoft.Json: Self referencing loop detected for property 'Context' with type 'FicoTestApp.Models.SalesOrder'. Path '[0].ChangeTracker'."
try adding
services.AddControllers().AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
to your
startup.cs
it should to the job

Apply patterns on repeating operations

What pattern/s can I apply on my code? I have been reading on the patterns Bridge, Command, and Builder, and it looks like I can apply them on the code block below, but I struggle at how I can apply them to my code. I've also tried adding a generic method, but I struggle with consuming it with my repository.
This is a webform backend code. I am using .Net 3.5 (can't upgrade), and C#7. Here is the code:
Entities
Note: I'm using data transfer objects because these classes have a lot of properties. Also, SpecialRequestDTO inherits StandardRequestDTO.
public class StandardRequest
{
public int RequestType { get; protected set; }
public string Name { get; protected set; }
private StandardRequest(StandardRequestDTO dto) { Name = dto.Name; }
public static StandardRequest Create(StandardRequestDTO dto) => new StandardRequest(dto);
}
public class SpecialRequest : StandardRequest
{
public string Desc { get; protected set; }
private SpecialRequest(SpecialRequestDTO dto) : base((StandardRequestDTO) dto) { Desc = dto.Desc; }
public static SpecialRequest Create(SpecialRequestDTO dto) => new SpecialRequestDTO(dto);
}
Repository
public class Repository
{
public void SaveStandardRequest(StandardRequest request)
{
var query = $"INSERT INTO Requests (Name, RequestType) Values(#{nameof(request.Name)}, #{nameof(request.RequestType)})";
// sqlcommand code etc
}
public void SaveSpecialRequest(SpecialRequest request)
{
var query = $"INSERT INTO Requests (Name, RequestType, Desc) VALUES(#{nameof(request.Name)}, #{nameof(request.Name)}, #{nameof(request.Desc)})";
// sqlcommand code etc
}
}
Index.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
string requestTypeStr = Request.Form[nameof(requestTypeStr)];
if (string.IsNullOrEmpty(requestTypeStr))
return;
}
if (requestTypeStr == 0)
{
ValidateStandardRequestFields();
var dto = CreateStandardRequestDTO();
_repository.SaveStandardRequest(dto);
}
if (requestTypeStr == 1)
{
ValidateSpecialRequestFields();
var dto = CreateSpecialRequestDTO();
_repository.SaveSpecialRequest(dto);
}
}
I took most of what I could from your post and this is what I could come up with.
Your DTO objects don't seem very clear to me, so I've left it out, but they have a role to play when it comes to saving the objects in the repository and I've left that part out.
Interface
public interface IRequest
{
int RequestType { get; set; }
string Name { get; set; }
void ValidateFields();
}
Two types of Request
public class StandardRequest : IRequest
{
public int RequestType { get ; set ; }
public string Name { get ; set; }
public void ValidateFields()
{
//validation logic
}
}
public class SpecialRequest: IRequest
{
public string Desc { get; set; }
public int RequestType { get; set; }
public string Name { get; set; }
public void ValidateFields()
{
//validation logic
}
}
Factory to create the Request objects
public class RequestFactory
{
public static IRequest CreateRequest(string requestTypeStr)
{
switch (requestTypeStr)
{
case "0": return new SpecialRequest();
default: return new StandardRequest();
}
}
}
Class to handle the interactions of the IRequest object, aptly named RequestInteractions, I know a poor name choice!
This class is what validates and saves the requests.
public class RequestInteractions
{
private IRequest _requestObj;
private Repository _repository;
public RequestInteractions(IRequest requestObj, Repository repository)
{
_requestObj = requestObj;
_repository = repository;
}
public bool ValidateAndSave()
{
try
{
_requestObj.ValidateFields();
_repository.SaveRequest(_requestObj);
return true;
}
catch (Exception e)
{
throw;
}
}
}
Repository - like I said, this needs to be fleshed out. The IRequest (through the DTO) should be able to tell you how it needs to be persisted. You'll have to fill this in.
public class Repository
{
public void SaveRequest(IRequest requestObject)
{
//The respective DTO should help you figure out what to save based on the type of IRequest
}
}
Tying it all together
var repository = new Repository();
var requestObject = RequestFactory.CreateRequest("");
var requestInteractions = new RequestInteractions(requestObject,repository);
requestInteractions.ValidateAndSave();
Benefit of this approach - You need to create a new Request class (and
a DTO) when you get a new Request to add to the system, the rest of
the plumbing need not be touched.
Downside - Well, a lot of code compared to what you have.

Create database in App_Start

I have an solution with 2 projects, one domain class and one webUI.
In the domain class I have 2 models, a db context and a databas initializer.
List.cs:
namespace Todo.Domain
{
public class List
{
public int ListID { get; set; }
public string Day { get; set; }
public ICollection<Task> Tasks { get; set; }
}
}
Task.cs:
namespace Todo.Domain
{
public class Task
{
public int TaskID { get; set; }
public int ListID { get; set; }
public string TodoTask { get; set; }
}
}
EFDbContext.cs:
namespace Todo.Domain
{
public class EFDbContext : DbContext
{
public EFDbContext() : base("TodoList") { }
public DbSet<List> Lists { get; set; }
public DbSet<Task> Tasks { get; set; }
}
}
Initializer:
namespace Todo.Domain
{
public class TodoDbInit : System.Data.Entity.DropCreateDatabaseIfModelChanges<EFDbContext>
{
protected override void Seed(EFDbContext context)
{
var list = new List<List>
{
new List { Day="MÃ¥ndag" }
};
list.ForEach(s => context.Lists.Add(s));
context.SaveChanges();
var task = new List<Task>
{
new Task { TodoTask="Fisk" }
};
task.ForEach(s => context.Tasks.Add(s));
context.SaveChanges();
}
}
}
Now, when I start my application, I want the database to be created. I have placed a setInitializer Global.asax:
namespace Todo.WebUI
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
Database.SetInitializer(new TodoDbInit());
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
}
When I run my application, the database Is not created. I don't know why. Have I done something wrong?
You must make a call against your database in one of your controllers in order for it to be created (creation is on-demand). If you wish to manually create the database on application start see the answer posted here: Entity Framework code first, isn't creating the database
The following code is only setting the initializer that you wish to use against your database.
Database.SetInitializer(new TodoDbInit());
In order for it to actually be used you will need to create and access entities of your db context.

Accessing the db connection from within my code first "table" class

Is it possible to get to the current connection from inside your code first "table" class?
I'm trying to write a MVC Multi Tenancy app (1 app, many db's) the easiest way i can think of is to pass the connection string (or tenant name) when creating the dbcontext (i've looked at other ways to do this, but don't really understand them). However once i go into the table class I cannot then access the current db connection to perform other actions i need.
Example code
public class ConnectionContext : DbContext
{
public ConnectionContext(String connectionString)
: base(connectionString)
{
if (!this.Database.Exists())
throw new Exception("Database does not exist");
}
public DbSet<Table1> Table1 { get; set; }
}
[Table("Table1")]
public class Table1
{
[Key]
public String Column1 { get; set; }
public String Column2 { get; set; }
public Int32 GetNoOfColumns()
{
using (var conn = new ConnectionContext("???")) // <-- **issue here**
{
//Code to get info
return 0;
}
}
public void Authorize()
{
using (var conn = new ConnectionContext("???")) // <-- **issue here**
{
this.Column2 = "Authorized";
conn.Entry(this).State = EntityState.Modified;
conn.SaveChanges();
}
}
}
Not sure it's possible in the way that I was looking at.
You don't have to make a constructor with a connection string parameter, you can create your dbcontext class like this:
public class ConnectionContext : DbContext
{
public ConnectionContext()
: base("nameOrConnectionString")
{
if (!this.Database.Exists())
throw new Exception("Database does not exist");
}
public DbSet<Table1> Table1 { get; set; }
}
And then call your connection context this way:
using (var conn = new ConnectionContext())
{
}

Exposing the return of method into a property

How can i expose the return of my method in to a class property?
public class BIContactLib: ContactLib
{
//Expose it here.. to replace the code below
public IEnumerable<BIContactLib> GetContactLibs
{
get { return (BIContactLib) GetAll}
set { ; }
}
}
public class BIContactLibService : IBIRequirement, IDisposable
{
private ClientContext context = new ClientContext();
//The return of the method here is the one I would like to expose
public IEnumerable<BIContactLib> GetAll()
{
var contactslib = context.ContactLibs;
return contactslib.ToList();
}
}
The reason behind this, is I want to create a view model with have the list of contacts library... heres my view model by the way..
public class PersonInformation
{
public BIPerson Person { get; set; }
public BIAddress Address { get; set; }
//This is where i want to use it
public IEnumerable<BIContactLib> GetAll { get; set; }
}
Or any other way to do this?
Best regards,
How about something like this
public IEnumerable<BIContactLib> GetContactLibs
{
get {
BiContractLib lib = new BiContractLib();
return lib.GetAll();
}
}

Categories

Resources