<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      嬸可忍叔不可忍的AutoMapper,你還用嗎?

      AutoMapper是讓人又愛又恨的項目

      • 愛它是因為它解決了一些問題,很多項目都有用,下載量很大,受眾很廣。
      • 恨它是因為它諸多反人類的設計。
      • 為此本人開源項目PocoEmit對標AutoMapper。

      1. AutoMapper反人類設計

      1.1 AutoMapper注冊代碼

      services.AddAutoMapper(cfg => cfg.CreateMap<User, UserDTO>());
      

      User和UserDTO除了類名不一樣,其他都一樣,怎么看這行代碼都多余。
      需要轉化的類型越多,多余的代碼就越多。
      類型轉化不應該就是個靜態方法嗎?而且AutoMapper注冊卻依賴容器,Mapper對象也是從容器獲取。
      本人覺得AutoMapper設計的太反人類了。

      1.2 PocoEmit對于大部分轉化是不需要手動配置

      • PocoEmit可以輕松的定義靜態實例。
      • PocoEmit靜態實例可以用來定義靜態委托字段,當靜態方法用。
      UserDTO dto = PocoEmit.Mapper.Default.Convert<User, UserDTO>(new User());
      
      public static readonly Func<User, UserDTO> UserDTOConvert = PocoEmit.Mapper.Default.GetConvertFunc<User, UserDTO>();
      

      2. AutoMapper的性能差強人意

      2.1 以下是AutoMapper官網例子與PocoEmit.Mapper的對比

      • Customer轉化為CustomerDTO(嵌套多個子對象、數組及列表)。
      • Auto是執行AutoMapper的IMapper.Map方法。
      • Poco是執行PocoEmit.Mapper的IMapper.Convert方法。
      • PocoFunc是執行PocoEmit.Mapper生成的委托。
      Method Mean Error StdDev Median Ratio RatioSD Gen0 Allocated Alloc Ratio
      Auto 89.30 ns 1.006 ns 1.118 ns 90.17 ns 1.46 0.03 0.0260 448 B 1.08
      Poco 61.31 ns 1.036 ns 1.194 ns 61.25 ns 1.00 0.03 0.0241 416 B 1.00
      PocoFunc 42.56 ns 0.066 ns 0.073 ns 42.56 ns 0.69 0.01 0.0223 384 B 0.92
      • Auto耗時比Poco多50%左右。
      • Auto耗時是PocoFunc的兩倍多。

      2.2 能不能用AutoMapper生成委托來提高性能呢

      • 既可以說能也可以說不能。
      • 說能是因為AutoMapper確實提供了該功能。
      • 說不能是因為AutoMapper沒打算給用戶用。

      2.2.1 AutoMapper生成委托有點麻煩

      var configuration = _auto.ConfigurationProvider.Internal();
      var mapRequest = new MapRequest(new TypePair(typeof(Customer), typeof(CustomerDTO)));
      Func<Customer, CustomerDTO, ResolutionContext, CustomerDTO> autoFunc = configuration.GetExecutionPlan<Customer, CustomerDTO>(mapRequest);
      

      作為對比PocoEmit.Mapper就簡單的多了

      Func<Customer, CustomerDTO> pocoFunc = PocoEmit.Mapper.Default.GetConvertFunc<Customer, CustomerDTO>();
      

      2.2.2 調用AutoMapper生成的委托更麻煩

      • 參數ResolutionContext沒有公開的構造函數,也找不到公開的實例。
      • 只能通過反射獲得ResolutionContext的實例。
      var field = typeof(AutoMapper.Mapper).GetField("_defaultContext", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
      ResolutionContext resolutionContext = field.GetValue(_auto) as ResolutionContext;
      

      2.2.3 加入AutoMapper生成委托再對比一下

      Method Mean Error StdDev Median Ratio RatioSD Gen0 Allocated Alloc Ratio
      Auto 89.30 ns 1.006 ns 1.118 ns 90.17 ns 1.46 0.03 0.0260 448 B 1.08
      AutoFunc 56.04 ns 0.103 ns 0.119 ns 56.03 ns 0.91 0.02 0.0260 448 B 1.08
      Poco 61.31 ns 1.036 ns 1.194 ns 61.25 ns 1.00 0.03 0.0241 416 B 1.00
      PocoFunc 42.56 ns 0.066 ns 0.073 ns 42.56 ns 0.69 0.01 0.0223 384 B 0.92
      • AutoMapper生成委托確實也快了不少。
      • 從百分比來看即使不生成委托,AutoMapper也慢不了多少?沒有數量級的區別,能忍? --- 反問句

      2.3 簡單類型轉化對比

      • User轉UserDTO,只有兩個簡單屬性
      Method Mean Error StdDev Ratio Gen0 Allocated Alloc Ratio
      Auto 35.436 ns 0.0455 ns 0.0505 ns 1.57 0.0019 32 B 0.50
      AutoFunc 4.159 ns 0.0847 ns 0.0906 ns 0.18 0.0019 32 B 0.50
      Poco 22.607 ns 0.1754 ns 0.1801 ns 1.00 0.0037 64 B 1.00
      PocoFunc 3.818 ns 0.0176 ns 0.0180 ns 0.17 0.0019 32 B 0.50
      • Auto耗時是AutoFunc差不多十倍,差出一個數量級了(回答了前面的反問)
      • AutoFunc耗時比PocoFunc稍多,這說明AutoMapper復雜類型轉化性能非常不好,簡單類型轉化可能還能湊合
      • 關鍵是性能好生成的委托AutoMapper不給用啊,“嬸可忍叔不可忍”啊!

      3. AutoMapper生成的代碼能通過代碼審核嗎?

      3.1 AutoMapper官網那個例子生成以下代碼

      T __f<T>(System.Func<T> f) => f();
      CustomerDTO _autoMap(Customer source, CustomerDTO destination, ResolutionContext context)
      {
          return (source == null) ?
              (destination == null) ? (CustomerDTO)null : destination :
              __f(() => {
                  CustomerDTO typeMapDestination = null;
                  typeMapDestination = destination ?? new CustomerDTO();
                  try
                  {
                      typeMapDestination.Id = source.Id;
                  }
                  catch (Exception ex)
                  {
                      throw TypeMapPlanBuilder.MemberMappingError(
                          ex,
                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                  }
                  try
                  {
                      typeMapDestination.Name = source.Name;
                  }
                  catch (Exception ex)
                  {
                      throw TypeMapPlanBuilder.MemberMappingError(
                          ex,
                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                  }
                  try
                  {
                      Address resolvedValue = null;
                      Address mappedValue = null;
                      resolvedValue = source.Address;
                      mappedValue = (resolvedValue == null) ? (Address)null :
                          ((Func<Address, Address, ResolutionContext, Address>)((
                              Address source_1,
                              Address destination_1,
                              ResolutionContext context) => //Address
                              (source_1 == null) ?
                                  (destination_1 == null) ? (Address)null : destination_1 :
                                  __f(() => {
                                      Address typeMapDestination_1 = null;
                                      typeMapDestination_1 = destination_1 ?? new Address();
                                      try
                                      {
                                          typeMapDestination_1.Id = source_1.Id;
                                      }
                                      catch (Exception ex)
                                      {
                                          throw TypeMapPlanBuilder.MemberMappingError(
                                              ex,
                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                      }
                                      try
                                      {
                                          typeMapDestination_1.Street = source_1.Street;
                                      }
                                      catch (Exception ex)
                                      {
                                          throw TypeMapPlanBuilder.MemberMappingError(
                                              ex,
                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                      }
                                      try
                                      {
                                          typeMapDestination_1.City = source_1.City;
                                      }
                                      catch (Exception ex)
                                      {
                                          throw TypeMapPlanBuilder.MemberMappingError(
                                              ex,
                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                      }
                                      try
                                      {
                                          typeMapDestination_1.Country = source_1.Country;
                                      }
                                      catch (Exception ex)
                                      {
                                          throw TypeMapPlanBuilder.MemberMappingError(
                                              ex,
                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                      }
                                      return typeMapDestination_1;
                                  })))
                          .Invoke(
                              resolvedValue,
                              (destination == null) ? (Address)null :
                                  typeMapDestination.Address,
                              context);
                      typeMapDestination.Address = mappedValue;
                  }
                  catch (Exception ex)
                  {
                      throw TypeMapPlanBuilder.MemberMappingError(
                          ex,
                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                  }
                  try
                  {
                      Address resolvedValue_1 = null;
                      AddressDTO mappedValue_1 = null;
                      resolvedValue_1 = source.HomeAddress;
                      mappedValue_1 = (resolvedValue_1 == null) ? (AddressDTO)null :
                          ((Func<Address, AddressDTO, ResolutionContext, AddressDTO>)((
                              Address source_2,
                              AddressDTO destination_2,
                              ResolutionContext context) => //AddressDTO
                              (source_2 == null) ?
                                  (destination_2 == null) ? (AddressDTO)null : destination_2 :
                                  __f(() => {
                                      AddressDTO typeMapDestination_2 = null;
                                      typeMapDestination_2 = destination_2 ?? new AddressDTO();
                                      try
                                      {
                                          typeMapDestination_2.Id = source_2.Id;
                                      }
                                      catch (Exception ex)
                                      {
                                          throw TypeMapPlanBuilder.MemberMappingError(
                                              ex,
                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                      }
                                      try
                                      {
                                          typeMapDestination_2.City = source_2.City;
                                      }
                                      catch (Exception ex)
                                      {
                                          throw TypeMapPlanBuilder.MemberMappingError(
                                              ex,
                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                      }
                                      try
                                      {
                                          typeMapDestination_2.Country = source_2.Country;
                                      }
                                      catch (Exception ex)
                                      {
                                          throw TypeMapPlanBuilder.MemberMappingError(
                                              ex,
                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                      }
                                      return typeMapDestination_2;
                                  })))
                          .Invoke(
                              resolvedValue_1,
                              (destination == null) ? (AddressDTO)null :
                                  typeMapDestination.HomeAddress,
                              context);
                      typeMapDestination.HomeAddress = mappedValue_1;
                  }
                  catch (Exception ex)
                  {
                      throw TypeMapPlanBuilder.MemberMappingError(
                          ex,
                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                  }
                  try
                  {
                      Address[] resolvedValue_2 = null;
                      AddressDTO[] mappedValue_2 = null;
                      resolvedValue_2 = source.Addresses;
                      mappedValue_2 = (resolvedValue_2 == null) ?
                          Array.Empty<AddressDTO>() :
                          __f(() => {
                              AddressDTO[] destinationArray = null;
                              int destinationArrayIndex = default;
                              destinationArray = new AddressDTO[resolvedValue_2.Length];
                              destinationArrayIndex = default(int);
                              int sourceArrayIndex = default;
                              Address sourceItem = null;
                              sourceArrayIndex = default(int);
                              while (true)
                              {
                                  if ((sourceArrayIndex < resolvedValue_2.Length))
                                  {
                                      sourceItem = resolvedValue_2[sourceArrayIndex];
                                      destinationArray[destinationArrayIndex++] = ((Func<Address, AddressDTO, ResolutionContext, AddressDTO>)((
                                          Address source_2,
                                          AddressDTO destination_2,
                                          ResolutionContext context) => //AddressDTO
                                          (source_2 == null) ?
                                              (destination_2 == null) ? (AddressDTO)null : destination_2 :
                                              __f(() => {
                                                  AddressDTO typeMapDestination_2 = null;
                                                  typeMapDestination_2 = destination_2 ?? new AddressDTO();
                                                  try
                                                  {
                                                      typeMapDestination_2.Id = source_2.Id;
                                                  }
                                                  catch (Exception ex)
                                                  {
                                                      throw TypeMapPlanBuilder.MemberMappingError(
                                                          ex,
                                                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                                  }
                                                  try
                                                  {
                                                      typeMapDestination_2.City = source_2.City;
                                                  }
                                                  catch (Exception ex)
                                                  {
                                                      throw TypeMapPlanBuilder.MemberMappingError(
                                                          ex,
                                                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                                  }
                                                  try
                                                  {
                                                      typeMapDestination_2.Country = source_2.Country;
                                                  }
                                                  catch (Exception ex)
                                                  {
                                                      throw TypeMapPlanBuilder.MemberMappingError(
                                                          ex,
                                                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                                  }
                                                  return typeMapDestination_2;
                                              })))
                                      .Invoke(
                                          sourceItem,
                                          (AddressDTO)null,
                                          context);
                                      sourceArrayIndex++;
                                  }
                                  else
                                  {
                                      goto LoopBreak;
                                  }
                              }
                          LoopBreak:;
                              return destinationArray;
                          });
                      typeMapDestination.Addresses = mappedValue_2;
                  }
                  catch (Exception ex)
                  {
                      throw TypeMapPlanBuilder.MemberMappingError(
                          ex,
                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                  }
                  try
                  {
                      List<Address> resolvedValue_3 = null;
                      List<AddressDTO> mappedValue_3 = null;
                      resolvedValue_3 = source.WorkAddresses;
                      mappedValue_3 = (resolvedValue_3 == null) ?
                          new List<AddressDTO>() :
                          __f(() => {
                              List<AddressDTO> collectionDestination = null;
                              List<AddressDTO> passedDestination = null;
                              passedDestination = (destination == null) ? (List<AddressDTO>)null :
                                  typeMapDestination.WorkAddresses;
                              collectionDestination = passedDestination ?? new List<AddressDTO>();
                              collectionDestination.Clear();
                              List<Address>.Enumerator enumerator = default;
                              Address item = null;
                              enumerator = resolvedValue_3.GetEnumerator();
                              try
                              {
                                  while (true)
                                  {
                                      if (enumerator.MoveNext())
                                      {
                                          item = enumerator.Current;
                                          collectionDestination.Add(((Func<Address, AddressDTO, ResolutionContext, AddressDTO>)((
                                              Address source_2,
                                              AddressDTO destination_2,
                                              ResolutionContext context) => //AddressDTO
                                              (source_2 == null) ?
                                                  (destination_2 == null) ? (AddressDTO)null : destination_2 :
                                                  __f(() => {
                                                      AddressDTO typeMapDestination_2 = null;
                                                      typeMapDestination_2 = destination_2 ?? new AddressDTO();
                                                      try
                                                      {
                                                          typeMapDestination_2.Id = source_2.Id;
                                                      }
                                                      catch (Exception ex)
                                                      {
                                                          throw TypeMapPlanBuilder.MemberMappingError(
                                                              ex,
                                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                                      }
                                                      try
                                                      {
                                                          typeMapDestination_2.City = source_2.City;
                                                      }
                                                      catch (Exception ex)
                                                      {
                                                          throw TypeMapPlanBuilder.MemberMappingError(
                                                              ex,
                                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                                      }
                                                      try
                                                      {
                                                          typeMapDestination_2.Country = source_2.Country;
                                                      }
                                                      catch (Exception ex)
                                                      {
                                                          throw TypeMapPlanBuilder.MemberMappingError(
                                                              ex,
                                                              default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                                                      }
                                                      return typeMapDestination_2;
                                                  })))
                                          .Invoke(
                                              item,
                                              (AddressDTO)null,
                                              context));
                                      }
                                      else
                                      {
                                          goto LoopBreak_1;
                                      }
                                  }
                              LoopBreak_1:;
                              }
                              finally
                              {
                                  enumerator.Dispose();
                              }
                              return collectionDestination;
                          });
                      typeMapDestination.WorkAddresses = mappedValue_3;
                  }
                  catch (Exception ex)
                  {
                      throw TypeMapPlanBuilder.MemberMappingError(
                          ex,
                          default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);
                  }
                  return typeMapDestination;
              });
      }    
      

      3.2 以下是PocoEmit.Mapper生成的代碼

      CustomerDTO _pocoConvert(Customer source)
      {
          CustomerDTO dest = null;
          if ((source != (Customer)null))
          {
              dest = new CustomerDTO();
              Address member0 = null;
              Address member1 = null;
              Address[] member2 = null;
              List<Address> member3 = null;
              dest.Id = source.Id;
              dest.Name = source.Name;
              member0 = source.Address;
              if ((member0 != null))
              {
                  dest.Address = member0;
              }
              member1 = source.HomeAddress;
              if ((member1 != null))
              {
                  // { The block result will be assigned to `dest.HomeAddress`
                  AddressDTO dest_1 = null;
                  if ((member1 != (Address)null))
                  {
                      dest_1 = new AddressDTO();
                      dest_1.Id = member1.Id;
                      dest_1.City = member1.City;
                      dest_1.Country = member1.Country;
                  }
                  dest.HomeAddress = dest_1;
                  // } end of block assignment;
              }
              member2 = source.Addresses;
              if ((member2 != null))
              {
                  // { The block result will be assigned to `dest.Addresses`
                  int count = default;
                  AddressDTO[] dest_2 = null;
                  int index = default;
                  Address sourceItem = null;
                  count = member2.Length;
                  dest_2 = new AddressDTO[count];
                  index = 0;
                  while (true)
                  {
                      if ((index < count))
                      {
                          sourceItem = member2[index];
                          // { The block result will be assigned to `dest_2[index]`
                          AddressDTO dest_3 = null;
                          if ((sourceItem != (Address)null))
                          {
                              dest_3 = new AddressDTO();
                              dest_3.Id = sourceItem.Id;
                              dest_3.City = sourceItem.City;
                              dest_3.Country = sourceItem.Country;
                          }
                          dest_2[index] = dest_3;
                          // } end of block assignment
                          index++;
                      }
                      else
                      {
                          goto forLabel;
                      }
                  }
                  forLabel:;
                  dest.Addresses = dest_2;
                  // } end of block assignment;
              }
              member3 = source.WorkAddresses;
              if ((member3 != null))
              {
                  // { The block result will be assigned to `dest.WorkAddresses`
                  List<AddressDTO> dest_4 = null;
                  dest_4 = new List<AddressDTO>(member3.Count);
                  dest_4;
                  int index_1 = default;
                  int len = default;
                  index_1 = 0;
                  len = member3.Count;
                  while (true)
                  {
                      if ((index_1 < len))
                      {
                          Address sourceItem_1 = null;
                          AddressDTO destItem = null;
                          sourceItem_1 = member3[index_1];
                          // { The block result will be assigned to `destItem`
                              AddressDTO dest_5 = null;
                              if ((sourceItem_1 != (Address)null))
                              {
                                  dest_5 = new AddressDTO();
                                  dest_5.Id = sourceItem_1.Id;
                                  dest_5.City = sourceItem_1.City;
                                  dest_5.Country = sourceItem_1.Country;
                              }
                              destItem = dest_5;
                              // } end of block assignment;
                          dest_4.Add(destItem);
                          index_1++;
                      }
                      else
                      {
                          goto forLabel_1;
                      }
                  }
                  forLabel_1:;
                  dest.WorkAddresses = dest_4;
                  // } end of block assignment;
              }
              CustomerConvertBench.ConvertAddressCity(
                  source,
                  dest);
          }
          return dest;
      }
      

      3.3 簡單對比如下

      • AutoMapper生成代碼三百多行,PocoEmit.Mapper一百多行,AutoMapper代碼量是兩倍以上
      • AutoMapper生成大量try catch,哪怕是int對int賦值也要try
      • AutoMapper用迭代器Enumerator訪問列表,PocoEmit.Mapper用索引器
      • AutoMapper這些區別應該是導致性能差的部分原因

      3.4 如何獲取AutoMapper生成的代碼

      LambdaExpression expression = _auto.ConfigurationProvider.BuildExecutionPlan(typeof(Customer), typeof(CustomerDTO));   
      

      3.4.1 如果要查看更可讀的代碼推薦使用FastExpressionCompiler

      • 可以使用nuget安裝
      • 前面的例子就是使用FastExpressionCompiler再手動整理了一下
      string code = FastExpressionCompiler.ToCSharpPrinter.ToCSharpString(expression);
      

      3.4.2 PocoEmit獲取生成代碼更簡單

      Expression<Func<Customer, CustomerDTO>> expression =  PocoEmit.Mapper.Default.BuildConverter<Customer, CustomerDTO>();
      string code = FastExpressionCompiler.ToCSharpPrinter.ToCSharpString(expression);
      

      3.4.3 PocoEmit生成代碼擴展性

      • PocoEmit可以獲取委托表達式自己來編譯委托
      • PocoEmit通過PocoEmit.Builders.Compiler.Instance來編譯,可以對Instance進行覆蓋來擴展
      • 通過實現Compiler類來擴展,只需要重寫CompileFunc和CompileAction兩個方法
      • 可以使用FastExpressionCompiler來實現Compiler類

      4. AutoMapper枚舉邏輯問題

      public enum MyColor
      {
          None = 0,
          Red = 1,
          Green = 2,
          Blue = 3,
      }
      ConsoleColor color = ConsoleColor.DarkBlue;
      // Red
      MyColor autoColor = _auto.Map<ConsoleColor, MyColor>(color);
      // None
      MyColor pocoColor = PocoEmit.Mapper.Default.Convert<ConsoleColor, MyColor>(color);
      
      • AutoMapper先按枚舉名轉化,失敗再按值轉化,不支持的DarkBlue被AutoMapper轉化為Red
      • 不同類型的枚舉值轉化沒有意義,定義枚舉可以不指定值
      • AutoMapper這完全是犯了畫蛇添足的錯誤
      • AutoMapper還有哪些槽點歡迎大家在評論區指出

      5. PocoEmit可擴展架構

      5.1 nuget安裝PocoEmit可獲得基礎功能

      • 通過PocoEmit可以讀寫實體的屬性
      • PocoEmit可以通過PocoEmit.Poco轉化基礎類型和枚舉
      • PocoEmit.Poco支持注冊轉化表達式

      5.2 nuget安裝PocoEmit.Mapper獲得更多功能

      • PocoEmit.Mapper可以支持PocoEmit.Poco的所有功能
      • PocoEmit.Mapper可以支持自定義實體類型(不支持集合(含數組、列表及字典)成員)的轉化和復制

      5.3 nuget安裝PocoEmit.Collections擴展集合功能

      • 通過UseCollection擴展方法給PocoEmit.Mapper增加集合功能
      • 擴展后PocoEmit.Mapper支持集合(含數組、列表及字典)的轉化和復制
      • 支持實體類型包含集合成員的轉化和復制
      • 嫌麻煩的同學可以直接安裝PocoEmit.Collections并配置UseCollection

      如何使用PocoEmit.Mapper替代AutoMapper請移步看下一篇 http://www.rzrgm.cn/xiangji/p/19062936

      源碼托管地址: https://github.com/donetsoftwork/MyEmit ,也歡迎大家直接查看源碼。
      gitee同步更新:https://gitee.com/donetsoftwork/MyEmit

      如果大家喜歡請動動您發財的小手手幫忙點一下Star。

      posted on 2025-08-27 00:06  xiangji  閱讀(2290)  評論(36)    收藏  舉報

      導航

      主站蜘蛛池模板: 亚洲国产精品久久电影欧美| 亚洲精品日韩在线观看| 精品人妻中文字幕av| 亚洲成av人片在www鸭子| 国产一区二区三区AV在线无码观看| 国产日韩av免费无码一区二区三区 | 国产免费一区二区三区在线观看 | 中文有无人妻vs无码人妻激烈| av新版天堂在线观看| 中文无码热在线视频| 成人看的污污超级黄网站免费| 另类 专区 欧美 制服丝袜| 欧美乱码伦视频免费| 狠狠色噜噜狠狠狠狠av不卡| 中文字幕一区二区网站| 国产一区在线播放av| 亚洲人成网站在线播放动漫| 在线观看AV永久免费| 国产资源精品中文字幕| 性一交一乱一乱一视频| 综合欧美视频一区二区三区| 99精品国产成人一区二区| 国产免费午夜福利片在线| 五月婷婷开心中文字幕| 精品无码国产污污污免费| 97精品人妻系列无码人妻| 日本一卡二卡3卡四卡网站精品| 少妇被粗大的猛烈进出69影院一| 免费AV片在线观看网址| 狠狠v日韩v欧美v| 波多野结衣高清一区二区三区 | 激情综合网激情综合| 亚洲国产精品久久无人区| 97人妻天天摸天天爽天天| 熟女视频一区二区三区嫩草| 国产情侣激情在线对白| 国产精品久久久久久人妻精品动漫| 国产精品成人av电影不卡| 国产黄色看三级三级三级| 久久青青草原精品国产app| 亚洲熟妇色自偷自拍另类|