如何让Dapper Dommel插入应用程序生成的主键?

时间:2017-04-13 10:05:04

标签: dapper

假设我的实体是

public class AppUser
{
    string Id { get; set; }
    string Name { get; set; }
}

默认情况下,Dapper Dommel不会插入Id字段。它会生成非常接近于此的SQL:

insert into AppUser (Name) values ("Dapper") select cast(scope_identity() as int)

这个SQL是使用Dapper Dommel Insert函数生成的,如下所示:

using (var connection = new System.Data.SqlClient.SqlConnection(ConnectionString))
{
    connection.Open();
    connection.Insert<AppUser>(new User { Id = "someGuid", Name = "Dapper" });
}

我希望它插入我为其提供值的Id列,也不执行select cast(scope_identity()as int)查询。也就是说,我想要这样的东西

insert into AppUser (Id, Name) values ("someGuid", "Dapper")

我似乎无法在文档中找到它。有谁知道如何实现这个目标?

3 个答案:

答案 0 :(得分:0)

我不熟悉Dommel,但你可以使用.Execute:

    _dbConnection.Execute(@"insert into AppUser (Id, Name) 
                            values (@Id, @Name)", new {Id = 1, Name = "Foo"});

答案 1 :(得分:0)

在初步测试中,这对我有用。有效地,此代码告诉Dommel您的属性均未生成/计算。如果您具有要忽略的计算属性,则可能需要针对您的用例更加具体。到目前为止我还不需要。

internal class KeysNotGeneratedPropertyResolver : DommelPropertyResolver
{
    public override IEnumerable<ColumnPropertyInfo> ResolveProperties(Type type)
    {
        return base.ResolveProperties(type)
            .Select(column => new ColumnPropertyInfo(column.Property, DatabaseGeneratedOption.None));
    }
}

internal class NotGeneratedKeyResolver : IKeyPropertyResolver
{
    private static readonly IKeyPropertyResolver DefaultResolver = new DommelKeyPropertyResolver();
    public ColumnPropertyInfo[] ResolveKeyProperties(Type type)
    {
        return DefaultResolver.ResolveKeyProperties(type)
            .Select(info => new ColumnPropertyInfo(info.Property, DatabaseGeneratedOption.None))
            .ToArray();
    }
}

然后在您的配置中

 DommelMapper.SetPropertyResolver(new KeysNotGeneratedPropertyResolver());
 DommelMapper.SetKeyPropertyResolver(new NotGeneratedKeyResolver());

答案 2 :(得分:0)

TL;DR;

由于库中的错误,“Id”属性被视为身份并从生成的插入语句中排除。将 Dapper-Dommel-FluentMap 库与下面提供的文件一起使用,您应该没问题。

说明:

我在使用 Dapper-Dommel-FluentMap 库(一个提供更多映射功能的互补姐妹库)时遇到了同样的问题,我在对其源代码进行了一些挖掘(几个小时)后设法找到了解决方案。

未插入 Id 属性的原因是因为 DommelDommel-FluentMap 都将所有关键属性(或名为 Id 的属性)视为身份。 但是,在现实生活中的应用程序中,并非所有关键属性都是身份,因此这种行为是一个错误。

这有点fixed,并于 2021 年 6 月合并到官方 GitHub 存储库,但作者自 8/23/2020 以来一直没有更新 NuGet 包,质疑此库的可靠性。因此,如果我们想继续使用 Dapper-Dommel,我们只有一个选择:使用我们自己的实现覆盖默认属性和关键属性解析器。

或者,您可以在 Dapper-Dommel 库中推出您自己的自定义实现,但在那里您是自己的。

解决方案

首先添加 Dapper.FluentMap.Dommel NuGet 包。

之后,在您的项目中添加以下文件:

public class CustomDommelPropertyResolver : DefaultPropertyResolver
{
    private static readonly IPropertyResolver DefaultResolver = new DefaultPropertyResolver();

    /// <inheritdoc/>
    protected override IEnumerable<PropertyInfo> FilterComplexTypes(IEnumerable<PropertyInfo> properties)
    {
        foreach (var propertyInfo in properties)
        {
            var type = propertyInfo.PropertyType;
            type = Nullable.GetUnderlyingType(type) ?? type;

            if (type.GetTypeInfo().IsPrimitive || type.GetTypeInfo().IsEnum || PrimitiveTypes.Contains(type))
            {
                yield return propertyInfo;
            }
        }
    }

    /// <inheritdoc/>
    public override IEnumerable<ColumnPropertyInfo> ResolveProperties(Type type)
    {
        IEntityMap entityMap;
        if (FluentMapper.EntityMaps.TryGetValue(type, out entityMap))
        {
            foreach (var property in FilterComplexTypes(type.GetProperties()))
            {
                // Determine whether the property should be ignored.
                var propertyMap = entityMap.PropertyMaps.FirstOrDefault(p => p.PropertyInfo.Name == property.Name);
                if (propertyMap == null || !propertyMap.Ignored)
                {
                    var dommelPropertyMap = propertyMap as DommelPropertyMap;
                    if (dommelPropertyMap != null)
                    {
                        yield return new ColumnPropertyInfo(property, dommelPropertyMap.GeneratedOption != default
                                                                    ? dommelPropertyMap.GeneratedOption
                                                                    : (dommelPropertyMap.Identity ? DatabaseGeneratedOption.Identity : DatabaseGeneratedOption.None));
                    }
                    else
                    {
                        yield return new ColumnPropertyInfo(property);
                    }
                }
            }
        }
        else
        {
            foreach (var property in DefaultResolver.ResolveProperties(type))
            {
                yield return property;
            }
        }
    }
}

public class CustomDommelKeyPropertyResolver : IKeyPropertyResolver
{
    private static readonly IKeyPropertyResolver DefaultResolver = new DefaultKeyPropertyResolver();

    /// <inheritdoc/>
    public ColumnPropertyInfo[] ResolveKeyProperties(Type type)
    {
        IEntityMap entityMap;
        if (!FluentMapper.EntityMaps.TryGetValue(type, out entityMap))
        {
            return DefaultResolver.ResolveKeyProperties(type);
        }

        var mapping = entityMap as IDommelEntityMap;
        if (mapping != null)
        {
            var allPropertyMaps = entityMap.PropertyMaps.OfType<DommelPropertyMap>();
            var keyPropertyInfos = allPropertyMaps
                 .Where(e => e.Key)
                 .Select(x => new ColumnPropertyInfo(x.PropertyInfo, x.GeneratedOption != default 
                                                                   ? x.GeneratedOption
                                                                   : (x.Identity ? DatabaseGeneratedOption.Identity : DatabaseGeneratedOption.None)))
                 .ToArray();

            // Now make sure there aren't any missing key properties that weren't explicitly defined in the mapping.
            try
            {
                // Make sure to exclude any keys that were defined in the dommel entity map and not marked as keys.
                var defaultKeyPropertyInfos = DefaultResolver.ResolveKeyProperties(type).Where(x => allPropertyMaps.Count(y => y.PropertyInfo.Equals(x.Property)) == 0);
                keyPropertyInfos = keyPropertyInfos.Union(defaultKeyPropertyInfos).ToArray();
            }
            catch
            {
                // There could be no default Ids found. This is okay as long as we found a custom one.
                if (keyPropertyInfos.Length == 0)
                {
                    throw new InvalidOperationException($"Could not find the key properties for type '{type.FullName}'.");
                }
            }

            return keyPropertyInfos;
        }

        // Fall back to the default mapping strategy.
        return DefaultResolver.ResolveKeyProperties(type);
    }
}

添加以下 AppUser Dommel 贴图:

public class AppUserMap : DommelEntityMap<AppUser>
{
    public AppUserMap()
    {
        Map(p => p.Id).ToColumn("Id")
            .IsKey()
            .SetGeneratedOption(DatabaseGeneratedOption.None)
            .Identity = false; // explicitly specified for clarity

        Map(x => x.Name).ToColumn("Name");
    }
}

Dommel's FluentMapper 初始化方法中添加地图:

FluentMapper.Initialize(config =>
{
    config.AddMap(new AppUserMap());

    config.ForDommel();
});

确保之后覆盖 Dommel 的默认解析器:

DommelMapper.SetKeyPropertyResolver(new CustomDommelKeyPropertyResolver());
DommelMapper.SetPropertyResolver(new CustomDommelPropertyResolver());

有了这个,除非您明确指定属性的数据库生成选项或身份,否则它们将不会被选为身份。

修复此错误的代码如下:

.Select(x => new ColumnPropertyInfo(x.PropertyInfo, x.GeneratedOption != default 
                                                                   ? x.GeneratedOption
                                                                   : (x.Identity ? DatabaseGeneratedOption.Identity : DatabaseGeneratedOption.None)))
 .ToArray();