Code Monkey home page Code Monkey logo

Comments (22)

Boriszn avatar Boriszn commented on July 19, 2024 1

As I understood, in first example you are using 1 transaction, right ?

So one transaction in current solution may look like:

// 1.  Starts the transaction for Device and DeviceDetail object

 var deviceRepository = unitOfWork.GetRepository<Device>();
 var deviceDeviceDetailRepository = unitOfWork.GetRepository<DeviceDetail>();

// Get device
Device device = deviceRepository.Get(333);

if (device == null)
{
      // 2. Will rollback transaction if exception 

      throw new NullReferenceException();
}

// updates the device 
 device.DeviceTitle = deviceViewModel.Title;

 deviceRepository.Update(device);

// adds new deviceDetail 
deviceDeviceDetailRepository.Add(
   new DeviceDetail { Description = "New description" } )

 // 3. Commit transaction (all changes in Device and DeviceDetail  will be moved in database)
unitOfWork.Commit();

Here I update Device entity and added new DeviceDetail entity with only one commit.
How it may look like in SQL transactions:

BEGIN TRANSACTION;  

BEGIN TRY  

    UPDATE  Device ....
    INSERT INTO DeviceDetails  ....

END TRY  
BEGIN CATCH  

    IF @@TRANCOUNT > 0  
        ROLLBACK TRANSACTION;  

END CATCH;  

IF @@TRANCOUNT > 0  
    COMMIT TRANSACTION;  
GO 

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024 1

In another example you are using 2...n transaction, right ?

Then you should use 2..n commits, and you can even check, if data was updated before first commit, with simple check: if(unitOfWork.Commit() != 0) { // execute another transaction code }

You can also split Commit(dbContext.SaveChanges) with add/update/delete Repository methods, and remove UoW. But I do not recommend to do it. (Perhaps, in projects with legacy code)
Also, I've recommend you to read about UnitOfWork pattern itself (like: martinfowler-UoW )

Maybe you can provide/share you code examples, etc ?

Best regards,
Boris

from devicemanager.api.

arunprasathv avatar arunprasathv commented on July 19, 2024 1

@Boriszn Thanks for the update. Have tested UoW dispose before, it worked fine for querying.

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024

Hi Ali,

Thank you a lot for the positive feedback.

Simple answer: No, you don't need any transaction mechanism.
UoW basically represents transactions.

Let me explain a bit how it works:

  1. When you execute add, update or delete (methods from Repository) entity framework just adds this changes to tracking log Tracking log, in memory. (You can see this changes in db context, tracking entities etc ...)
  2. When you execute Commit() EF will take all changes from TrackingLog (in memory) and executes them as queries to Database. So, only, after this action, the Database will be updated.
  3. In case if exception (somewhere in Business Logic/ Services) will be thrown UoW dispose all tracking changes (and DB context itself) So this is basically Rollback mechanism.

(You can test it, just put throw new Exception('test') somewhere in service, and breakpoints in UoW Commit/dispose methods)

(However, you may need some transactions in case if you need to switch between different databases like MS SQL, Oracle or MySql, in real-time and you using different EF providers)

Please let me know if you have more questions.

Regards,
Boris

from devicemanager.api.

AliCharper avatar AliCharper commented on July 19, 2024

Thanks, but I still have had some issues for Nested Entities, because the record in Parent table should be inserted at first so I need two Commit and this means a big problem for me. I need a mechanism to be able to ADD/Update/Delete in one shoot even for Nested scenarios.

Regards
Ali

from devicemanager.api.

AliCharper avatar AliCharper commented on July 19, 2024

Another thing is :
Sometimes, I should make sure about inserting the first record in a Table and then start adding another one in the second table. In such this cases, I am paralyzed or if I need the Identity number which DB generates after inserting the record.

from devicemanager.api.

arunprasathv avatar arunprasathv commented on July 19, 2024

@Boriszn
It's a great Article. I've got a use case where I need to join multiple tables (5+) to get the final result. How would you do that in this architecture and will it be scalable? Will there be any performance impact in this approach?

from devicemanager.api.

AliCharper avatar AliCharper commented on July 19, 2024

Thanks alot. Due to Iranian new year, I have been busy from yesterday. I will have a look tomorrow.

from devicemanager.api.

AliCharper avatar AliCharper commented on July 19, 2024

I think your first example is 100% enough for any kind of scenarios , but let me check it on a real scenario. Will update you all asap.

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024

@arunprasathv Thanks a lot. Basically, Yes this is possible.
Example:

// 1. Create repository
 var deviceRepository = unitOfWork.GetRepository<Device>();
 var deviceDeviceDetailRepository = unitOfWork.GetRepository<DeviceDetail>();

 // 2. Get all devices and deviceDetails
 IQueryable<Device> devices = deviceRepository.GetAll();
 IQueryable<DeviceDetails> deviceDetails = deviceDetailRepository .GetAll();

 // 3. Join 2 tables, uses device as a result model
var deviceWithDetails =
               from device in devices 
               join deviceDetail in deviceDetails  on device.Id equals deviceDetail.DeviceId
               where device.Title.Contains('RaspberryPi3')
               select device;

List<Devices> deviceWithDetailsList = deviceWithDetails.ToList();

However if you have to deal with 5+ joins this means that you have some issues in application architecture, you may need to think how to simplify it or even move to NoSql database.

  • Performance: Generally, it will be fast according to my example. But it depends on query complexity, expressions, aggregations, sub-queries you use alongside with joins, database (MsSql, MySql) and providers.
    I would recommend you create SQL query separately, perhaps in Sql Management studio (to be able to debug and see execution plan) before.
  • Scalability: Architecture itself is scalable so what you need is create additional tenant shard/database (horizontal scale). In case if you need deploy application as single node and use clusters (Micro-service architecture) then you need just add application do Docker (I have plans to add Docker support) or Microsoft Service Fabric. Or you meant Joins solution ?

Would be nice if can provide some example and more details.

@AliCharper Sure, let me know if you have more questions.

from devicemanager.api.

arunprasathv avatar arunprasathv commented on July 19, 2024

@Boriszn Thanks for the reply.

I do agree with you but the application was designed badly and we inherited it from a different team that developed a few years ago. I'm trying to optimize the performance, its relational in nature, like the customer, invoice, order entities with one to many,many to many. Without considering No SQL im trying to fix the problem. I've worked in No SQL too and the performance is better as there's no normalization and no need to worry about the index. let me know your thoughts.

Have a question regarding deviceRepository.GetAll();

  1. Does it happen on the client side, does it get all records from table like a million rows to the client side and apply LINQ JOIN?
  2. Is there any reason why we need to have UOW and repository pattern when EF Core itself provides that abstraction with DBContext and DBSet.

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024

Hi @arunprasathv.

Answers:

  1. No, it doesn't, it just says to db what table it should use in query. (and it returns IQueriable) Basically, my previous example just building SQL query where only line: deviceWithDetails.ToList(); executes this query.
  2. In general, we need Repository to remove duplication code (DRY), separate your data access logic from business logic, etc. UnitOfWork - represents transaction (object life cycle). Avoid using this patterns you may end-up with a lot of boilerplate code, duplication, etc.

In addition to my answer:

  • If you have a lot of complex joins a may need consider to use some MicroORM (like Dapper, OrmLite etc..) alongside with Entity Framework. (B/c building complex joins in LINQ to Objects maybe painful ;))
  • Build, test and improve queries on DB side before you build them with EF or other ORM. (in this case you can find issues which does not related to code but to database itself)
  • Always check what SQL query EF generates.

from devicemanager.api.

arunprasathv avatar arunprasathv commented on July 19, 2024

@Boriszn Thanks for the update.

  1. Also, I noticed the context is being created and not disposed of for every action, for example, latest DB updates for devices will not be fetched since the context isn't disposed of. may be notrack bheavior should be added. UOW has dispose, not sure how it disposes of device context and when it does.

  2. Let's say I've 5 APIs, can I use the same context for all.

  3. All Management code should be moved to a common assembly so other apis like customer,order can also use it. have a common context.

Please provide your thoughts.

services.AddDbContext(options =>
options.UseSqlServer(connectionString));

        services.AddScoped<IDbContext, DeviceContext>();

from devicemanager.api.

arunprasathv avatar arunprasathv commented on July 19, 2024

@Boriszn
One More thing, I do not want to use contextfactory so will change UOW as below, I just need IDBContext. Not dealing with multi-tenant architecture for now. let me know your thoughts.

private IDbContext dbContext;

public UnitOfWork(IDbContext dbContext)
    {
        this.dbContext = dbContext;
    }

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024

Hi @arunprasathv. Thanks for the feedback.

Please find my answers below:

  1. This not really true, context disposed:
  • after UoW commit was called
  • after exception in method was thrown
  • after garbage collection executed (or GC.Collect() was called).

Here code part, in UnitOfWork.cs, which does this:

.....
        /// <summary>
        /// Disposes the current object
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);

            // ReSharper disable once GCSuppressFinalizeForTypeWithoutDestructor
            GC.SuppressFinalize(obj: this);
        }

        /// <summary>
        /// Disposes all external resources.
        /// </summary>
        /// <param name="disposing">The dispose indicator.</param>
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.dbContext != null)
                {
                    this.dbContext.Dispose();
                    this.dbContext = null;
                }
            }
}
....

You can set break point debug and see (test) how it works.

  1. Yes, if you gonna share context alongside with Entity Framework data models and migrations you should create assembly like YourNamespace.DataAcess and move all files from Data directory there.

  2. If you have different Entity Framework models for each API then you should create assembly only from Management directory (probably create separate solution and move this library to NuGet package and share it between your APIs as NuGet dependency).
    Be aware that all parts of this app can be easily separated to own assembly only if it necessary (if they will reused in another projects or assemblies etc.)

  3. @arunprasathv If you don't need multi tenancy please take a look on (or clone) this branch.. Everything you need, without multi tenancy feature.

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024

@arunprasathv Sure, your welcome.

Jus, let me know if you will have more questions. ;)

from devicemanager.api.

AliCharper avatar AliCharper commented on July 19, 2024

Dear Boriszn,

Can you please give us an example about using this platform in CQRS - Event sourcing. Then it would be more nice and useful.

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024

Hi @AliCharper

Sure, I can give you example how you can use this architecture with Event Sourcing or CQRS. However, please be aware that this topic is out of scope of this Repository (and issue as well) ;-)

Options:

  1. You can add additional layer (Event Query -> Handler) using Mediatr Like on 3th slide Here.
    Here the example repository. (You can watch the updates. So I will introduce using Data Access etc. Here the Project Board)
  2. Other option is using Message Bus as a middle-ware. You can base it on Azure Service Bus (RabbitMQ, Apache Kafka, ActiveMQ etc. ).
    Here the Example repository using Azure Service Bus.

Also, please take into account using following projects:

Hope it helps ;-)

Regards,
Boris

from devicemanager.api.

AliCharper avatar AliCharper commented on July 19, 2024

Thanks a lot.

I have seen some of the article you have mentioned. I am going to create a full BackEND based on CQRS-ES so the Repository inside the pattern would be what you have created as an UOW sample. Will share very soon once its done.

Regards
Ali

from devicemanager.api.

arunprasathv avatar arunprasathv commented on July 19, 2024

@AliCharper @Boriszn
Can't wait to see this implementation. Is there a specific use case when CQRS is a must ?

Scenarios: Back office system like billing should get data from different systems like Order Management, CRM, Customer Management etc... how would you gather data from all these systems and create Invoice finally. Data is being collected through ETL process (SSIS), can this be replaced with Message Bus, Kafka, Apache Nifi etc so we get data as it changes rather than waiting for a day.

I was thinking about CQRS as well, but it only helps in Service layer where angular calls for showing a page. Pls let me know otherwise.

What would be the best architecture you would consider for this system like this?

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024

@AliCharper
Be aware that CQRS is quite useful pattern however quite complex. (complexity in testing, understanding) before start implementing it I would recommend ask yourself: Do I really need it ? Is there other/easy way ? :)

@arunprasathv
Sure there is several option how to do this:

  1. Use task scheduler components like quartznet or HangfireIO Create a project/library/service (with listed components) which will work as middle ware and will constantly gather data and combine/create reports etc, from Order, Billing etc services.

  2. Yes :) Use some Message Bus. (RabbitMQ, ActiveMQ, Azure Service Bus) So Billing, CRM etc will send messages to bus and Report service will gather these messages and builds the reports etc.

p.s
What you think if I create Gitter/Slack chat channel, so we can move this conversation there ? And I can close this issue ?

So Here the link to the Gitter channel, now we can move this conversation there.

from devicemanager.api.

Boriszn avatar Boriszn commented on July 19, 2024

@AliCharper @arunprasathv Please be aware I've moved this conversation to the Gitter Room: https://gitter.im/DeviceManager-Api/Lobby. Feel free to join ;)

from devicemanager.api.

Related Issues (11)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.