Comments (10)
Can you make up an example of the problem? Pseudocode is totally OK. At the moment I assume you do explicit Migrations. The Migrations should go into a common project, that's shared between all projects. So if you try to create a table with the same Table Name for example, the Migration should fail and as thus the mapping can't be broken this way.
from bytefish.de.
Currently I'm not using your ModularEfCore implementation but an in-house produced similiar setup.
I ran into a problem regarding mapping and the explicit migration created.
In the main project there's an entity, which mapping is handled there, and in a module I have another entity that needs to have a relation to that entity.
So the setup is as follows. In the main project
namespace MainProject.Data.Models
{
public class MainEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string PropertyNotUsedInModule { get; set; }
public MyModule.Data.Models.MainEntity MyModuleMainEntity { get; set; }
}
}
// relevant mapping:
modelBuilder.Entity<MainEntity>(entity =>
{
// one to one relation to MyModule MainEntity
entity.HasOne(e => e.MyModuleMainEntity)
.WithOne()
.HasForeignKey<MyModule.Data.Models.MainEntity>(e => e.Id);
});
And in the module:
namespace MyModule.Data.Models
{
public class ModuleEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int MainEntityId { get; set; }
public MainEntity MainEntity { get; set; }
}
public class MainEntity
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ModuleEntity> ModuleEntities { get; set; }
}
}
When adding a migration all goes well except for the column 'Name' that is used in both the main project and the module.
Next to the column 'Name' being created in the table, there is also a column 'MainEntity_Name' being created.
I was hoping that the setup used by you has something in place for this 'sharing' of entities between modules and between the main project and a module.
from bytefish.de.
What about making sure each entity maps to its own table? You didn't define the table name and your classes have the same name. My guess is, that Entity Framework Core by Convention uses the Class Name as Table Name. Did you try to Map them to different tables?
from bytefish.de.
How to prevent this? Abstract the mapping like I did in my post, then make sure the constructor takes a Schema name and a Table name. At one point iterate over the mappings and throw an Exception if you find two mappings the same names.
from bytefish.de.
Let me first express my gratitude and appreciation for taking the time to respond to my questions. Thank you!
I left out some of the mapping details for brevity. The 'MainEntity' classes both are mapped to the same database table, that is correct. The one-on-one relationship between the two ensures the primary key is shared and stored in the same column. But apparently EF migrations doesn't seem able to map the identical 'Name' properties to the same column in the database.
Perhaps it's better to move away from my example and my code, since I'm not using ModularEfCore (yet ;-)). How about the following example.
A customer relationship management module (CRM) and a shipment module both have an 'Customer' entity, which originates in the core module.
The 'Customer' in the core module has basic properties to support loggin in to the applicatie, like a username, password and first/last name.
The 'Customer' in the CRM module has additional properties for contact like phone number and e-mailaddress, as well as a log of correspondence with the customer which is linked to the 'Customer' entity.
The 'Customer' in the shipment module has additional properties for shipment like address and billing information, as well as a history of orders and shipments of the customer.
How is this handled in ModularEfCore? Partial classes aren't an option since the modules define their classes in their own assemblies.
Where are the extra poperties put and where is the mapping located? The migrations are probably created in the main (core) project.
There's no need to create a working example if that takes more time then just globally indicating where things are put and a description of how things work.
from bytefish.de.
@MBroertjes No worries.
This is also an interesting topic for me, because I experiment with the best architecture for mid- / large-scale monolithic projects. What I always do is to separate the Domain Model and the Mappings completely. I never use Attributes, but do everything in a separate class handling the mapping.
What I have learnt through many projects is, that it's never good to add too much magic to a system.
Let's say you have a class BaseEntity
:
public abstract class BaseEntity {
public int Id { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
}
Your other C# classes derive from this BaseEntity
, because they should all be audited.
And from this point on: Don't overthink it.
In all honesty: I always rewrite the complete database mapping for all these classes from zero. Because maybe you want a Primary Key of one table to have a special name? Maybe a Primary Key of one table should be auto incremented, but the other shouldn't? Maybe I need a Composite Primary Key and my abstraction breaks there? And so on, and so on. Maybe you find your own edge cases.
I am pretty sure you can come up with a base class BaseEntityMapping
, that does these general mappings on the EntityTypeBuilder<TEntityType>
for you... like this:
public abstract class BaseEntityMap<TEntityType>
where TEntityType : BaseEntity
{
public void Map(ModelBuilder builder)
{
var builder = builder.Entity<TEntityType>();
// Apply the General Mappings:
builder
.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasColumnName("Id")
.ValueGeneratedOnAdd();
builder
.Property(x => x.CreatedOn)
.HasColumnName("CreatedOn");
builder
.Property(x => x.CreatedBy)
.HasColumnName("CreatedBy");
// Apply the Module-specific mappings:
InternalMap(builder);
}
protected abstract void InternalMap(EntityTypeBuilder<TEntityType> builder);
}
Now all your other BaseEntity
implementations would need to implement the above BaseEntityMapping
for the mapping, which maps the general properties and all additional properties would be mapped by the module-specific implementations.
But like I said... I would just rewrite the above for all entities. Because if you tackle the problem with such a simple approach, there won't be a problem for EntityFramework Core to generate the migrations. All this comes at the cost of duplication. But duplication is better, than the wrong abstraction.
from bytefish.de.
I am closing the issue. But feel free to discuss the issue further. 😎
from bytefish.de.
I see the elegance of the code in your reply, but it doesn't add anything meaningfull of on the topic of shared entities in different assemblies.
That is what 'ModularEfCore' is designed for, isn't it?
from bytefish.de.
Hm, then I don't understand your use case correctly, sorry. 🤔
from bytefish.de.
Okay, thanks anyway.
from bytefish.de.
Related Issues (20)
- TransactionalTestBase package? HOT 1
- Thank you for the excellent article. Unfortunately, it did not work for me. HOT 2
- Question: Usage of bytea HOT 1
- Sql minor bug HOT 1
- Auto Configuere data source in data base per tenant HOT 1
- Spring Boot Multitenancy guide - Error with AbstractRoutingDataSource HOT 3
- Query on spring abstractroutingdatasource advantage HOT 2
- Question on dynamic load of the data source HOT 10
- Add a flow diagram HOT 7
- NEST not working HOT 1
- Adding null in AuditQueryResultUtil HOT 3
- How the tenant can be changed on runtime HOT 1
- Can't write CLR type System.DateTime to database type timestamp with time zone HOT 1
- AbstractMap used by PersonMap in SqlMapper blog entry doesn't match java.util.AbstractMap HOT 7
- Update async multi-tenant example HOT 3
- Select tenant HOT 1
- Subject: Parsing Command Line Arguments in .NET || Topic: Add basic arg validation HOT 2
- How to addnew datasource without @Scheduled HOT 1
- How can we do the reverting to a particular revision using Versioning and Auditing with spring data Envers? please provider me an example for reverting HOT 2
- Tables are not getting created while dynamically tenant details in tenant.json HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from bytefish.de.