I'm relatively new to programming so here is my question.
I have a C#-Forms application and an access database.
In the database I have the data of about 200-300 cars (Name, Year of construction, ...)
In my forms application i show all the cars in a list and i have a filter where I can search for specific words and types and so on.
At the moment I react to any filter input and then execute a new sql-query and list all the cars that fit the filter.
I obviously don't think that's a good solution because i have a database access every keyDown action.
Is it a viable way to create a car-class and create an instance of this class for every car and store them in a list?
What is the best way to handle all the 200 cars without reading them out of the database over and over again?
If your car data is not changing frequantly than you can store in memory data and after filter on that.
When new record is added you need to update in memory data.
the following code may help, although you should fill the blanks.
class CarDA
{
public const string YOUR_SPECIFIC_CACHE_KEY = "YOUR_SPECIFIC_CACHE_KEY";
public IList<Car> Search(string searchExpression)
{
var carList = ListAllCars();
//AMK: do the math and do the filter
return Filtered_Car_List;
}
private IList<Car> ListAllCars()
{
var ExpireTime = 10;
if (!MemoryCache.Default.Contains(YOUR_SPECIFIC_CACHE_KEY))
{
MemoryCache.Default.Add(
new CacheItem(YOUR_SPECIFIC_CACHE_KEY, PopulateCarList()),
new CacheItemPolicy
{
AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(ExpireTime)
});
}
return MemoryCache.Default.Get(YOUR_SPECIFIC_CACHE_KEY) as IList<Car>;
}
private IList<Car> PopulateCarList()
{
//AMK: fetch car list from db and create a list
return new List<Car>();
}
}
class Car
{
public string Name { get; set; }
}
Seems more a philosophy question than a technical one, so I'd like to give you my 2 cents on this.
If the Cars database is modified only by your application and the application is run by a single person, you can read the data once and set a mechanism in your UI to reaload the in memory data when through the UI you update the database.
If you have multiple users using the data and updating the database, so that one user can add cars to the database while another is reading it to perform some operation on them, you have to prepare a mechanism to verify if data have been modified by someone else in the system to all users so that they can reload the cars list. (it can be something like a temporized query to a table where you memorize last date and time of update of the cars table for example, or it can be something more sophisticated in different cases).
Said so, Usually when working with databases I prepare a DataProvider Class that 'speaks' with the database, creating, updating, deleting and querying data, a Class that represents the Table Row of my data (in your case a Car class) and The DataProvider returns a List to my User interface that I can Use As is, or, if needed I can move to an ObservableCollection if using it with WPF UI objects and the data can change, or I can create something like a UICar object that is a Car plus other properties related to its use inside the User Interface that can be more useful to provide actions and functionality to the user using your application.
When you have your Cars collection inside your application, Searching in memory data using a simple Linq query becomes rather simple to implement and much more effective than calling the database at any change in the search textbox.
Also, as a suggestion, to avoid querying (memory or db is the same) at any keystroke, set a timer (just a few milliseconds) before starting the query reset in the key event so that if the user is typing a word the query starts just when he/she stops typing.
HTH
Related
I'm creating ASP.NET Core application using Dapper as ORM.
What is the proper flow to saves objects into multiple tables?
In my app architecture I got standard web api controllers that invoke command/query handlers that calculate/invoke other services/repositories etc.
My entities/db tables are User, Order, Product. One of my CommandHandler create user, then calculate products, orders etc. I just wonder how to save these objects into database. I see 2 solutions:
1.) I create some kind of DTO for all the calculated stuff during command handler:
public class TestDto
{
public User User;
public IList<Orders> Orders;
}
calculate all the stuff, fill the DTO one by one, and then at the end of command handler invoke all repositories:
...using (var ts = new Transaction)
{
_userRepository.Save(dto.User);
_ordersRepository.Save(dto.Orders);
ts.Complete
}
etc..
2.) Create transaction per whole command handler, and save user immediately after calculating him in memory, then calculate orders and save them immediately as well, and the same with orders.
You're going to need at least one transaction regardless. You don't want to save an incomplete order. I say at least one because it depends on if your user can exist separately from the order.
You could create and save the user first in one transaction, then save the order in a second transaction. If anything goes wrong while saving the order, you still have the user committed to the database. With option #2, you could perform the two transactions within your command handler.
With option #1, I'd be concerned about introducing an extra object tying users and orders together. You may find times when you need to make changes to just the user or an order separately from each other. The TestDto wouldn't be helpful.
I read much about DDD, but I can't steel see how use it in a real life. I make some examples (with C# and Entity Framework) for things which I can't understand.
Add money for User. User has just amount of money.
Ok, It's simple. It's example of model User
class User
{
public decimal Balance {get; private set; }
public void AddMoney(decimal sum)
{
if(sum>0)
Balance+=sum;
}
}
But how can I use it?
Fetch user from database
Update user - performed by domain model
Save changes
So, the first question is where I should perform fetching and saving data from database (repository)? I can't do this inside my domain model.
User has history of transaction, not just simple sum
class User
{
public ICollection<Transaction> Transactions {get; private set; }
public void AddMoney(decimal sum)
{
if(sum>0)
Transactions.Add(new Transaction(sum));
}
}
In this case I should fetch user from database and then EF will add new Entity to collection. But it isn't efficient, more efficient is do something like this:
transactionsRepository.Add(new Transaction(sum, userId));
but it isn't DDD-way.
Get money from one user and transfer to another
In this case operation affects multiple models. Where I should put logic which works with multiple models? (Maybe this example isn't good enough).
Get users' current balance
User's balance is a sum of all transactions
decimal Balance() => transactionsRepository.Get().Sum(x=>x.TransactionSum);
In this case query contains logic - how I should fetch data to do something, not simple fetch\save entities like in other examples. Where I should place queries with logic? Get total balance, get last unread messages etc.
So, the first question is where I should perform fetching and saving data from database (repository)? I can't do this inside my domain model.
You do this in an Application service in a Layered architecture or a command handler in a CQRS architecture .
But it isn't efficient, more efficient is do something like this
It is more efficient but indeed not the DDD way. Aggregates should have no dependency to repository. They work only with state that is stored in memory. An application service is responsible fir loading and storing an aggregate.
Where I should put logic which works with multiple models?
In Sagas/Process managers.
Where I should place queries with logic?
It depends on what do you query.
If not using CQRS:
If you query data from an Aggregate in an Aggregate's method.
If you query a specific lists of Aggregates them you put that logic in the repository.
If using CQRS then any query is done on a read-model/projection.
I have a scenario in which I need some help.
Let us assume that there is a User who listens to some type of Music.
class User
{
public virtual List<UserMusicType> Music { get; set; }
}
public class UserMusicType
{
public int ID { get; set; }
public MusicType name { get; set; }
}
public class MusicType
{
public int ID { get; set; }
public string Name { get; set; }
}
There is a form where I am asking users to check/select all types of Music he listens to. He selects 3 types namely { Pop, Rock, and Electronic }
CASE 1:
Now I want to update the User Entity and insert these 3 new types. From my understanding, I need to first remove whatever MusicTypes for this users were saved in the Database then insert these new types again. Is it a correct approach? Removing all previous and Inserting new ones? Or any other way to do it?
CASE 2:
I am taking MusicType names as string of course. Now while updating the User Entity, I'll have to first fetch the MusicType.ID after that I'll be able to do this:
user.Music.Add(new UserMusicType() { ID = SOME_ID });
Is there a better approach for this case?
I'll be glad to have some replies from experienced people in EF. I want to learn if there is an efficient way of doing it. Or even if my approach/Models are totally wrong or could be improved.
First of all, you don't need the UserMusicType class, you can just declare the `User class as
class User
{
public virtual List<MusicType> Music { get; set; }
}
And entity framework will create a many to many relationship table in the database
As for the first question, it depends. If you use this relationship any where else, like payment or audit trail, then the best way would be to compare the posted values to the saved values, ex:
User selected Music 1, Music 2, Music 3 for the first time and saved, in this case the 3 records will be inserted.
User edited his selection and chose Music 1,Music 3,Music 4, in this case you will get the values submitted which is 1,3,4 and retrieve the values stored in the database which is 1,2,3
Then you will get the new values which are the items that exist in the new values but not in the old, in this case it will be 4
You will get the removed values, which exist in the old but not in the new, in this case it will be Music 2.
The rest can be ignored.
So, your query, will be add Music 4, remove Music 2.
If you don't depend on the relationship, then it is easier to just remove all user music and add the collection again.
As for the second part of your question, I assume you will display some chechboxes for the user, you should make the value for the checkbox control as the MusicType ID, and this is what will be posted to the backend and you can use it to link it to the user.
ex:
user.Music.Add(new MusicType{ID=[selected ID ]}
You should not depend on the music name
First question:
Actually, it is a personal preference. Because, wouldn't want to delete all rows which belongs to that user and then insert them. I would compare the collection which is posted from the form with the rows which is stored in the database. Then, delete those entities from the database which are not exist in the collection anymore. And, insert new ones. Even, you can update those entities which has modified some additional details.
By the way, you can easily achieve this with the newly released EntityGraphOperations for Entity Framework Code First. I am the author of this product. And I have published it in the github, code-project and nuget. With the help of InsertOrUpdateGraph method, it will automatically set your entities as Added or Modified. And with the help of DeleteMissingEntities method, you can delete those entities which exists in the database, but not in the current collection.
// This will set the state of the main entity and all of it's navigational
// properties as `Added` or `Modified`.
context.InsertOrUpdateGraph(user)
.After(entity =>
{
// And this will delete missing UserMusicType objects.
entity.HasCollection(p => p.Music)
.DeleteMissingEntities();
});
You can read my article on Code-project with a step-by-step demonstration and a sample project is ready for downloading.
Second question:
I don't know on which platform you are developing your application. But, generally I am storing such libraries as MusicType in a cache. And use DropDownList element for rendering all types. When user posts the form, I am getting values rather than names of the selected types. So, no additional work is required.
I am creating a site using EF database-first with ASP.NET MVC (I follow all of this tutorial: https://www.asp.net/mvc/overview/getting-started/database-first-development/creating-the-web-application) and everything is working ok.
The problem is that in the index view (the list of records) I want to remove one of the fields from the the entity. I don't just want it to be hidden in the views, but all together removed (the issue is that it holds a very big chunk of data, which make the loading of the list very slow).
On the "edit" view I do want to show and use all fields.
If I remove one field from the the entity, I get this error:
The entity type XXX is not part of the model for the current context.
on
return View(db.XXX.ToList());
What can I do?
There are a couple of ways you can go about this. The issue, at hand, is the .ToList().
If you just do db.XXX, then you will obtain an Enumerable, which, until it is enumerated, is just a set of instructions to grab data from the DB. Once you call .ToList() it will actually go and grab the data from the DB, which is the step that is taking so long.
The best thing to do, in my opinion is define a ViewModel that contains all of the fields except the one with the large amount of data.
public class ViewModel
{
public ViewModel(){}
public int Id {get;set;}
public string OtherData {get;set;}
}
Move the
db.XXX outside of the View function as so:
var initialDBObject = db.XXX;
From there you can take your Enumerable (which is still just a set of instructions to access data from your DB) and Select it into your ViewModel object like so:
var viewModelObject = initialDBObject.Select(x=> new ViewModel
{
Id = x.Id;
OtherData = x.OtherData;
//do not add the large column of data to the ViewModel
});
What is happening here (before calling ToList()) is you are modifying the query that linq generates behind the scenes to grab the data from table XXX (if you put a break-point on this line and hover over initialDBObject you will see the SQL that gets generated). Instead of just grabbing the data from table XXX, the query will grab the data and insert it into a ViewModel object (instead of the XXX object, as defined in your .edmx file) once you call ToList().
You can also
.Select(x=> new
{
Id,
OtherData
});
and create an anonymous object, but getting an anonymous object to work in a View is a little complicated.
Then you need to update the Index View page to use the ViewModel instead of the original DB object and you can pass it like:
View(viewModelObject.ToList());
Name the ViewModel something besides ViewModel, though. Like [DB Table Name]ViewModel or something similar.
If you have lots of rows in the DB it will still take a long time to load all of the data, in which case you need to look into Paging.
There is no problem with ToList(), on its own, the original issue was caused by the call to ToList() because it is at that point in the code that the program goes to the database and uses the linq-generated query to grab the data. If you are trying to ToList() an entire table's worth of data, or as in your case, have a column with a huge chunk of data, it may take some time or you may run out of memory.
Regarding the 15 columns that you have to include in the .Select(), yes, that is annoying. Unfortunately, you cannot use a constructor in a linq statement, so are forced to populate each column.
Another alternative to defining a ViewModel, which may be a little bit easier is to open up the .edmx design surface, right-click the background and Add New->Entity. You can use table XXX as a base, give it a different name, like XXXViewModel or whatever and then delete the column containing the large amount of data. Then you just need to do db.XXXViewModel.ToList().
I'm using CQRS + ES and I have a modeling problem that can't find a solution for.
You can skip the below and answer the generic question in the title: Where would you query data needed for business logic?
Sorry of it turned out to be a complex question, my mind is twisted at the moment!!!
Here's the problem:
I have users that are members of teams. It's a many to many relationship. Each user has an availability status per team.
Teams receive tickets, each with a certain load factor, that should be assigned to one of the team's members depending on their availability and total load.
First Issue, I need to query the list of users that are available in a team and select the one with the least load since he's the eligible for assignment.(to note that this is one of the cases, it might be a different query to run)
Second Issue, load factor of a ticket might change so i have to take that into consideration when calculating the total load per user . Noting that although ticket can belong to 1 team, the assignment should be based on the user total load and not his load per that team.
Currently a TicketReceivedEvent is received by this bounded context and i should trigger a workflow to assign that ticket to a user.
Possible Solutions:
The easiest way would be to queue events and sequentially send a command AssignTicketToUser and have a service query the read model for the user id, get the user and user.assignTicket(Ticket). Once TicketAssignedEvent is received, send the next assignment command. But it seems to be a red flag to query the read model from within the command handler! and a hassle to queue all these tickets!
Have a process manager per user with his availability/team and tickets assigned to that user. In that case we replace the query to the read side by a "process manager lookup" query and the command handler would call Ticket.AssignTo(User). The con is that i think too much business logic leaked outside the domain model specifically that we're pulling all the info/model from the User aggregate to make it available for querying
I'm inclined to go with the first solution, it seems easier to maintain, modify/extend and locate in code but maybe there's something i'm missing.
Always (well, 99.99% of cases) in the business/domain layer i.e in your "Command" part of CQRS. This means that your repositories should have methods for the specific queries and your persistence model should be 'queryable' enough for this purpose. This means you have to know more about the use cases of your Domain before deciding how to implement persistence.
Using a document db (mongodb, raven db or postgres) might make work easier. If you're stuck with a rdbms or a key value store, create querying tables i.e a read model for the write model, acting as an index :) (this assumes you're serializing objects). If you're storing things relationally with specific table schema for each entity type (huge overhead, you're complicating your life) then the information is easily queryable automatically.
Why can't you query the aggregates involved?
I took the liberty to rewrite the objective:
Assign team-ticket to user with the lowest total load.
Here we have a Ticket which should be able to calculate a standard load factor, a Team which knows its users, and a User which knows its total load and can accept new tickets:
Update: If it doesn't feel right to pass a repository to an aggregate, it can be wrapped in a service, in this case a locator. Doing it this way makes it easier to enforce that only one aggregate is updated at a time.
public void AssignTicketToUser(int teamId, int ticketId)
{
var ticket = repository.Get<Ticket>(ticketId);
var team = repository.Get<Team>(teamId);
var users = new UserLocator(repository);
var tickets = new TicketLocator(repository);
var user = team.GetUserWithLowestLoad(users, tickets);
user.AssignTicket(ticket);
repository.Save(user);
}
The idea is that the User is the only aggregate we update.
The Team will know its users:
public User GetGetUserWithLowestLoad(ILocateUsers users, ILocateTickets tickets)
{
User lowest = null;
foreach(var id in userIds)
{
var user = users.GetById(id);
if(user.IsLoadedLowerThan(lowest, tickets))
{
lowest = user;
}
}
return lowest;
}
Update: As a ticket may change load over time, the User needs to calculate its current load.
public bool IsLoadedLowerThan(User other, ILocateTickets tickets)
{
var load = CalculateLoad(tickets);
var otherLoad = other.CalculateLoad(tickets);
return load < otherLoad;
}
public int CalculateLoad(ILocateTickets tickets)
{
return assignedTicketIds
.Select(id => tickets.GetById(id))
.Sum(ticket.CalculateLoad());
}
The User then accepts the Ticket:
public void AssignTicket(Ticket ticket)
{
if(ticketIds.Contains(ticket.Id)) return;
Publish(new TicketAssignedToUser
{
UserId = id,
Ticket = new TicketLoad
{
Id = ticket.Id,
Load = ticket.CalculateLoad()
}
});
}
public void When(TicketAssignedToUser e)
{
ticketIds.Add(e.Ticket.Id);
totalLoad += e.Ticket.Load;
}
I would use a process manager / saga to update any other aggregate.
You can query the data you need in your application service. This seems to be similar to your first solution.
Usually, you keep your aggregates cross-referenced, so I am not quite sure where the first issue comes from. Each user should have a list of teams it belongs to and each group has the list of users. You can complement this data with any attributes you want, including, for example, availability. So, when you read your aggregate, you have the data directly available. Surely, you will have lots of data duplication, but this is very common.
In the event sourced model never domain repositories are able to provide any querying ability. AggregateSource by Yves Reynhout is a good reference, here is the IRepository interface there. You can easily see there is no "Query" method in this interface whatsoever.
There is also a similar question Domain queries in CQRS