【问题标题】:How do I make Dapper Dommel insert an application generated primary key?如何让 Dapper Dommel 插入应用程序生成的主键?
【发布时间】:2021-08-11 02:43:17
【问题描述】:

假设我的实体是

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")

我似乎在文档中找不到这个。有谁知道如何做到这一点?

【问题讨论】:

    标签: dapper


    【解决方案1】:

    在初步测试中,这对我有用。实际上,这段代码告诉 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】:

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

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

      【讨论】:

      • 是的,你是绝对正确的,但不幸的是它会破坏 Dommel 的目的,因为它使用诸如 Insert(object)、Delete(object)、Update(object) 之类的方法 - 好处是没有困难编码 SQL。
      【解决方案3】:

      TL;DR;

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

      解释:

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

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

      这有点fixed,并于2021年6月合并到官方GitHub repo,但作者自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);
          }
      }
      

      添加以下AppUserDommel地图:

      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();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-15
        • 2017-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多