Create some child files on create entity event - cuba-platform

I have entity named Products (name, description), and then entity named ProductPrices (Product, Rate, Price), and entity Rates.
I need to get all the rates available when I create a product, and create the entries in ProductPrices for editing prices, and editing prices on the same screen (same grid) not using editor screen of ProductPrices.
On same Question, when I add a new Rate, I need to create all records in all available products.
How can I do this step in my test project ?

In the data model, I've marked the collection of ProductPrice in Product as #Composition. It means that product prices will be edited only as part of a product.
public class Product extends StandardEntity {
#Column(name = "NAME")
protected String name;
#Column(name = "DESCRIPTION")
protected String description;
#OrderBy("orderNo")
#Composition
#OnDelete(DeletePolicy.CASCADE)
#OneToMany(mappedBy = "product")
protected List<ProductPrice> prices;
The ProductPrice entity has an invisible attribute orderNo for proper sorting in the table.
For automatic adding product prices when a new product is created, I implemented the initNewItem() method in the product editor:
public class ProductEdit extends AbstractEditor<Product> {
#Inject
private Metadata metadata;
#Inject
private DataManager dataManager;
#Override
protected void initNewItem(Product item) {
item.setPrices(new ArrayList<>());
List<Rate> rates = dataManager.loadList(
LoadContext.create(Rate.class).setQuery(
LoadContext.createQuery("select r from products$Rate r order by r.name")));
int i = 0;
for (Rate rate : rates) {
ProductPrice productPrice = metadata.create(ProductPrice.class);
productPrice.setProduct(item);
productPrice.setRate(rate);
productPrice.setPrice(BigDecimal.ZERO);
productPrice.setOrderNo(i++);
item.getPrices().add(productPrice);
}
}
}
In order to edit prices inline, I've set editable="true" for table and its price column:
<table id="pricesTable"
editable="true"
height="200px"
width="100%">
<columns>
<column id="rate"/>
<column id="price"
editable="true"/>
</columns>
Your requirement of creating corresponding product prices for all products when a new Rate is added can be implemented by an entity listener:
#Component("products_RateEntityListener")
public class RateEntityListener implements BeforeInsertEntityListener<Rate> {
#Inject
private Persistence persistence;
#Inject
private Metadata metadata;
#Override
public void onBeforeInsert(Rate entity) {
TypedQuery<Product> query = persistence.getEntityManager().createQuery("select p from products$Product p", Product.class);
List<Product> products = query.getResultList();
for (Product product : products) {
ProductPrice productPrice = metadata.create(ProductPrice.class);
productPrice.setProduct(product);
productPrice.setRate(entity);
productPrice.setPrice(BigDecimal.ZERO);
Integer maxOrder = product.getPrices().stream()
.map(ProductPrice::getOrderNo)
.max(Integer::compareTo)
.orElse(0);
productPrice.setOrderNo(maxOrder + 1);
persistence.getEntityManager().persist(productPrice);
}
}
}
The working sample project is available here.

i've attached a Screenshot of my Screen. For me is OK. Problem is when i'm creating a new producto, we need to insert in their Prices all available Rates in Table Rates.
Screen Created
Entity Product
When i create a new RAte, i need to add in ProductPrices a value 0 for all existing products.
When i create a new Product i need to add in ProductPrices a value 0 for all available rates.

Other Captures
Entity ProductPrices
Entity Rates

Related

DDD, Aggregate Root and entities in library application scenario

I'm building a library application. Let's assume that we have a requirement to let registered people in the library to borrow a book for some default period of time (4 weeks).
I started to model my domain with an AggregateRoot called Loan with code below:
public class Loan : AggregateRoot<long>
{
public static int DefaultLoanPeriodInDays = 30;
private readonly long _bookId;
private readonly long _userId;
private readonly DateTime _endDate;
private bool _active;
private Book _book;
private RegisteredLibraryUser _user;
public Book Book => _book;
public RegisteredLibraryUser User => _user;
public DateTime EndDate => _endDate;
public bool Active => _active;
private Loan(long bookId, long userId, DateTime endDate)
{
_bookId = bookId;
_userId = userId;
_endDate = endDate;
_active = true;
}
public static Loan Create(long bookId, long userId)
{
var endDate = DateTime.UtcNow.AddDays(DefaultLoanPeriodInDays);
var loan = new Loan(bookId, userId, endDate);
loan.Book.Borrow();
loan.AddDomainEvent(new LoanCreatedEvent(bookId, userId, endDate));
return loan;
}
public void EndLoan()
{
if (!Active)
throw new LoanNotActiveException(Id);
_active = false;
_book.Return();
AddDomainEvent(new LoanFinishedEvent(Id));
}
}
And my Book entity looks like this:
public class Book : Entity<long>
{
private BookInformation _bookInformation;
private bool _inStock;
public BookInformation BookInformation => _bookInformation;
public bool InStock => _inStock;
private Book(BookInformation bookInformation)
{
_bookInformation = bookInformation;
_inStock = true;
}
public static Book Create(string title, string author, string subject, string isbn)
{
var bookInformation = new BookInformation(title, author, subject, isbn);
var book = new Book(bookInformation);
book.AddDomainEvent(new BookCreatedEvent(bookInformation));
return book;
}
public void Borrow()
{
if (!InStock)
throw new BookAlreadyBorrowedException();
_inStock = false;
AddDomainEvent(new BookBorrowedEvent(Id));
}
public void Return()
{
if (InStock)
throw new BookNotBorrowedException(Id);
_inStock = true;
AddDomainEvent(new BookReturnedBackEvent(Id, DateTime.UtcNow));
}
}
As you can see I'm using a static factory method for creating my Loan aggregate root where I'm passing an identity of the borrowing book and the user identity who is going to borrow it. Should I pass here the references to these objects (book and user) instead of ids? Which approach is better? As you can see my Book entity has also a property which indicates the availability of a book (InStock property). Should I update this property in the next use-case, for example in the handler of LoadCreatedEvent? Or should it be updated here within my AggregateRoot? If it should be updated here inside my aggregate I should pass the entire book reference instead of just an ID to be able to call it's method _book.Borrow().
I'm stuck at this point because I would like to do it pretty correct with the DDD approach. Or am I starting to do it from the wrong side and I'm missing something or thinking in a wrong way of it?
DomainEvents are in-memory events that are handled within the same domain.
You commit or rollback the entire "Transaction" together. Consider Domain Event as a DTO, which needs to hold all the information related to what just happened in the domain. So, as long as you have that information I do not think it matters if you pass Id, or the entire object.
I would go for passing the id in the domain event though as that information is sufficient to pass on the information to the DomainEventHandler.
Also, refer to this example of a similar scenario in Microsoft Docs, where they only pass UserId and CardTypeId along with all the other relevant information in the Domain event.
public class OrderStartedDomainEvent : INotification {
public string UserId { get; }
public int CardTypeId { get; }
public string CardNumber { get; }
public string CardSecurityNumber { get; }
public string CardHolderName { get; }
public DateTime CardExpiration { get; }
public Order Order { get; }
public OrderStartedDomainEvent(Order order,
int cardTypeId, string cardNumber,
string cardSecurityNumber, string cardHolderName,
DateTime cardExpiration)
{
Order = order;
CardTypeId = cardTypeId;
CardNumber = cardNumber;
CardSecurityNumber = cardSecurityNumber;
CardHolderName = cardHolderName;
CardExpiration = cardExpiration;
} }
There are a couple of things that look suspicious in your sample code:
The first one, Loan does the following:
loan.Book.Borrow();
but it doesn't have a reference to Book and, at first sight, it doesn't seem it should either.
The second one, your Book entity seems to have many responsibilities: hold book information like Author, title, subject, hold stock information, and manage the Borrowing state. This is far too many responsibilities for an aggregate, let alone for an entity within an aggregate. Which also begs the question, does a Book really belong to a Loan? it seems strange.
I would recommend, rethinking your aggregates and try to give them a single purpose. What follows is purely as an example on the type of thinking that you could do, not a proposed design:
First, it makes sense to have a Book somewhere, which holds the book information. You can manage book information and Author information completely independent from the rest of the system. In fact, this part would look pretty much the same for a book store, a library, a publishing company, and an e-commerce site.
As you are modeling a Library, it probably makes sense to have something like LibraryItem (domain experts will know the right word for this). This item might have a type (book, DVD, magazine, etc) and the id of the actual item, but it doesn't care about the Title, Description, etc. with the Id is enough. Potentially, it also stores the location/sorting of the item with the library. Also, this Item might have some sort of Status, let's say Active/Retired. If it's Active, the item exists in the Library. If it's Retired, it doesn't exist anymore. If you have multiple items of the same book, you'll simply create more Items with the same BookId and if it's possible to identify the concrete physical book, with a bar code, for example, each Item will have that unique code, so you can find it by scanning the bar code.
Now a Loan, to me, it's basically an ItemId plus a CustomerId (not sure if they are called customers in this domain). Every time a Customer wants to borrow an Item, the user will find the Item (maybe scanning the bar code), and find the Customer. At this point you have to create the Loan with the CustomerId, ItemId, date and not a lot more I would say. This could be an aggregate on itself or simply be managed by the Item. Depending on what you chose the implementation will vary obviously. Note that you don't reuse Loans. You'll have somewhere a list of Loans that you can query and this list won't be inside the Item aggregate. This aggregate only needs to make sure that 2 loans of the same item are not allowed at the same time.
Well, that was a rather long preliminary explanation to answer your question: you need to manage the InStock in the same aggregate that allows you to Borrow a book. It has to be transactional, because you want to ensure that a book is not borrowed multiple times at once. But instead of passing one aggregate to the other, design your aggregates so that they have the right data and responsibilities together.

.Net core Razor Pages add many child rows

How would I handle a model with many children in a razor page Create page?
I have the following models:
public class Invoice
{
public string ClientName;
public DateTime InvoiceDate;
public List<InvoiceItem> InvoiceItems;
}
public class InvoiceItem
{
public string ProductName;
public decimal Qty;
public decimal UnitPrice;
}
I want to add a button in my Invoice Create page that says "Add new item". I will then add (using ajax) a new line on the form with a new InvoiceItem - this I know how to do.
What I don't know is how to set up everything so all the invoice lines will get recognized by the OnPostAsync() method and saved in the database together with the Invoice.
Any pointers or examples would be appreciated.

Unwanted loading of relations in EF Core

Hello,
I have problem with EF Core feature - It automatically binds related entities together when the entities are somewhere independently attached to current dbCotnext.
Let's assume following two entities:
public class Seller {
public Guid Id {get;set;}
public List<Product> Products {get;set;}
}
public class Product {
public Guid Id {get;set;}
public Guid SellerId {get;set;}
public Seller Seller {get;set;}
}
And some code in the controller (just for imagination):
var seller = DbContext.Sellers.FirstOrDefault(e => e.Id == someId);
var products = DbContext.Products.All(t => t.SellerId == someId);
return StatusCode(200, products);
The returned JSON will be like
[
{
"id": "1234",
"sellerId": "5678",
"seller": {
"id" : "5678",
"products": ["(muted reference loop exception from json converter here.)"]
}
}
]
But I don't want the Seller to be included in each Product. If I did, I'd call Products.Include(...) for that or something else.
I don't want to crawl through entities and null the navigation properties.
I don't want to hide it with [JsonIgnore] because sometimes the relation must be included.
I also don't want to manually detach every entity all the time when this happens.
The question is, is there any way to disable or work around this behaviour?
Thanks
No, you can't/shouldn't. You need separate dto class(es).
Newtonsoft.Json is responsible for object serialization, it decides which properties must [not] be serialized. You can control it's behavior only using it's attributes. You can't control it from EF :)
And as soon as you wish sometimes to include property and sometimes not - you need two different classes (each with correct attributes). Everything other is a hack. DTO, Automapper and all this stuff - you are welcome.
BTW, having different class(es) for external API and internal data storage allows you to easily change one without breaking other (in future).
Have you tried this configuration on Startup class:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc().AddJsonOptions(a => a.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
// other code
}
You have to change model class and using nullable type.
public class Product {
public Guid Id {get;set;}
public Guid SellerId {get;set;}
public Seller? Seller {get;set;}
}

Mvc rules and concepts

A view can have only one #model Folder.ModelName but what if I want to show data from two different tables in one view? How should I do this and what is the right way? For example, if I want to select all profits of the current user and to select all costs of the current user in the one view, then obviously this is 2 tables with 2 different models. Basically my question is, where can I find some rules and concepts for the asp.net mvc pattern?
You would create something known as ViewModel. ViewModel can contain one or more entities, methods, additional fields, etc. You then pass this ViewModel to your View. For example,
namespace Sample
{
public class ProfitCostViewModel
{
public Profit Profit { get; set; }
public Cost Cost { get; set; }
public decimal DoSomeCalculations()
{
// Do something
}
}
}
In your Action, create an instance of this ViewModel class and initialize properties Profit and Cost. Then pass this object to the View. Inside of your view your can declare model like:
#model Sample.ProfitCostViewModel
and use it as
<p>Current profit or something: #Model.Profit.SomeProfitProperty</p>
<p>Sample cost: #Model.Profit.SomeCostProperty</p>
And that's how you would pass two or more entities as a model to your view.
UPDATE: You action could be something like:
public ActionResult YourAction()
{
var profitCostVm = new ProfitCostViewModel();
profitCostVm.Profit = LoadProfitFromSomewhere();
profitCostCm.Cost = LoadCostFromSomewhere();
return View(profitCostCm);
}

Is there something analogous on NHibernate regarding Entity Framework's navigation property?

Is there something analogous on NHibernate regarding Entity Framework's navigation property? For example, instead of:
s.Save(new Product { Category = s.Get<Category>("FD"), Name = "Pizza" });
I wish I could write:
s.Save(new Product { CategoryId = "FD", Name = "Pizza" });
Can I inform NHibernate not to use the Product's Category property as a mechanism to save the Product's category? I want to use CategoryId instead(Read: I don't want to use DTO). Entity Framework seems able to facilitate avoiding DTO patterns altogether, while at the same time offering the full benefit of ORM(can avoid joins using navigation properties). I want the EF's offering the best of both worlds(lean mechanism for saving objects, i.e. no need to retrieve the property's object) and navigation mechanism for querying stuff
Sample from EF: http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-code-first-walkthrough.aspx
public class Category
{
public virtual string CategoryId { get; set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
}
public class Product
{
public virtual int ProductId { get; set; }
public virtual string Name { get; set; }
public virtual string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
[UPDATE]
Regarding James answer, I tried seeing the NHibernate's actions in SQL Server Profiler.
// this act didn't hit the Category table from the database
var c = s.Load<Category>("FD");
// neither this hit the Category table from the database
var px = new Product { Category = c, Name = "Pizza" };
// this too, neither hit the Category table from the database
s.Save(px);
Only when you actually access the Category object that NHibernate will hit the database
Console.WriteLine("{0} {1}", c.CategoryId, c.Name);
If I understand your question, you want to save a Product with a Category without hitting the database to load the Category object. NHibernate absolutely supports this and you almost have the right code. Here is how you do it in NHibernate:
s.Save(new Product { Category = s.Load<Category>("FD"), Name = "Pizza" });
This will not hit the database to fetch the actual Category, but it will simply save a Product with the correct Category.Id. Note that you don't need (and I would recommend getting rid of Product.CategoryId).
Now why does this work with session.Load(), but not session.Get()... With session.Get(), NHibernate has to return the object or null. In .NET, there is no way for an object to replace itself with null after the fact. So NHibernate is forced to go to the database (or L1 cache) to verify that the "FD" Category actually exists. If it exists, it returns an object. If not, it must return null.
Let's look at session.Load(). If the object is not present in the database, it throws an exception. So NHibernate can return a proxy object from session.Load() and delay actually hitting the database. When you actually access the object, NHibernate will check the database and can throw an exception at that point if the object doesn't exist. In this case, we're saving a Product to the database. All NHibernate needs is the Category's PK, which it has in the proxy. So it doesn't have to query the database for the Category object. NHibernate never actually needs to hydrate an actual Category object to satisfy the save request.