feat : complete create and edit product , add file controller

add upload and get files in file controller , complete create and edit product with uploading images
release
Amir Hossein Khademi 2024-01-30 02:33:24 +03:30
parent 1c780f9fac
commit 45a4791cd9
24 changed files with 1964 additions and 43 deletions

View File

@ -0,0 +1,45 @@
using NetinaShop.Domain.Enums;
namespace NetinaShop.Api.Controller;
public class FileController : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
var group = app.NewVersionedApi("File")
.MapGroup("api/file")
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser());
group.MapGet("", GetFilesAsync)
.WithDisplayName("GetFilesAsync")
.HasApiVersion(1.0);
group.MapPost("", UploadFileAsync)
.WithDisplayName("UploadFileAsync")
.HasApiVersion(1.0);
}
public async Task<IResult> GetFilesAsync([FromQuery]StorageFileType? fileType,[FromServices] IStorageService storageService, CancellationToken cancellationToken)
=> TypedResults.Ok(await storageService.GetStorageFiles(fileType: fileType ?? StorageFileType.Image));
public async Task<IResult> UploadFileAsync([FromBody] FileUploadRequest uploadRequest, [FromServices] IStorageService storageService, CancellationToken cancellationToken)
{
var bytes = Convert.FromBase64String(uploadRequest.StringBaseFile);
using var originalMedFileStream = new MemoryStream(bytes);
using var originalThumbFileStream = new MemoryStream(bytes);
using var thumbnailFileStream = new MemoryStream();
using var mediumFileStream = new MemoryStream();
await uploadRequest.ImageResize(originalMedFileStream, mediumFileStream, 1280);
await uploadRequest.ImageResize(originalThumbFileStream, thumbnailFileStream, 200);
var medFileName = await storageService.UploadObjectFromFileAsync(uploadRequest.FileName, $"{uploadRequest.FileUploadType.ToDisplay()}/Med", uploadRequest.ContentType, mediumFileStream);
await storageService.UploadObjectFromFileAsync(uploadRequest.FileName, $"{uploadRequest.FileUploadType.ToDisplay()}/Thumb", uploadRequest.ContentType, thumbnailFileStream);
var response = new FileUploadResponse
{
FileName = medFileName,
FileLocation = $"{uploadRequest.FileUploadType.ToDisplay()}/Med/{medFileName}",
FileUrl = $"https://storage.vesmook.com/{uploadRequest.FileUploadType.ToDisplay()}/Med/{medFileName}"
};
return TypedResults.Ok(response);
}
}

View File

@ -52,7 +52,7 @@
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
@ -74,8 +74,10 @@
<Using Include="NetinaShop.Common.Models.Exception" />
<Using Include="NetinaShop.Common.Models.Mapper" />
<Using Include="NetinaShop.Core" />
<Using Include="NetinaShop.Core.Abstracts" />
<Using Include="NetinaShop.Core.CoreServices.Abstracts" />
<Using Include="NetinaShop.Core.Models.Api" />
<Using Include="NetinaShop.Core.Utilities" />
<Using Include="NetinaShop.Domain" />
<Using Include="NetinaShop.Domain.CommandQueries.Commands" />
<Using Include="NetinaShop.Domain.CommandQueries.Queries" />

View File

@ -2,14 +2,18 @@
{
public enum FileUploadType
{
[Display(Name = "Images")]
Image,
[Display(Name = "Handouts")]
Handout,
[Display(Name = "Videos")]
Video,
Image
}
public class FileUploadRequest
{
public string StringBaseFile { get; set; }
public string FileName { get; set; }
public string ContentType { get; set; }
public FileUploadType FileUploadType { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace NetinaShop.Common.Models.Api;
public class FileUploadResponse
{
public string FileUrl { get; set; }
public string FileLocation { get; set; }
public string FileName { get; set; }
public bool IsOriginal { get; set; } = true;
}

View File

@ -24,7 +24,7 @@
<PackageReference Include="MD.PersianDateTime.Standard" Version="2.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.2.0" />
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,9 @@
namespace NetinaShop.Core.Abstracts;
public interface IStorageService : IScopedDependency
{
Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes);
Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream);
Task<List<StorageFileSDto>> GetStorageFiles(StorageFileType fileType);
}

View File

@ -12,6 +12,7 @@
<PackageReference Include="AspNetCoreRateLimit.Redis" Version="2.0.0" />
<PackageReference Include="Autofac.Extras.Quartz" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.0" />
<PackageReference Include="Quartz" Version="3.8.0" />
</ItemGroup>
@ -22,10 +23,10 @@
<ItemGroup>
<Folder Include="Abstracts\" />
<Folder Include="BaseServices\Abstracts\" />
<Folder Include="EntityServices\ReviewHandlers\" />
<Folder Include="Models\Api\" />
<Folder Include="Utilities\" />
</ItemGroup>

View File

@ -0,0 +1,18 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
namespace NetinaShop.Core.Utilities;
public static class ImageConvertor
{
public static async Task<Stream> ImageResize(this FileUploadRequest fileUpload, Stream input, Stream output, int newWidth)
{
using var image = await Image.LoadAsync(input);
var height_width = image.Height / image.Width;
var new_Height = newWidth * height_width;
image.Mutate(x => x.Resize(newWidth, new_Height));
image.Mutate(x => x.Resize(newWidth, new_Height));
await image.SaveAsJpegAsync(output);
return output;
}
}

View File

@ -8,8 +8,15 @@ public class ProductLDto : BaseDto<ProductLDto,Product>
public string ExpertCheck { get; set; } = string.Empty;
public string Tags { get; set; } = string.Empty;
public string Warranty { get; set; } = string.Empty;
public bool BeDisplayed { get; set; }
public bool HasExpressDelivery { get; set; }
public int MaxOrderCount { get; set; }
public double Cost { get; set; }
public double PackingCost { get; set; }
public Guid BrandId { get; set; }
public string BrandNames { get; set; } = string.Empty;
public string BrandName { get; set; } = string.Empty;
public Guid CategoryId { get; set; }
public string CategoryName { get; set; } = string.Empty;
public List<SpecificationSDto> Specifications { get; set; } = new();
public List<ReviewSDto> Reviews { get; set; } = new();

View File

@ -1,11 +1,26 @@
namespace NetinaShop.Domain.Dtos.SmallDtos;
using Microsoft.IdentityModel.Tokens;
namespace NetinaShop.Domain.Dtos.SmallDtos;
public class StorageFileSDto : BaseDto<StorageFileSDto , StorageFile>
{
public string Name { get; internal set; } = string.Empty;
public string FileLocation { get; internal set; } = string.Empty;
public string FileName { get; internal set; } = string.Empty;
public bool IsHeader { get; internal set; }
public bool IsPrimary { get; internal set; }
public StorageFileType FileType { get; internal set; }
public string Name { get; set; } = string.Empty;
public string FileLocation { get; set; } = string.Empty;
public string FileName { get; set; } = string.Empty;
public bool IsHeader { get; set; }
public bool IsPrimary { get; set; }
public StorageFileType FileType { get; set; }
public bool Selected { get; set; }
public DateTime CreatedAt
{
get
{
string date = FileName.Split('.').First().Split('_').Last();
if (!date.IsNullOrEmpty() && long.TryParse(date, out long longDate))
return DateTimeExtensions.UnixTimeStampToDateTime(longDate);
return DateTime.MinValue;
}
}
}

View File

@ -36,7 +36,7 @@ public partial class Product
public Specification AddSpecification(string title, string detail,string value, bool isFeature, Guid parentId)
{
var ent = Specification.Create(title, detail, value, isFeature, Id, parentId);
var ent = Specification.Create(title, value, detail, isFeature, Id, parentId);
Specifications.Add(ent);
return ent;
}

View File

@ -53,8 +53,8 @@ public partial class Product : ApiEntity
public float Rate { get; internal set; }
public int ReviewCount { get; internal set; }
public int Viewed { get; internal set; }
public bool HasExpressDelivery { get; set; }
public int MaxOrderCount { get; set; }
public bool HasExpressDelivery { get; internal set; }
public int MaxOrderCount { get; internal set; }
public Guid BrandId { get; internal set; }

View File

@ -7,17 +7,20 @@ public partial class Specification : ApiEntity
{
public Specification()
{
}
public Specification(string title, string detail,string value, bool isFeature, Guid productId, Guid parentId)
public Specification(string title, string detail, string value, bool isFeature, Guid productId, Guid parentId)
{
Title = title;
Detail = detail;
Value = value;
IsFeature = isFeature;
ProductId = productId;
ParentId = parentId;
if (parentId != default)
ParentId = parentId;
else
ParentId = null;
}
public string Title { get; internal set; } = string.Empty;
public string Detail { get; internal set; } = string.Empty;
@ -27,7 +30,7 @@ public partial class Specification : ApiEntity
public Guid ProductId { get; internal set; }
public Product? Product { get; internal set; }
public Guid ParentId { get; internal set; }
public Guid? ParentId { get; internal set; }
public Specification? Parent { get; internal set; }
public List<Specification> Children { get; internal set; } = new();

View File

@ -2,6 +2,8 @@
public enum StorageFileType
{
[Display(Name = "Images")]
Image,
[Display(Name = "Videos")]
Video
}

View File

@ -1,6 +0,0 @@
namespace NetinaShop.Domain.Mappers
{
public static partial class CategoryStorageFileMapper
{
}
}

View File

@ -20,7 +20,13 @@ namespace NetinaShop.Domain.Mappers
ExpertCheck = p1.ExpertCheck,
Tags = p1.Tags,
Warranty = p1.Warranty,
Cost = p1.Cost,
BeDisplayed = p1.BeDisplayed,
PackingCost = p1.PackingCost,
HasExpressDelivery = p1.HasExpressDelivery,
MaxOrderCount = p1.MaxOrderCount,
BrandId = p1.BrandId,
CategoryId = p1.CategoryId,
Specifications = funcMain1(p1.Specifications),
Reviews = funcMain2(p1.Reviews),
Files = funcMain3(p1.Files),
@ -41,7 +47,13 @@ namespace NetinaShop.Domain.Mappers
result.ExpertCheck = p5.ExpertCheck;
result.Tags = p5.Tags;
result.Warranty = p5.Warranty;
result.Cost = p5.Cost;
result.BeDisplayed = p5.BeDisplayed;
result.PackingCost = p5.PackingCost;
result.HasExpressDelivery = p5.HasExpressDelivery;
result.MaxOrderCount = p5.MaxOrderCount;
result.BrandId = p5.BrandId;
result.CategoryId = p5.CategoryId;
result.Specifications = funcMain4(p5.Specifications, result.Specifications);
result.Reviews = funcMain5(p5.Reviews, result.Reviews);
result.Files = funcMain6(p5.Files, result.Files);
@ -57,7 +69,13 @@ namespace NetinaShop.Domain.Mappers
ExpertCheck = p13.ExpertCheck,
Tags = p13.Tags,
Warranty = p13.Warranty,
Cost = p13.Cost,
BeDisplayed = p13.BeDisplayed,
PackingCost = p13.PackingCost,
HasExpressDelivery = p13.HasExpressDelivery,
MaxOrderCount = p13.MaxOrderCount,
BrandId = p13.BrandId,
CategoryId = p13.CategoryId,
Specifications = p13.Specifications.Select<SpecificationSDto, Specification>(p14 => new Specification()
{
Title = p14.Title,
@ -65,7 +83,7 @@ namespace NetinaShop.Domain.Mappers
Value = p14.Value,
IsFeature = p14.IsFeature,
ProductId = p14.ProductId,
ParentId = p14.ParentId,
ParentId = (Guid?)p14.ParentId,
Id = p14.Id
}).ToList<Specification>(),
Reviews = p13.Reviews.Select<ReviewSDto, Review>(p15 => new Review()
@ -100,7 +118,15 @@ namespace NetinaShop.Domain.Mappers
ExpertCheck = p17.ExpertCheck,
Tags = p17.Tags,
Warranty = p17.Warranty,
BeDisplayed = p17.BeDisplayed,
HasExpressDelivery = p17.HasExpressDelivery,
MaxOrderCount = p17.MaxOrderCount,
Cost = p17.Cost,
PackingCost = p17.PackingCost,
BrandId = p17.BrandId,
BrandName = p17.Brand == null ? null : p17.Brand.Name,
CategoryId = p17.CategoryId,
CategoryName = p17.Category == null ? null : p17.Category.Name,
Specifications = funcMain7(p17.Specifications),
Reviews = funcMain8(p17.Reviews),
Files = funcMain9(p17.Files),
@ -121,7 +147,15 @@ namespace NetinaShop.Domain.Mappers
result.ExpertCheck = p21.ExpertCheck;
result.Tags = p21.Tags;
result.Warranty = p21.Warranty;
result.BeDisplayed = p21.BeDisplayed;
result.HasExpressDelivery = p21.HasExpressDelivery;
result.MaxOrderCount = p21.MaxOrderCount;
result.Cost = p21.Cost;
result.PackingCost = p21.PackingCost;
result.BrandId = p21.BrandId;
result.BrandName = p21.Brand == null ? null : p21.Brand.Name;
result.CategoryId = p21.CategoryId;
result.CategoryName = p21.Category == null ? null : p21.Category.Name;
result.Specifications = funcMain10(p21.Specifications, result.Specifications);
result.Reviews = funcMain11(p21.Reviews, result.Reviews);
result.Files = funcMain12(p21.Files, result.Files);
@ -137,7 +171,15 @@ namespace NetinaShop.Domain.Mappers
ExpertCheck = p29.ExpertCheck,
Tags = p29.Tags,
Warranty = p29.Warranty,
BeDisplayed = p29.BeDisplayed,
HasExpressDelivery = p29.HasExpressDelivery,
MaxOrderCount = p29.MaxOrderCount,
Cost = p29.Cost,
PackingCost = p29.PackingCost,
BrandId = p29.BrandId,
BrandName = p29.Brand.Name,
CategoryId = p29.CategoryId,
CategoryName = p29.Category.Name,
Specifications = p29.Specifications.Select<Specification, SpecificationSDto>(p30 => new SpecificationSDto()
{
Title = p30.Title,
@ -145,7 +187,7 @@ namespace NetinaShop.Domain.Mappers
Value = p30.Value,
IsFeature = p30.IsFeature,
ProductId = p30.ProductId,
ParentId = p30.ParentId,
ParentId = p30.ParentId == null ? default(Guid) : (Guid)p30.ParentId,
Id = p30.Id
}).ToList<SpecificationSDto>(),
Reviews = p29.Reviews.Select<Review, ReviewSDto>(p31 => new ReviewSDto()
@ -312,7 +354,7 @@ namespace NetinaShop.Domain.Mappers
Value = item.Value,
IsFeature = item.IsFeature,
ProductId = item.ProductId,
ParentId = item.ParentId,
ParentId = (Guid?)item.ParentId,
Id = item.Id
});
i++;
@ -402,7 +444,7 @@ namespace NetinaShop.Domain.Mappers
Value = item.Value,
IsFeature = item.IsFeature,
ProductId = item.ProductId,
ParentId = item.ParentId,
ParentId = (Guid?)item.ParentId,
Id = item.Id
});
i++;
@ -492,7 +534,7 @@ namespace NetinaShop.Domain.Mappers
Value = item.Value,
IsFeature = item.IsFeature,
ProductId = item.ProductId,
ParentId = item.ParentId,
ParentId = item.ParentId == null ? default(Guid) : (Guid)item.ParentId,
Id = item.Id
});
i++;
@ -582,7 +624,7 @@ namespace NetinaShop.Domain.Mappers
Value = item.Value,
IsFeature = item.IsFeature,
ProductId = item.ProductId,
ParentId = item.ParentId,
ParentId = item.ParentId == null ? default(Guid) : (Guid)item.ParentId,
Id = item.Id
});
i++;

View File

@ -16,7 +16,7 @@ namespace NetinaShop.Domain.Mappers
Value = p1.Value,
IsFeature = p1.IsFeature,
ProductId = p1.ProductId,
ParentId = p1.ParentId,
ParentId = (Guid?)p1.ParentId,
Id = p1.Id
};
}
@ -33,7 +33,7 @@ namespace NetinaShop.Domain.Mappers
result.Value = p2.Value;
result.IsFeature = p2.IsFeature;
result.ProductId = p2.ProductId;
result.ParentId = p2.ParentId;
result.ParentId = (Guid?)p2.ParentId;
result.Id = p2.Id;
return result;
@ -47,7 +47,7 @@ namespace NetinaShop.Domain.Mappers
Value = p4.Value,
IsFeature = p4.IsFeature,
ProductId = p4.ProductId,
ParentId = p4.ParentId,
ParentId = p4.ParentId == null ? default(Guid) : (Guid)p4.ParentId,
Id = p4.Id
};
}
@ -64,7 +64,7 @@ namespace NetinaShop.Domain.Mappers
result.Value = p5.Value;
result.IsFeature = p5.IsFeature;
result.ProductId = p5.ProductId;
result.ParentId = p5.ParentId;
result.ParentId = p5.ParentId == null ? default(Guid) : (Guid)p5.ParentId;
result.Id = p5.Id;
return result;
@ -76,7 +76,7 @@ namespace NetinaShop.Domain.Mappers
Value = p7.Value,
IsFeature = p7.IsFeature,
ProductId = p7.ProductId,
ParentId = p7.ParentId,
ParentId = p7.ParentId == null ? default(Guid) : (Guid)p7.ParentId,
Id = p7.Id
};
}

View File

@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.305.17" />
<PackageReference Include="Refit" Version="7.0.0" />
</ItemGroup>
@ -19,14 +20,16 @@
<ItemGroup>
<Folder Include="Models\" />
<Folder Include="Services\" />
</ItemGroup>
<ItemGroup>
<Using Include="Amazon.S3" />
<Using Include="Amazon.S3.Model" />
<Using Include="Microsoft.Extensions.Hosting" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Microsoft.Extensions.Options" />
<Using Include="NetinaShop.Common.Extensions" />
<Using Include="NetinaShop.Common.Models" />
<Using Include="NetinaShop.Common.Models.Api" />
<Using Include="NetinaShop.Common.Models.Exception" />

View File

@ -0,0 +1,114 @@
using NetinaShop.Domain.Dtos.SmallDtos;
using NetinaShop.Domain.Enums;
namespace NetinaShop.Infrastructure.Services;
public class StorageService : IStorageService
{
private IAmazonS3? _s3Client;
private string _bucketName = "vesmeh-content";
private IAmazonS3 GetClientAsync()
{
var awsCredentials = new Amazon.Runtime.BasicAWSCredentials("979313b7-30fb-40ff-94d8-d0390d3fa876", "d37a1cc6acfea3a6f92c538ef0f6601f1edcdc9143942b6470e5d1032aa6bfe2");
var config = new AmazonS3Config { ServiceURL = "https://s3.ir-thr-at1.arvanstorage.ir" };
_s3Client = new AmazonS3Client(awsCredentials, config);
return _s3Client;
}
public async Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, byte[] fileBytes)
{
using var memorySteam = new MemoryStream(fileBytes);
var client = GetClientAsync();
var fileEx = fileName.Split('.').Last();
fileName = fileName.Split('.').First();
fileName = fileName + "_" + StringExtensions.GetId(5) + "_" + DateTime.Today.ToString("yyyy-MM-dd-HH-mm-ss") + "." + fileEx;
var putRequest = new PutObjectRequest
{
BucketName = _bucketName,
Key = Path.Combine(filePath, fileName),
ContentType = contentType,
InputStream = memorySteam,
CannedACL = S3CannedACL.PublicRead
};
putRequest.Metadata.Add("x-amz-meta-title", fileName);
PutObjectResponse response = await client.PutObjectAsync(putRequest);
return fileName;
}
public async Task<string> UploadObjectFromFileAsync(string fileName, string filePath, string contentType, Stream fileStream)
{
var client = GetClientAsync();
var fileEx = fileName.Split('.').Last();
fileName = fileName.Split('.').First();
fileName = fileName + "_" + StringExtensions.GetId(5) + "_" + DateTimeExtensions.DateTimeToUnixTimeStamp(DateTime.Now) + "." + fileEx;
var putRequest = new PutObjectRequest
{
BucketName = _bucketName,
Key = Path.Combine(filePath, fileName),
ContentType = contentType,
InputStream = fileStream,
CannedACL = S3CannedACL.PublicRead
};
putRequest.Metadata.Add("x-amz-meta-title", fileName);
PutObjectResponse response = await client.PutObjectAsync(putRequest);
return fileName;
}
public async Task<List<StorageFileSDto>> GetStorageFiles(StorageFileType fileType)
{
var client = GetClientAsync();
ListObjectsRequest request = new() { BucketName = _bucketName};
switch (fileType)
{
case StorageFileType.Image:
request.Prefix = "Images/Med";
break;
case StorageFileType.Video:
request.Prefix = "Videos";
break;
default:
break;
}
var files = new List<StorageFileSDto>();
do
{
ListObjectsResponse response = await client.ListObjectsAsync(request);
// Process the response.
foreach (var s3Object in response.S3Objects.Where(s=>s.Size>0))
{
files.Add(new StorageFileSDto
{
FileLocation = s3Object.Key,
FileName = s3Object.Key.Split('/').Last()
});
}
// If the response is truncated, set the marker to get the next
// set of keys.
if (response.IsTruncated)
{
request.Marker = response.NextMarker;
}
else
{
request = null;
}
} while (request != null);
return files.OrderByDescending(o=>o.CreatedAt).ToList();
}
}

View File

@ -32,6 +32,10 @@ public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, List<Pr
_ => products
};
}
else
{
products = products.OrderByDescending(p => p.CreatedAt);
}
var productSDtos = await products.Skip(request.Page * 20)
.Take(20)

View File

@ -31,7 +31,7 @@ public class UpdateProductCommandHandler : IRequestHandler<UpdateProductCommand,
.Where(s => s.ProductId == ent.Id).ToListAsync(cancellationToken);
foreach (var dbSpecification in dbSpecifications)
{
if (request.Specifications.Any(s => s.Id == dbSpecification.Id))
if (request.Specifications.FirstOrDefault(s => s.Id != dbSpecification.Id) == null)
{
_repositoryWrapper.SetRepository<Specification>().Delete(dbSpecification);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);
@ -49,7 +49,7 @@ public class UpdateProductCommandHandler : IRequestHandler<UpdateProductCommand,
.Where(s => s.ProductId == ent.Id).ToListAsync(cancellationToken);
foreach (var dbFile in dbFiles)
{
if (request.Files.Any(s => s.Id == dbFile.Id))
if (request.Files.FirstOrDefault(s => s.Id == dbFile.Id)==null)
{
_repositoryWrapper.SetRepository<ProductStorageFile>().Delete(dbFile);
await _repositoryWrapper.SaveChangesAsync(cancellationToken);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,68 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NetinaShop.Repository.Migrations
{
/// <inheritdoc />
public partial class editSpecificationNullParent : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Specifications_Specifications_ParentId",
schema: "public",
table: "Specifications");
migrationBuilder.AlterColumn<Guid>(
name: "ParentId",
schema: "public",
table: "Specifications",
type: "uuid",
nullable: true,
oldClrType: typeof(Guid),
oldType: "uuid");
migrationBuilder.AddForeignKey(
name: "FK_Specifications_Specifications_ParentId",
schema: "public",
table: "Specifications",
column: "ParentId",
principalSchema: "public",
principalTable: "Specifications",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Specifications_Specifications_ParentId",
schema: "public",
table: "Specifications");
migrationBuilder.AlterColumn<Guid>(
name: "ParentId",
schema: "public",
table: "Specifications",
type: "uuid",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "uuid",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "FK_Specifications_Specifications_ParentId",
schema: "public",
table: "Specifications",
column: "ParentId",
principalSchema: "public",
principalTable: "Specifications",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}

View File

@ -802,7 +802,7 @@ namespace NetinaShop.Repository.Migrations
b.Property<string>("ModifiedBy")
.HasColumnType("text");
b.Property<Guid>("ParentId")
b.Property<Guid?>("ParentId")
.HasColumnType("uuid");
b.Property<Guid>("ProductId")
@ -1409,9 +1409,7 @@ namespace NetinaShop.Repository.Migrations
{
b.HasOne("NetinaShop.Domain.Entities.Products.Specification", "Parent")
.WithMany("Children")
.HasForeignKey("ParentId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
.HasForeignKey("ParentId");
b.HasOne("NetinaShop.Domain.Entities.Products.Product", "Product")
.WithMany("Specifications")