Creating generic DbContext Factory in Entity Framework - c#

I'm using .Net Core 2.1. I'm using more than one DbContext. I'm creating a DbContextFactory for every context. But, I want to do this in a generic way. I want to create only one DbContextFactory. How can I achieve this?
MyDbContextFactory.cs
public interface IDbContextFactory<TContext> where TContext : DbContext
{
DbContext Create();
}
public class MyDbContextFactory : IDbContextFactory<MyDbContext>
{
public IJwtHelper JwtHelper { get; set; }
public MyDbContextCreate()
{
return new MyDbContext(this.JwtHelper);
}
DbContext IDbContextFactory<MyDbContext>.Create()
{
throw new NotImplementedException();
}
}
UnitOfWork.cs
public class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext : DbContext
{
public static Func<TContext> CreateDbContextFunction { get; set; }
protected readonly DbContext DataContext;
public UnitOfWork()
{
DataContext = CreateDbContextFunction();
}
}
MyDbContext.cs
public class MyDbContext : DbContext
{
private readonly IJwtHelper jwtHelper;
public MyDbContext(IJwtHelper jwtHelper) : base()
{
this.jwtHelper = jwtHelper;
}
}

So you have a database, and a class that represent this database: your DbContext, it should represent the tables and the relations between the tables that are in your database, nothing more.
You decided to separate the operations on your database from the database itself. That is a good thing, because if several users of your database want to do the same thing, they can re-use the code to do it.
For instance, if you want to create "an Order for a Customer with several OrderLines, containing ordered Products, agreed Prices, amount, etc", you'll need to do several things with your database: check if the customer already exists, check if all products already exist, check if there are enough items, etc.
These things are typically things that you should not implement in your DbContext, but in a separate class.
If you add a function like: CreateOrder, then several users can re-use this function. You'll only have to test this only once, and if you decide to change something in your order model, there is only one place where you'll have to change the creation of an Order.
Other advantages of separating the class that represents your database (DbContext)
from the class that handles this data is that will be easier to change the internal structure without having to change the users of your database. You can even decide to change from Dapper to Entity Framework without having to change usage. This makes it also easier to mock the database for test purposes.
Functions like CreateOrder, QueryOrder, UpdateOrder already indicate that they are not generic database actions, they are meant for an Ordering database, not for a School database.
This might have the effect that unit-of-work might not be a proper name for the functionality you want in the separate class. A few years ago, unit-of-work was mainly meant to represent actions on a database, not really a specific database, I'm not really sure about this, because I saw fairly soon that a real unit-of-work class would not enhance functionality of my DbContext.
Quite often you see the following:
A DbContext class that represents your Database: the database that you created, not any generic idea of databases
A Repository class that represent the idea of storing your data somewhere, how this is stored, it could be a DbContext, but also a CSV-file, or a collection of Dictionary classes created for Test purposes. This Repository has an IQueryable, and functions to Add / Update / Remove (as far as needed
A class that represents your problem: the ordering model: Add Order / Update Order / Get Order of a Customer: this class really knows everything about an Order, for instance that it has an OrderTotal, which is probably nowhere to be found in your Ordering database.
Outside DbContext you sometimes may need SQL, for instance to improve efficiency of a call. Outside Repository it should not be seen that you are using SQL
Consider to separate the concerns: how to save your data (DbContext), how to CRUD (create, fetch, update, etc) the data (Repository), how to use the data (combine the tables)
I think what you want to do in your unit-of-work should be done inside the repository. Your Ordering class should create the Repository (which creates the DbContext), query several items to check the data it has to Add / Update, do the Add and Update and save the changes. After that your ordering class should Dispose the Repository, which in turn will Dispose the DbContext.
The Repository class will look very similar to the DbContext class. It has several sets that represent the tables. Every set will implement IQueryable<...> and allow to Add / Update / Remove, whatever is needed.
Because of this similarity in functions you could omit the Repository class and let your Ordering class use the DbContext directly. However, keep in mind, that changes will be bigger if in future you decide that you don't want to use entity framework anymore but some newer concept, or maybe return back to Dapper, or even more low level. SQL will seep through into your Ordering class
What to choose
I think you should answer several questions for yourself:
Is there really only one database that should be represented by your DbContext, could it be that the same DbContext should be used in a 2nd database with the same layout. Think of a test database, or a development database. Wouldn't it be easer / better testable / better changeable, to let your program create the DbContext that is to be used?
Is there really one group of Users of your DbContext: should everyone have the possibility to Delete? to Create? Could it be that some programs only want to query data (the program that e-mails the orders), and that order programs need to add Customers. And maybe another program needs to Add and Update Products, and the amount of Products in the warehouse. Consider Creating different Repository classes for them. Each Repository gets the same DbContext, because they are all accessing the same database
Similarly: only one data processing class (the above mentioned ordering class): should the process that handles Orders, be able to change product prices and add items to the stock?
The reason that you need the factories, is because you don't want to let your "main" program decide what items it should create for the purpose it is running right now. Code would be much easier if you created the items yourself:
Creation sequence for an Ordering Process:
IJwtHelper jwtHelper = ...;
// The product database: all functionality to do everything with Products and Orders
ProductDbContext dbContext = new ProductDbContext(...)
{
JwtHelper = jwtHelper,
...
};
// The Ordering repository: everything to place Orders,
// It can't change ProductPrices, nor can it stock the wharehouse
// So no AddProduct, not AddProductCount,
// of course it has TakeNrOfProducts, to decrease Stock of ordered Products
OrderingRepository repository = new OrderingRepository(...) {DbContext = dbContext};
// The ordering process: Create Order, find Order, ...
// when an order is created, it checks if items are in stock
// the prices of the items, if the Customer exists, etc.
using (OrderingProcess process = new OrderingProcess(...) {Repository = repository})
{
... // check if Customer exists, check if all items in stock, create the Order
process.SaveChanges();
}
When the Process is Disposed, the Repository is Disposed, which in turns Disposes the DbContext.
Something similar for the process that e-mails the Orders: It does not have to check the products, nor create customers, it only has to fetch data, and maybe update that an order has been e-mailed, or that e-mailing failed.
IJwtHelper jwtHelper = ...;
// The product database: all functionality to do everything with Products and Orders
ProductDbContext dbContext = new ProductDbContext(...) {JwtHelper = jwtHelper};
// The E-mail order repository: everything to fetch order data
// It can't change ProductPrices, nor can it stock the wharehouse
// It can't add Customers, nor create Orders
// However it can query a lot: customers, orders, ...
EmailOrderRepository repository = new EmailOrderRepository(...){DbContext = dbContext};
// The e-mail order process: fetch non-emailed orders,
// e-mail them and update the e-mail result
using (EmailOrderProcess process = new EmailOrderProcess(...){Repository = repository}
{
... // fetch the orders that are not e-mailed yet
// email the orders
// warning about orders that can't be emailed
// update successfully logged orders
repository.SaveChanges();
See how much easier you make the creation process, how much more versatile you make it: give the DbContext a different JwtHelper, and the data is logged differently, give the Repository a different DbContext and the data is saved in a different database, give the Process a different Repository, and you'll use Dapper to execute your queries.
Testing will be easier: create a Repository that uses Lists to save the tables, and testing your process with test data will be easy
Changes in databases will be easier. If for instance you later decide to separate your databases into one for your stock and stock prices and one for Customers and Orders, only one Repository needs to change. None of the Processes will notice this change.
Conclusion
Don't let the classes decide which objects they need. Let the creator of the class say: "hey, you need a DbContext? Use this one!" This will omit the need of factories and such
Separate your actual database (DbContext) from the concept of storing and retrieving data (Repository), use a separate class that handles the data without knowing how this data is stored or retrieved (The process class)
Create several Repositories that can only access the data they need to perform the task (+data that can be foreseen in future after expected changed). Don't make too much Repositories, but also not one that can do everything.
Create process classes that do only what they are meant to do. Don't create one process class with 20 different tasks. It will only make it more difficult to describe what it should do, more difficult to test it and more difficult to change the task

If you want to reuse the existing implementation, EF Core 5 provides DbContext Factory out of the box now:
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#dbcontextfactory
Make sure you dispose it correctly, as it's instances are not managed by the application's service provider.
See Microsoft documentation
Using a DbContext factory

Related

How To Clear Uninserted POCO's From DbContext? - Entity Framework Code First

I am currently developing a project for a university which provides students to create an account and with this account they can calculate their GPA, accessing the forum and screen sharing. I am using ASP.NET MVC and ASP.NET Web Api technologies. I developed my Business Layer and made "Manager" classes that helps to make db operations for all POCOs of my project. All of my manager classes uses same DbContext which is coming from BaseManager class. I have some set of rules in my POCO classes that prevents db insertion if the creedientals are wrong.
When I enter these fields wrong, db insertion fails as expected. But after that when I insert the correct values (entering a value to a field that should not be null etc.) db insertion fails again and same validationerrors shows up as before failed attempt despite that the mistakes are corrected.
Here is my User POCO class(I am sharing the 2 fields that I'm talking about):
[Table("UserTable")]
public class User
{
[Required]
[Index(IsUnique = true)]
[MaxLength(30, ErrorMessage = "Username can not exceed 30 characters!")]
public string Username { get; set; }
[Required]
[Index(IsUnique = true)]
[MaxLength(50, ErrorMessage = "Email can not exceed 50 characters!")]
[DataType(DataType.EmailAddress)]
[RegularExpression("^([\\w-\\.]+)#((\\[[0-9]{1,3}\\.[0-9]{1,3} \\.[0-9]{1,3}\\.)|(([\\w-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$")]
public string Email { get; set; }
}
When I check the DbSet of my corresponding POCO class, I saw all of my attempted objects also kept locally just like my 2 records on the database. Here's the screenshot:
Here's my Business Layer logic:
I created Generic Repository classes that can be implemented by any POCO classes.
I created UnitOfWork class that creates DbSontext with SingletonPattern (UnitOfWork object creating at UnitOfWorkBase class) and has 2 methods used by Manager and ComplexManager classes.
1.Generic GetRepository
2.Generic GetManager
I use manager classes in order to define every POCO's set of methods like CRUD and POCO specified methods (like UserManager has CheckCreedientals for login method). All db operations made here.
I use complex manager classes in order to achieve methods that requires more than one manager. (Like createAccount method that takes place in AccountComplexManager class creates two POCO classes and adds them into database.). There is no db operations except commiting the changes made here. The method of corresponding manager class is being called and db operations are done by these methods of manager classes' methods.
1.Student POCO
2.User POCO
I created 3 base classes for my Managers in order to reduce dummy code.
1.MotherBaseManager class: UnitOfWork object created here and using by all managers that makes db operations. This class also inherited by the next 2 classes. It has only Context method which returns the UnitOfWork object.
2.BaseComplexManager class: Base class of ComplexManagers classes. Has GetManager method in order to get corresponding managers to achieve some db tasks and Save method in order to commit changes on db.
3.BaseManager class: Base class of Manager classes. It has Get Repository class in order to get corresponding repository classes for performing db operations such as CRUD.
My Class View of adjusted version of Business Layer is:
I want to purge dbSet local from insert-attempted objects without restarting the application in order to give chance users to correct their mistakes and create account again.
If I have mispelled or mistaken at some point please forgive me. Any helps would be appreciated. Thanks!
Besides the very valid comment from sellotape about DbContext lifetime, there are a couple of things you can do.
If you want to make the least changes to your app, then I'd suggest to use transactions, e.g.
using (var transaction = dbContext.Database.BeginTransaction())
{
try
{
// Prepare POCOs to be stored
...
dbContext.SaveChanges();
transaction.Commit();
}
catch (Exception)
{
// Handle failure
}
}
If SaveChanges() fails, your transaction will be automatically rolled back and all the objects added in this block will be removed from dbContext.
You can find more info in MSDN on transactions in EF Core or EF 6.
However, I would suggest you to rethink your validation scheme instead. Data validation is not supposed to happen on the database level - validation should happen much earlier in the request lifetime, maybe even before you create POCOs to be stored to the database.

CQRS - Creating BaseCommandHandler using Mediatr in C#, ASP.net Core

So I learned about Mediatr created by Jimmy Bogard and it really amazed me how we achieve CQRS pattern using Mediatr.
I started to implement it in my application, and then I realized that I might have hundreds of Entities in my application, and for each Entity I am not sure about creating separate commands and queries, that would be just opposite of DRY. So I started writing base commands and handlers.
public abstract class CreateBaseCommand : IRequest
{
}
public abstract class DeleteBaseCommand : IRequest
{
} //... and so on.
And respective handlers.
public abstract class CreateBaseHandler : IRequestHandler<CreateBaseCommand>
{
}
public abstract class DeleteBaseCommandHandler : IRequestHandler<DeleteBaseCommand>
{
}//... and so on.
But I realised, I would still need separate commands for my domain entities, and they will have to derive from their Base commands respectively.
Then I though if I can just put all commands in one and have just one base handler.
public abstract class BaseCommand : IRequest
{
int CommandType
}
public abstract class BaseCommandHandler : IRequestHandler<BaseCommand>
{
public Task<Unit> Handle(BaseCommand request, CancellationToken cancellationToken)
{
if (CommandType == 0)
{
// Create entity
}
if (CommandType == 1)
{
// Update entity
}//.. and so on
}
}
I was wondering if there is more efficient way to do this, I am not very convinced with idea of using CommandType and have one handle method to perform all CRUD operations.
Is this a good approach or should I have separate set of commands for each domain entity?
Technically you should have a separate command for each creation. Adding a base doesn’t get you much, and complicates the structure so I would get rid of that.
The real problem is that you’re thinking of this from the point of view of your data model, not your domain model. You’re creating a command per table row when you should be creating a command per business idea.
For example, say you’re writing a retail system and creating a new order for a registered user, and that order has 5 line items. Looking at this from the data model point of view, you need to send 1 create command for the order and 5 create commands for the line items. What should happen is to send one command AddNewOrderCommand and have the User class handle it. The User class method then creates the new order database row and all line items. How you add the order (what rows to create, etc) is now encapsulated in the method where it belongs instead of your command architecture.
Are you using Domain Driven Design for your app? If not, you should consider it because its very good with complex systems, and if you have hundreds of entities chances are good this qualifies as a complex system. In DDD parlance, the User above is called and Aggregate Root and the Order and Line Item are just entities. You don’t have Create commands for entities, just for the Aggregate Roots. (Note that a class can sometimes be both an aggregate root and a normal entity in another aggregate root. Why this happens and how to handle it is out of scope for this question)
Look through your model and find things that don’t make sense unless they are owned by something else. For example the line item from the example above. Having a line item in my database that’s not related to an order makes no sense. Therefore, all line items should be created by orders. Having an order with no user associated with it also makes no sense. Therefore the user should create the order. A user, however, seems to be the top of the pyramid, so it is the aggregate root and does need a Create command.
If you analyze your domain, you’ll find you don’t need nearly so many Create commands, and so the problem disappears.

Database Class Instance, Fields, Entity Framework

Im new to C# and Entity Framework and I have a question about fields and initialization of a database class.
I have received some code in a program from my teacher that has a connection to a MySQL database through Entity Framework.
So far we have seen examples where inside methods for adding stuff to the database you first create an instance of it. An example of what we have seen so far:
using (var db = new p4_databaseEntities())
{
cp_car carEntity = new cp_car();
carEntity.make = car.make;
carEntity.model = car.model;
carEntity.year = car.year;
db.cp_car.Add(carEntity); //
db.SaveChanges();
MessageBox.Show("A Car has been added");
}
this.Close();
cp_car is a table in the database and a class in the program.
db is the current instance of the database.
Now, in the code I have received, this is not done this way. Its done in a different matter. Btw the program is a windows forms.
In the first form window, inside the constructor, he has created a new instance of the database and he calls upon a method called init
from the another class called LinqQueries. The code for that:
public Form1()
{
InitializeComponent();
p4_databaseEntities db = new p4_databaseEntities();
LinqQueries.Init(db);
this.CenterToScreen();
}
How the LinqQueries class looks:
private static p4_databaseEntities _db;
public static void Init(p4_databaseEntities db)
{
_db = db;
}
As I understand he created a new instance of the database in the constructor, where he also called on the init method. This method then defined the db object as _db. Now every single method he makes for adding or removing data from the database he is using _db and not db.
My question is does this mean that the init method assigns the static field of type p4_databaseEntities (name of database class) as an object? Is the value of the _db then an object? Is it a reference to an object? And also i noticed he uses the same field over and over again when making changes to the database which led me to believe it may be an active object that doesn’t die through out the programs lifespan?
If anyone could clarify this It would be greatly appreciated. Excuse any errors or wrong statements I have made please correct me if im wrong in any way. Im new to C# and Entity Framework.
Thanks beforehand
You are a bit inaccurate in your descriptions. This attributes to your confusion.
A DbContext is not a database, it represents the connection to the database. If you construct a DbContext object, you get access to the tables in the database via the Entities described in the DbSet.
Although it seems that a DbSet represents a table in the database, it does not. For instance an entity that you access via a DbSet can have an ICollection as member, which contains items that are not part of the table, but are items in a different table. Accessing the items in this ICollection causes an SQL Join to be performed, without you having to type the join
Hence a DbSet object in the DbConnection does not represent database table, it represents the access to the properties that can be accessed using the DbSet object, inclusive the properties of all objects in the database that are in tables that have a relation with the DbSet object.
Your first code with the using statement is the normal way entity framework should be used.
To hide the design of the database, quite often a separate class is introduced that is the only one that should use the DbContext. All users of the database should communicate using the seperate class.
This allows changing the internals of the database without having to change the code that uses the database.
This is what probably was meant as the purpose of the LinqQueries class. Instead of calling functions of the DbContext directly, users should call the (probably static) functions of LinqQueries. This way the internal structure of the database can change without having to change the callers of the LinqQueries functions.
What in fact is happening is that LinqQueries is meant to communicate with only one DbContext. LinqQueries does not decide which DbContext is used. Proper functioning depends heavily on exactly one user of LinqQueries who should create a DbContext and Init LinqQueries. This one user should also know when no one needs the LinqQueries anymore, because he has to Dispose() the DbContext.
This design if fairly error prone. Supposing the designer makes very grood products, his products will be used by a lot of users (meaning software, not operators). How do you assert that exactly one user will call the Init function?
If you really want that all users use the same Dbcontext, why not let the constructor of LinqQueries create this DbContext. In fact the design is similar to the singleton design pattern, so why not create LinqQueries as a singleton.
The limitation that all users of LinqQuery should use the same one and only DbContext limits the use of the LinqQuery class unnecessarily.
If users of the LinqQuery class could pass a DbContext to the constructor, then users could decide which database should be used by this particular LinqQueries object. This is quite handy when creating unit tests: instead of using the original database, the testable code could be used with a database with specific test values.
All in all the goal of the designer is unclear. IMHO it is a bad design and you are right that it is not clear what happens.
class LinqQueries
{
// default constructor: use the default DbContext
public LinqQueries()
{
this.DbContext = new p4_databaseEntities();
}
// special constructor: user provided dbContext
public LinqQueries(p4_databaseEntities dbContext)
{
this.dbContext = dbContext;
}
private readonly p4_databaseEntities dbContext;
public IEnumerable<...> GetMyElements(...)
{
return this.dbContext....;
}
}
This way, every creator of the LinqQueries would exactly know what to do either use the default p4_databaseEntities or create your own Dbcontext and dispose it when not needed anymore
using (var mydbContext = new p4_databaseEntities())
{
LinqQueries linqQueries = new LinqQueries(myDbContext);
// note that this constructor is very lightWeight!
var result = linqQueries.MyQeury(...);
}
This method is really safe. Any errors made by me, does not influence the code of any other user of the LinqQuery class.

Should I have duplicate class properties or create an extra database table?

I'm trying to come up with a data model to represent a simple money transaction system.
I have one class to represent a Transaction, and another class to represent a TransactionRecurrence.
Both classes will need properties for an Id, Amount, Type (Withdrawal/Deposit), and a Note of some sort. The Transaction will also hold a TransactionDate, while the TransactionRecurrence will hold some sort of data to represent the recurrence (10th of every month etc.).
My question is: For the properties that both classes will share should I just duplicate the properties in both classes (which would go against DRY principle), or should I create a new class, say, TransactionInfo that holds the four properties then add a property with that type to the two other classes (which would require a new DB table and foreign key references/SQL joins that would slow down the queries)?
Edit: For example users will each have a collection of Transactions (which are one-time transactions) and a separate collection of TransactionRecurrences both which they create on the client via a web api. These are stored in the database in separate tables. Then the user can request the transactions through the api. The Transactions can just be added to a collection directly, but the TransactionRecurrences will need to be used to generate more Transaction objects based upon a date range passed in with the request before being added to the collection. The collection is then serialized and sent to the requester.
Why not have a abstract base class or interface holding those common properties and have your concrete classes implement or inherit that interface or base class. That way you are not duplicating the properties anymore.
Moreover, explain what you are trying to achieve actually and provide better example. So that someone may come up with a better approach.
If every transaction should have recurring facility then you can consider using Composition like below
public class TransactionRecurrence
{
public int DurationPerMonth { get; set; }
}
public class Transaction
{
public TransactionRecurrence _transref;
public Transaction(TransactionRecurrence transref)
{
_transref = transref;
}
//other properties
}

Repository and Unit of Work patterns - How to save changes

I'm struggling to understand the relationship between the Repository and Unit of Work patterns despite this kind of question being asked so many times. Essentially I still don't understand which part would save/commit data changes - the repository or the unit of work?
Since every example I've seen relates to using these in conjunction with a database/OR mapper let's make a more interesting example - lets persist the data to the file system in data files; according to the patterns I should be able to do this because where the data goes is irrelevant.
So for a basic entity:
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
}
I imagine the following interfaces would be used:
public interface IAccountRepository
{
Account Get(int id);
void Add(Account account);
void Update(Account account);
void Remove(Account account);
}
public interface IUnitOfWork
{
void Save();
}
And I think in terms of usage it would look like this:
IUnitOfWork unitOfWork = // Create concrete implementation here
IAccountRepository repository = // Create concrete implementation here
// Add a new account
Account account = new Account() { Name = "Test" };
repository.Add(account);
// Commit changes
unitOfWork.Save();
Bearing in mind that all data will be persisted to files, where does the logic go to actually add/update/remove this data?
Does it go in the repository via the Add(), Update() and Remove() methods? It sounds logical to me to have all the code which reads/writes files in one place, but then what is the point of the IUnitOfWork interface?
Does it go in the IUnitOfWork implementation, which for this scenario would also be responsible for data change tracking too? To me this would suggest that the repository can read files while the unit of work has to write files but that the logic is now split into two places.
Repository can work without Unit Of Work, so it can also have Save method.
public interface IRepository<T>
{
T Get(int id);
void Add(T entity);
void Update(T entity);
void Remove(T entity);
void Save();
}
Unit Of Work is used when you have multiple repositories (may have different data context). It keeps track of all changes in a transaction until you call Commit method to persist all changes to database(file in this case).
So, when you call Add/Update/Remove in the Repository, it only changes the status of the entity, mark it as Added, Removed or Dirty... When you call Commit, Unit Of Work will loop through repositories and perform actual persistence:
If repositories share the same data context, the Unit Of Work can work directly with the data context for higher performance(open and write file in this case).
If repositories have different data context(different databases or files), the Unit Of Work will call each repository's Save method in a same TransactionScope.
I'm actually quite new to this but as nobody wiser has posted:
The code which CRUDs happens in the repositories as you would expect, but when Account.Add (for example) is called, all that happens is that an Account object is added to the list of things to be added later (the change is tracked).
When unitOfWork.Save() is called the repositories are allowed to look through their list of what has changed Or the UoW's list of what has changed (depending on how you choose to implement the pattern) and act appropriately - so in your case there might be a List<Account> NewItemsToAdd field that has been tracking what to add based on calls to .Add(). When the UoW says it's OK to save, the repository can actually persist the new items as files, and if successful clear the list of new items to add.
AFAIK the point of the UoW is to manage the Save across multiple repositories (which combined are the logical unit of work that we want to commit).
I really like your question.
I've used Uow / Repository Pattern with Entity Framework and it shows how much EF actually does (how the context tracks the changes until SaveChanges is finally called). To implement this design pattern in your example you need to write quite a bit of code to manage the changes.
Ehe, things are tricky. Imagine this scenario: one repo saves something in a db, other on the file system and the third something on the cloud. How do you commit that?
As a guideline, the UoW should commit things, however in the above scenario, Commit is just an illusion as you have 3 very different things to update. Enter eventual consistency, which means that all things will be consistent eventually (not in the same moment as you're used with a RDBMS).
That UoW is called a Saga in a message driven architecture. The point is every saga bit can be executed at different time. Saga completes only when all 3 repositories are updated.
You don't see this approach as often, because most of the time you'll work with a RDBMS, but nowadays NoSql is quite common so a classic transactional approach is very limited.
So, if you're sure you work ONLY with ONE rdbms, use a transaction with the UoW and pass teh associated connection to each repository. At the end, UoW will call commit.
If you know or expect you might have to work with more than one rdbms or a storage that doesn't support transactions, try to familiarize yourself with a message driven architecture and with the saga concept.
Using the file system can complicate things quite much if you want to do it on yourself.
Only write when the UoW is committed.
What you have to do is to let the repositories enqueue all IO operations in the UnitOfWork. Something like:
public class UserFileRepository : IUserRepository
{
public UserFileRepository(IUnitOfWork unitOfWork)
{
_enquableUow = unitOfWork as IEnquableUnitOfWork;
if (_enquableUow == null) throw new NotSupportedException("This repository only works with IEnquableUnitOfWork implementations.");
}
public void Add(User user)
{
_uow.Append(() => AppendToFile(user));
}
public void Uppate(User user)
{
_uow.Append(() => ReplaceInFile(user));
}
}
By doing so you can get all changes written to the file(s) at the same time.
The reason that you don't need to do that with DB repositories is that the transaction support is built into the DB. Hence you can tell the DB to start a transaction directly and then just use it to fake a Unit Of Work.
Transaction support
Will be complex as you have to be able to roll back changes in the files and also prevent different threads/transactions from accessing the same files during simultaneous transactions.
normally, repositories handle all reads, and unit-of-work handles all writes,but for sure you can handle all reads and writes by only using one of these two
(but if only using repository pattern, it will be very tedious to maintain maybe 10 repositories,more worse,maybe result in inconsistent reads and writes be overwritten),
advantage of mix using both is ease of tracing status change and ease of handling concurrency and consistent problems.
for better understanding,you can refer links: Repository Pattern with Entity Framework 4.1 and Parent/Child Relationships
and
https://softwareengineering.stackexchange.com/questions/263502/unit-of-work-concurrency-how-is-it-handled

Categories