Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add docs for integration with entity framework and dapper #8

Open
chinwobble opened this issue Sep 7, 2019 · 5 comments
Open

add docs for integration with entity framework and dapper #8

chinwobble opened this issue Sep 7, 2019 · 5 comments
Assignees

Comments

@chinwobble
Copy link

Great project!

Would you be welcome to adding more documentation on how to integrate this with entity framework and dapper so that they get mapped properly to the DB?

@chinwobble
Copy link
Author

@andrewlock anyway this is possible?

@andrewlock
Copy link
Owner

Hi @chinwobble, sorry for the delay! You can use a Dapper TypeHandler<> as shown in this post. For EF Core, you can use the approach from this post I haven't had a chance to see if it works with EF Core 3 yet!

@jedielson
Copy link

Hi... Is there any plan to do it?

In the new versions we are able to add StronglyTypedIdConverter.EfCoreValueConverter to our strongly typed value, but it forces us to reference EF Core in same project of our models.

How can we use it with fluent Api? Do I have to add EF as a reference for my models project?

P.S: Awesome lib. Congrats 😄

@MovGP0
Copy link

MovGP0 commented May 11, 2022

I made some improvements on that value converter selector, such that it also works with non-GUID ids:

using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using StronglyTypedIds;
using System.Collections.Concurrent;

namespace StronglyTypedId;

public sealed class StronglyTypedIdValueConverterSelector: ValueConverterSelector
{
    private readonly ConcurrentDictionary<(Type ModelClrType, Type ProviderClrType), ValueConverterInfo> _converters = new();

    public StronglyTypedIdValueConverterSelector(ValueConverterSelectorDependencies dependencies) : base(dependencies)
    { }

    public override IEnumerable<ValueConverterInfo> Select(Type modelClrType, Type? providerClrType = null)
    {
        var baseConverters = base.Select(modelClrType, providerClrType);
        foreach (var converter in baseConverters)
        {
            yield return converter;
        }

        // Extract the "real" type T from Nullable<T> if required
        var underlyingModelType = UnwrapNullableType(modelClrType);
        var underlyingProviderType = UnwrapNullableType(providerClrType);

        // 'null' means 'get any value converters for the modelClrType'
        if (underlyingProviderType is not null && underlyingProviderType != typeof(Guid)) yield break;

        // Try and get a nested class with the expected name.
        var converterType = underlyingModelType?.GetNestedType(nameof(StronglyTypedIdConverter.EfCoreValueConverter));

        if (underlyingModelType == null) yield break;
        if (converterType == null) yield break;

        var baseType = converterType!.BaseType!.GenericTypeArguments[1]!;
        yield return _converters.GetOrAdd((underlyingModelType, baseType), _ =>
        {
            // Create an instance of the converter whenever it's requested.
            ValueConverter factory(ValueConverterInfo info) => (ValueConverter)Activator.CreateInstance(converterType, info.MappingHints)!;

            // Build the info for our strongly-typed ID => Guid converter
            return new ValueConverterInfo(modelClrType, baseType, factory);
        });
    }

    private static Type? UnwrapNullableType(Type? type)
    {
        if (type is null) { return null; }
        return Nullable.GetUnderlyingType(type) ?? type;
    }
}

I still have a problem with linq queries that join a strongly typed id with the unterlying type.

Example

Assume we have the following classes:

public abstract class Person<T> where T:struct
{
    public T Id { get; set; }
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
}

public sealed class Customer : Person<CustomerId>
{
}

public sealed class Employee : Person<EmployeeId>
{
}

public sealed class PersonInfo
{
    // could be a CustomerId or a PersonId, so we use Guid here
    public Guid PersonId { get; set; }
    public string EmailAddress  { get; set; } = string.Empty;
}

The following query causes an exception:

from c in ctx.Customer
join pi in ctx.PersonInfo on c.Id.Value equals pi.PersonId
select new {
    c.FirstName,
    c.LastName,
    pi.EmailAddress
}

This causes the same error:

from c in ctx.Customer
join pi in ctx.PersonInfo on c.Id.ToString() equals pi.PersonId.ToString()
select new {
    c.FirstName,
    c.LastName,
    pi.EmailAddress
}
The LINQ expression '...' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

@mohaaron
Copy link

Hi... Is there any plan to do it?

In the new versions we are able to add StronglyTypedIdConverter.EfCoreValueConverter to our strongly typed value, but it forces us to reference EF Core in same project of our models.

How can we use it with fluent Api? Do I have to add EF as a reference for my models project?

P.S: Awesome lib. Congrats 😄

I'm just adding this package for the first time and trying it out, and this question about needing to reference the EFCore is what I'm wondering about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants