Finbuckle.MultiTenant icon indicating copy to clipboard operation
Finbuckle.MultiTenant copied to clipboard

TenantId type

Open imanov opened this issue 3 years ago • 12 comments

Is it possible to add an option for using custom TenantId type, instead of string ? Guid ,long, int are good examples of better types which are spread all over the tables in the database.

In cases where the TenantInfo is part of the same database and tenants are dynamically created, following the same ID types is a good practice, as well as better performance and storage usage for int,long,Guid , compared to nvarchar(64)

imanov avatar Nov 23 '20 16:11 imanov

@imanov I'll take a look. It would be a major change but probably doable.

AndrewTriesToCode avatar Nov 23 '20 17:11 AndrewTriesToCode

I ran across the same issue and here is what I did to make this work. The project is pretty new so I am running EF Core 5. I am not sure what other versions of EF Core this will work with.

My Tenant Info class uses a Guid as the id. I implement the interface and just return the Guid as a string.

public string Identifier { get => this.id.ToString(); set => this.id = new Guid(value); }

Then I created an extension method to add to EntityTypeBuilder. Here is what I used:

public static EntityTypeBuilder<T> ConfigureTenantId<T>(this EntityTypeBuilder<T> builder) where T : class
{
    builder.Property("TenantId")
        .HasColumnName("tenant_id")
        .HasColumnType("uniqueidentifier")
        .HasConversion(GuidConverter);
    return builder;
}

and the converter

ValueConverter<string, Guid> GuidConverter = new ValueConverter<string, Guid>
(
    v => new Guid(v),
    v => ((Guid)v).ToString()
);

You can see that I had to change the column name to match our naming scheme and the type. Here is the SQL that it generates:

--part of the where clause
[q].[tenant_id] = @__ef_filter__Id_0)
--the parameter type
@__ef_filter__Id_0='?' (DbType = Guid)

As far as I can tell everything is working and is all setup in EF Core configurations.

johanan avatar Mar 31 '21 02:03 johanan

@johanan that is a good soluion!

AndrewTriesToCode avatar Apr 03 '21 18:04 AndrewTriesToCode

Is it possible to even change the name of the property and to have it as an actual property on the entity instead of a shadow property that only exists in SQL?

Yannick86 avatar Jun 29 '22 13:06 Yannick86

@johanan Is it possible for you to give me some more information on what you did here? I'm new to Finbuckle & EF Core facing this same issue. I don't understand how you implemented this strategy.

JxEngel avatar Jul 13 '22 21:07 JxEngel

@JxEngel The TenantId property is a shadow property on the EF Core model. Essentially what happens is this:

  1. Finbuckle creates a shadow property on the model, names it TenantId and sets the type to string. See https://github.com/Finbuckle/Finbuckle.MultiTenant/blob/main/src/Finbuckle.MultiTenant.EntityFrameworkCore/Extensions/EntityTypeBuilderExtensions.cs#L53
  2. The extension method ConfigureTenantId comes along and adds to the shadow property. In my example it updates the column name and column type which map directly to the database. Because the type isn't a string anymore it needs to be converted from the string value when it's in Finbuckle to the UniqueIdentifier (Guid) in the database. This is where you would add whatever changes are specific to your database.
  3. This can be referenced anywhere the EF Core model is configured. Configuration Files or OnModelCreating in the context.

For more information read up on EF Core shadow properties and configuring them: https://docs.microsoft.com/en-us/ef/core/modeling/shadow-properties#configuring-shadow-properties.

@Yannick86 I am going off of memory here, but I think if you had a property on the entity with the name TenantId EF Core will use it. I don't have any examples in front of me and I always second guess myself when I start diving into EF Core as it is so configurable and I always have to lookup what does what.

I want to add that even if it isn't an actual property off of the entity the shadow property is available as long as the context that queried, created, or attached to that specific entity is still around.

johanan avatar Jul 14 '22 21:07 johanan

@johanan If you can still recall, what does your custom Tenant class look like? How do you implement ITenantInfo if you have an existing Id property that is a guid?

public class Tenant : ITenantInfo
{
  public Guid Id { get; set; }. //<-- ITenantInfo requires this to be a string.....?
}

Thanks for your comment, struggling to find a solution

mgreer5 avatar May 26 '23 02:05 mgreer5

@johanan here is how I used it some time ago. There could be better options now with .NET 7.

image image

imanov avatar May 26 '23 08:05 imanov

For anyone looking, I found a slightly easier way to use a custom tenant class guid id by implementing the interface for ITenantInfo explicitly, see below:

public class Tenant : ITenantInfo
{
    public Guid Id { get; private set; }

    string? ITenantInfo.Id
    {
        get => Id.ToString();
        set => Id = Guid.TryParse(value, out var tmp)
            ? tmp
            : throw new ArgumentNullException($"Id must be a valid Guid");
    }
}

mgreer5 avatar May 26 '23 14:05 mgreer5

I was just searching for this. I think this is a must. Guid is now defacto standard. Since TenantId should never change, what is the reason to not be Guid or int? I have external system that handles Tenants and uses Guid as Id. Perfect solution for my service would be to have TenantId as Guid. Using Guid as string is a huge mistake, Guid is not string, Guid is binary. What we see as Guid, is hexadecimal representation of it. Using Guid as string simply breaks whole point of using Guids, and can lead to huge performance issues on database. If I wanted unique string, I would just use unique string.

turkysha avatar Jan 15 '24 15:01 turkysha

Yes this is still active on the roadmap. I've done a few quick tests on this and the change impact is nontrivial on the code. Stay tuned.

AndrewTriesToCode avatar Jan 17 '24 04:01 AndrewTriesToCode

as an addendum to this enhancement one suggests here that a customizable tenantid column name would expedient in scenarios where the ef core domain model contains an entity called tenant that

  • does not participate in multitenant resolution
  • does not have a primary key specification that matches the multitenant id type constraints thanks

vigouredelaruse avatar Jan 26 '24 21:01 vigouredelaruse