Giter Club home page Giter Club logo

dddandefcore's People

Contributors

superjmn avatar vkhorikov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dddandefcore's Issues

Question: How to handle an invariant that references data outside the Aggregate Root? (EG: Unique Email)

Hi Vladimir,

I am looking for your opinion on how to handle an invariant such as "each Student's email must be unique".

If you move the Aggregate Root to be the full list of students, it could deliver poor performance, and would be a big change just to accommodate this rule.

Is this a rule that would have to be checked in the domain services or application services layer?

Any tips or ideas would be helpful

Thanks for the course!

Darren

Raising events while creating entity

Problem

I'm trying to understand of how to raise event while creating entity.
For example, you have next line:
RaiseDomainEvent(new StudentEmailChangedEvent(Id, email));
This will raise domain event for existing entity with it unique identifier.

Is it possible to do the same, but for entities that wasn't stored to DB? Lets say, I want to know when entity was created and for some reason I need his identifier. Probably attach ID at overload of SaveChanges method, or something like that. Which way is correct from DDD perspective for that simple task?

Thanks!

EF Core instance of entity type cannot be tracked

I've been following the approaches proposed in this repo and the associated pluralsight course, and they have worked really well, but recently been getting the following exception thrown by EFCore:

"The instance of entity type 'YourEntity' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.
When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values."

I have created a minimum example to demonstrate the issue (using EFCore inmemory and a Sqlite DB) here: https://github.com/robertlarkins/efcore-cannot-track-entity-issue

As far as I can determine it is related to static entities (such as Course in this repo, or Appointment Status in my linked repo), and it occurs when the context is holding an instance of AppointmentStatus pulled from the database, and the same AppointmentStatus is added to the context from the static AppointmentStatus reference in code. When trying to save the context to the database the above exception gets thrown.

This exception occurs on this line:

IEnumerable<EntityEntry> enumerationEntries = ChangeTracker.Entries()
                .Where(x => EnumerationTypes.Contains(x.Entity.GetType()));

so it never gets to the point of trying to set the EntityState as unchanged.

Is this an issue you have come across before? I couldn't find how this repo stops this occurring, hopefully I have just missed something, but if not, then I assume it could potentially happen within this repo as well. Alternatively should this issue be presented on the EFCore issue tracker?

EF Core backing field not returning data from database

In the Student.Disenroll method, it directly accesses the _enrollments backing field, but in EF Core 3.1.4 when I try to access the backing field of an IReadOnlyList it doesn't trigger the loading of the data from the database. It appears I have to call the property rather than the backing field for this to happen. I assume this is a recent change in EF Core? So I'm wondering how the Disenroll method should be modified so it can remove an item from the _enrollments backing field without first accessing the Enrollments property to cause EF Core to populate _enrollments.

Here's the same issue posted on StackOverflow (by somebody else): https://stackoverflow.com/questions/63388035/ef-core-3-1-navigation-property-doesnt-lazy-load-entities-when-calling-the-bac
The provided answer (and comments) don't seem to match with your recommendations, so I'm hoping there is a better way.

Issue updating the foreign key for Many to One relation ship

Hey @vkhorikov , great course. I am hitting a snag while updating the foreign key. I have posted the question on Stack overflow as well here: Many to one foreign key null

Relation: Many products can belong to the same category. Have simplified the domain models to focus on the issue

EfCore: 5.0

Product Model

public class Product: Entity, IAggregateRoot
{
    protected Product() { }

    public Product(string name, Category category): this()
    {
        Name = name;
        Category = category;
    }

    public string Name { get; }
    public virtual Category Category { get; }
}     

Category model

public class Category: Entity
{
    protected Category() { }

    public Category(string name): this()
    {
        Name = name;
    }

    public string Name { get; }
}

Product Configuration

public class ProductConfiguration: IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.ToTable("Product");

        builder.HasKey(x => x.Id);

        builder.Property(x => x.Id)
               .HasValueGenerator<StringValueGenerator>()
               .ValueGeneratedOnAdd();

        builder.Property(x => x.Name).IsRequired().HasMaxLength(500);
        
        builder.HasOne<Category>().WithMany();
    }
}

Category configuration

public class CategoryConfiguration: IEntityTypeConfiguration<Category>
{
    public void Configure(EntityTypeBuilder<Category> builder)
    {
        builder.ToTable("Category");

        builder.HasKey(x => x.Id);

        builder.Property(x => x.Id)
               .HasValueGenerator<StringValueGenerator>()
               .ValueGeneratedOnAdd();

        builder.Property(x => x.Name)
               .IsRequired()
               .HasMaxLength(255);
    }
}

So having this configuration and domain models in place, when trying to insert some seed data the "CategoryId" property (shadow property created by EFCore, and not in the domain model) is always null

public class ProductDbContextSeeder
{
    public async Task SeedAsync(ProductDbContext context)
    {
        var category = await context.Categories.FirstOrDefaultAsync(x => x.Name == "Test Category");
        if (category is null)
        {
            category = new Category("Test Category");
            await context.Categories.AddAsync(category);
            await context.SaveChangesAsync();
        }

        var product = await context.Products.FirstOrDefaultAsync(x => x.Name == "Test Product");
        if (product is null)
        {
            product = new Product("Test Product", category);
            context.Products.Attach(product);
            await context.SaveChangesAsync();
        }
    }
}

I have instected the state of the entries right before the call to context.SaveChangesAsync() and it seems that the entities are in correct state as below:


When saving Category

  • Category -> Added

When saving Product

  • Product -> Added
  • Category -> Unchanged

What am I missing? Appreciate any hints. pointers.

Thanks

Question: How to model complex and nested structure of VOs (with more than one level of nesting VOs) in EF Core with Fluent API?

Hi Vladimir! Thanks for your course, it's really great with amazing knowledge!
I would like to ask you about a quite complex (IMO) case.
Let's say I have a bunch of Value Objects (I removed some of the nested VOs and I removed business logic from all of them as well to make things clear):

public class Area : ValueObject
{
    private static readonly IReadOnlyList<AreaUnit> _supportedUnits =
        new List<AreaUnit>() { AreaUnit.SquareCentimeter, AreaUnit.SquareMeter };

    public AreaUnit Unit { get; private set; }

    public DecimalValue AreaValue { get; }

    public DecimalSeparator DecimalSeparator { get; }

    public Area(AreaUnit unit, DecimalValue areaValue, DecimalSeparator decimalSeparator)
        // Validation

    // GetEqualityComponents    
}

public class AreaUnit : ValueObject
{
    public static readonly AreaUnit SquareMeter
        = new AreaUnit(AreaUnitAbbrevation.SquareMeter, AreaUnitFullName.SquareMeter);

    public static readonly AreaUnit SquareCentimeter
        = new AreaUnit(AreaUnitAbbrevation.SquareCentimeter, AreaUnitFullName.SquareCentimeter);

    public AreaUnitAbbrevation Abbreviation { get; private set; }
    public AreaUnitFullName FullName { get; private set; }

    public AreaUnit(AreaUnitAbbrevation abbreviation, AreaUnitFullName fullName)
        // Validation

    // GetEqualityComponents
}

public class AreaUnitAbbrevation : ValueObject
{
    private static readonly List<string> _supportedUnitAbbrevations = new List<string>() { "m2", "cm2" };

    public static readonly AreaUnitAbbrevation SquareMeter = new AreaUnitAbbrevation("m2");
    public static readonly AreaUnitAbbrevation SquareCentimeter = new AreaUnitAbbrevation("cm2");

    public string Value { get; private set; }

    public AreaUnitAbbrevation(string abbrevation)
        // Validation       

    // GetEqualityComponents
    // implicit operators
}

public class AreaUnitFullName : ValueObject
{
    private readonly List<string> _supportedUnitFullNames = new List<string>() { "square meter", "square centimeter" };

    public string Value { get; private set; }

    public AreaUnitFullName(string fullName)
        // Validation

    // GetEqualityComponents
    // implicit operators
}

public class DecimalValue : ValueObject
{
    public uint ValueBeforeSeparator { get; private set; }
    public uint ValueAfterSeparator { get; private set; }

    public DecimalValue(int valueBeforeSeparator, int valueAfterSeparator)       
        // Validation

     // GetEqualityComponents
}

public class DecimalSeparator : ValueObject
{
    private readonly IReadOnlyList<char> _allowedSeparators = new List<char>() { '.', ',' };

    public char Value { get; private set; }

    public DecimalSeparator(char separator)
        // Validation

    // GetEqualityComponents
    // implicit operators
}
 
public class Apartment : AggregateRoot
{
    // some Entities, more VOs, business methods, etc.

    public Area Space { get; }

    // some Entities, more VOs, business methods, etc.
}

// EF Core part
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Apartment>(a =>
    {
        a.ToTable("Apartment").HasKey(p => p.EntityId);
        a.OwnsOne(p => p.Space, o =>
        {
            // Is that below even possible - more than simple property access (I mentioned that problem a little bit below)?
            o.Property(oo => oo.Unit).HasColumnName("AreaUnit").OwnsOne(???? we have here more nested VO like AreaUnitFullName and AreaUnitAbbrevation);
            o.Property(oo => oo.AreaValue).HasColumnName("AreaValue").HasConversion(...some conversion here...);
            o.Property(oo => oo.DecimalSeparator).HasColumnName("DecimalSeparator").HasConversion(x=>x.subVO.subVO.value ??? let's assume we want get only one property from subVO and make conversion here for whole tree of nested VOs);
            o.Property(oo => oo.NestedVO.NestedVO).HasColumnName(...).HasConversion(...)
        });
    });
} 

How could I model this structure of VOs for EF Core DbContext in OnModelCreating() methods? I tried several things and I had been only getting errors like this:
https://stackoverflow.com/questions/54328216/ef-core-modelbuilder-not-a-valid-expression-fluent-api but of course my error was not exactly like this and the answer from this link is not the solution for me.
I mean, I've just got an exception similar to "The expression 'a => a.xyz' is not a valid property expression. The expression should represent simple property access: 't => t.MyProperty'.". I see of course this extract: "simple property access" thus my question here, because I have a complex structure of VOs with more nested VOs which equals in the end in more than simple property access requirements.
Have you ever designed such a complex and deep structure of VOs? With EF Core, am I only able to have one level of nested VOs?

To make thing more generic, this kind of structure does not work for me with EF Core Fluent API:

class ParentAggregateRoot
{
	Entity1;
	Entity2;
	ValueObject1;
	ValueObject2;
	ValueObject3;
	ValueObject4;
	ValueObject5;
}

class ValueObject1
{
	ValueObject1a;
	ValueObject1b;
	ValueObject1c;
}

class ValueObject1a
{
	ValueObject1aa;
	ValueObject1ab;
	ValueObject1ac;
}
class ValueObject1b
{
	ValueObject1ba;
	ValueObject1bb;
	ValueObject1bc;
}

class ValueObject1aa
{
	ValueObject1aaa
	ValueObject1ab
	ValueObject1d
	Entity3
}

OR it could work, it can be modeled but I just don't know how to properly use OwnsOne() methods and other methods from EF Core Fluent API?
Do you know how one can solve this kind of complex structure with more levels of nested VOs and Entities?
Maybe am I doing something wrong in Domain Modeling, like there should be more Entities instead of VOs? For my non-generic example, VOs: AreaUnit, DecimalValue, DecimalSeparator (and more nested VOs with more nested VOs, etc.) have to be Entities (or at least some of them)?
The problem is they are not Entities in my Domain, but maybe to workaround EF Core, they should be Entities? Or maybe I have to disentangle nested VOs and move them up? But then, I would end up with a class with about 1000 lines - so much logic I have inside all the nested VOs.
Hopefully, I've presented my issue clearly, if not, I'll try to give more clues, just let me know. Thank you very very much in advance!

[Question] Backing fields (Module 5)

Why did you use metadata instead of the following from EF Core docs?

modelBuilder.Entity<Student>()
        .Property(b => b.Enrollments)
        .HasField("_enrollments")
        .UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);

And why didn't you use => _enrollments.ToReadOnlyList() or AsReadOnly() or the following from Microsoft docs?

    private readonly List<Enrollment> _enrollments;
    public IReadOnlyCollection<Enrollment> Enrollments => _enrollments;

I'm trying to follow along but there are so many ways of doing things it seems.

[Question] Why is the Course aggregate modelled as navigation property rather than identity in the Student aggregate?

Hi Vladimir,

I really enjoy your courses and blogs and I recently watched your course DDD and EFCore.

And there is one thing that leads to a question:

As I understand it Student and Course are aggregates on their own.

This is why I cannot come to understand why Student models Course as navigation property.

From what I have learned aggregates should reference other aggregates only by identity. It doesnt seem like Student has any business logic that requires it to know anything about the Course details rather than it's identity to hold it's business invariants.

Also you need to perform some additional stuff to make sure EFCore does not perform database operations for the course when changing the student aggregate.

What is the benefit of modeling it this way? I understand modelling child entities as navigation properties which makes total sense, but why also another aggregate (course in this case)?

If this is about the course name provided in the EnrollIn() produced error message I think this could be handled differently...

What am I missing here?

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.