eventflow / EventFlow

Async/await first CQRS+ES and DDD framework for .NET
https://geteventflow.net
Other
2.38k stars 445 forks source link

Question about readmodel id behaviour with Entity Framework #679

Closed Ultre00 closed 3 years ago

Ultre00 commented 5 years ago

I have created a custom implementation of IIdentity. This so i can use a basic Guid for every aggregate root without the '\<entity-name>-' in front of the Guid. I am not sure if this is even possible but when the system is trying to create a readmodel i get the following exception:

System.InvalidCastException: 'Unable to cast object of type 'System.String' to type 'System.Guid'.'

This is because my readmodel has a property Id which is a Guid and not a string. The only reason i am trying to set a Guid as identity is because not all my entities in the database are handled by EventFlow and i want all my entities to have a Guid as the primary key so i can also use the tables created by EventFlow as a fallback scenario without using EventFlow.

The Identity looks like this:

public class GuidId : SingleValueObject<string>, IIdentity
    {
        public GuidId() : base(Guid.NewGuid().ToString())
        {
        }

        public GuidId(string id) : base(Guid.Parse(id).ToString())
        {
            if(string.IsNullOrEmpty(id) || !Guid.TryParse(id, out Guid guidValue))
                throw new ArgumentException("id");
        }

        public GuidId(Guid id) : base(id.ToString())
        {
            if (id == Guid.Empty)
                throw new ArgumentException("id");
        }

        public static GuidId New => new GuidId(Guid.NewGuid());

        public static implicit operator GuidId(Guid id)
        {
            return new GuidId(id);
        }

        public static implicit operator Guid(GuidId id)
        {
            return new Guid(id.Value);
        }
    }

and the readmodel implementation is the same as from one of the examples except i am now using a Guid for the id:

public abstract class VersionedReadModel : IReadModel
    {
        public Guid Id { get; protected set; }
        public DateTime? DeletedAt { get; protected set; }
        public bool IsDeleted => DeletedAt.HasValue;

        [ConcurrencyCheck]
        public long Version { get; set; }
    }

Is it possible to create an entity framework managed readmodel where the Id field is a Guid ? There is no place i am actually mapping this id field yet. I think EventFlow.EntityFramework is trying to convert by naming convention ?

KoriSeng commented 5 years ago

The thing about why the identity is stored as name-guid, is to act as some sort of namespace. which is why all identities when converted from string to IIdentity calls IsValid.

One work around that i can think of instead of using the EventFlow readmodel system. you can write subscribers to subscribe to the relevant event and use pure EF core to write the actual data instead of using the Eventflow read model library.

rasmus commented 5 years ago

@Ultre00 you can implement your own identity and use it throughout EventFlow. There is even a test to ensure that it remains possible at

https://github.com/eventflow/EventFlow/blob/develop/Source/EventFlow.Tests/Exploration/CustomAggregateIdExplorationTest.cs

But I do think you are right, EventFlow expects the types to match. And the suggestion by @7thcubic is very good. Alternatively you could implement your custom read store for EventFlow, the InMemoryReadStore is good place to have a look at what's expected.

https://github.com/eventflow/EventFlow/blob/develop/Source/EventFlow/ReadStores/InMemory/InMemoryReadStore.cs

If you do that, use the EventFlow.TestHelpers to get the test suites that EventFlow uses to test event stores.

Note: When using Guid as IDs in your database, please make sure to think about index fragmentation. Simply doing a Guid.NewGuid() will wreak havoc on your indexes if using a MSSQL Server. Have a look at the IdentityIndexFragmentationTests to see how it affects your DB.

https://github.com/eventflow/EventFlow/blob/master/Source/EventFlow.MsSql.Tests/IntegrationTests/IdentityIndexFragmentationTests.cs

If using EventFlow, you can use the NewComb() to generate Guids that cause very little index fragmentation.

rasmus commented 3 years ago

Closing this as a stale question