diff --git a/ComponentPractice/ComponentPractice/ComponentPractice.csproj b/ComponentPractice/ComponentPractice/ComponentPractice.csproj
index 620d767..c2b1527 100644
--- a/ComponentPractice/ComponentPractice/ComponentPractice.csproj
+++ b/ComponentPractice/ComponentPractice/ComponentPractice.csproj
@@ -8,6 +8,7 @@
+
diff --git a/ComponentPractice/ComponentPractice/Components/Layout/NavMenu.razor b/ComponentPractice/ComponentPractice/Components/Layout/NavMenu.razor
index 0a654dc..f2a91dc 100644
--- a/ComponentPractice/ComponentPractice/Components/Layout/NavMenu.razor
+++ b/ComponentPractice/ComponentPractice/Components/Layout/NavMenu.razor
@@ -35,6 +35,7 @@
+
diff --git a/ComponentPractice/ComponentPractice/Components/Pages/DataGridCsvExport.razor b/ComponentPractice/ComponentPractice/Components/Pages/DataGridCsvExport.razor
new file mode 100644
index 0000000..0e183d3
--- /dev/null
+++ b/ComponentPractice/ComponentPractice/Components/Pages/DataGridCsvExport.razor
@@ -0,0 +1,45 @@
+@page "/datagridcsvexport";
+@using ComponentPractice.Services
+
+@inject InMemoryData data;
+@inject ExportService exportService;
+
+@rendermode RenderMode.InteractiveServer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ RadzenDataGrid grid2;
+
+ IEnumerable employees2;
+
+ protected override async Task OnInitializedAsync()
+ {
+ employees2 = await data.GetFixedEmployeeDataAsync();
+ }
+
+ // table: FixedEmployees
+ public void Export(string type)
+ {
+ exportService.Export("FixedEmployees", type, new Query()
+ {
+ OrderBy = grid2.Query.OrderBy,
+ Filter = grid2.Query.Filter,
+ Select = string.Join(",", grid2.ColumnsCollection.Where(c => c.GetVisible() && !string.IsNullOrEmpty(c.Property))
+ .Select(c => c.Property.Contains(".") ? $"{c.Property} as {c.Property.Replace(".", "_")}" : c.Property))
+ });
+ }
+}
diff --git a/ComponentPractice/ComponentPractice/Controllers/ExportController.cs b/ComponentPractice/ComponentPractice/Controllers/ExportController.cs
new file mode 100644
index 0000000..bd99ccf
--- /dev/null
+++ b/ComponentPractice/ComponentPractice/Controllers/ExportController.cs
@@ -0,0 +1,30 @@
+using ComponentPractice.Data;
+using ComponentPractice.Models;
+using ComponentPractice.Services;
+using Microsoft.AspNetCore.Mvc;
+
+namespace ComponentPractice.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class ExportController : ControllerBase
+ {
+ private InMemoryData _inMemoryData;
+ private ToCSVService _toCsvService;
+
+ public ExportController(InMemoryData inMemoryData, ToCSVService toCsvService)
+ {
+ _inMemoryData = inMemoryData;
+ _toCsvService = toCsvService;
+ }
+
+ //[HttpGet("/InMemoryData/FixedEmployees/csv")] // 절대경로 주의!
+ [HttpGet("InMemoryData/FixedEmployees/csv")]
+ public async Task ExportToCSV()
+ {
+ List employees = await _inMemoryData.GetFixedEmployeeDataAsync();
+ IQueryable query = _toCsvService.ApplyQuery(employees.AsQueryable(), Request.Query);
+ return _toCsvService.ToCSV(query);
+ }
+ }
+}
diff --git a/ComponentPractice/ComponentPractice/Data/InMemoryData.cs b/ComponentPractice/ComponentPractice/Data/InMemoryData.cs
index 8f613bb..cc86ef4 100644
--- a/ComponentPractice/ComponentPractice/Data/InMemoryData.cs
+++ b/ComponentPractice/ComponentPractice/Data/InMemoryData.cs
@@ -5,43 +5,60 @@ namespace ComponentPractice.Data
{
public class InMemoryData
{
+ private List _fixedSample;
+
+ Random _random = new Random();
+ string[] _firstNames = new[] { "John", "Jane", "Michael", "Emily", "Robert", "Linda", "William", "Jessica", "David", "Sarah" };
+ string[] _lastNames = new[] { "Smith", "Johnson", "Brown", "Williams", "Jones", "Garcia", "Miller", "Davis", "Martinez", "Hernandez" };
+ string[] _jobs = new[] { "Developer", "Designer", "Manager", "Analyst", "Consultant", "Administrator", "Engineer", "Specialist", "Technician", "Coordinator" };
+ string[] _titles = new[] { "Junior", "Mid", "Senior", "Lead", "Chief" };
+ string[] _cities = new[] { "New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia", "San Antonio", "San Diego", "Dallas", "San Jose" };
+ string[] _regions = new[] { "NY", "CA", "IL", "TX", "AZ", "PA", "TX", "CA", "TX", "CA" };
+ string[] _counties = new[] { "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA" };
+
+ public InMemoryData()
+ {
+ _fixedSample = GenerateEmployData(100);
+ }
+
public async Task> GetEmployeeDataAsync(int count)
{
- var employees = new List();
- var random = new Random();
- var firstNames = new[] { "John", "Jane", "Michael", "Emily", "Robert", "Linda", "William", "Jessica", "David", "Sarah" };
- var lastNames = new[] { "Smith", "Johnson", "Brown", "Williams", "Jones", "Garcia", "Miller", "Davis", "Martinez", "Hernandez" };
- var jobs = new[] { "Developer", "Designer", "Manager", "Analyst", "Consultant", "Administrator", "Engineer", "Specialist", "Technician", "Coordinator" };
- var titles = new[] { "Junior", "Mid", "Senior", "Lead", "Chief" };
- var cities = new[] { "New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia", "San Antonio", "San Diego", "Dallas", "San Jose" };
- var regions = new[] { "NY", "CA", "IL", "TX", "AZ", "PA", "TX", "CA", "TX", "CA" };
- var counties = new[] { "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA" };
+ return await GenerateEmployData(count).ToDynamicListAsync();
+ }
+
+ public async Task> GetFixedEmployeeDataAsync()
+ {
+ return await _fixedSample.ToDynamicListAsync();
+ }
+ private List GenerateEmployData(int count)
+ {
+ var employees = new List();
for (int i = 0; i < count; i++)
{
var employee = new Employee
{
ID = i + 1,
Photo = $"https://randomuser.me/api/portraits/med/{(i % 2 == 0 ? "men" : "women")}/{i}.jpg",
- FirstName = firstNames[random.Next(firstNames.Length)],
- LastName = lastNames[random.Next(lastNames.Length)],
- Job = jobs[random.Next(jobs.Length)],
- Title = titles[random.Next(titles.Length)],
- BirthDate = new DateTime(random.Next(1950, 2000), random.Next(1, 13), random.Next(1, 29)),
- HireDate = DateTime.Today.AddDays(-random.Next(1, 3650)),
- Address = $"{random.Next(1, 9999)} Main St",
- City = cities[random.Next(cities.Length)],
- Region = regions[random.Next(regions.Length)],
- PostalCode = $"{random.Next(10000, 99999)}",
- Country = counties[random.Next(counties.Length)],
- Phone = $"{random.Next(100, 999)}-555-{random.Next(1000, 9999)}",
- Extension = $"{random.Next(100, 9999)}",
+ FirstName = _firstNames[_random.Next(_firstNames.Length)],
+ LastName = _lastNames[_random.Next(_lastNames.Length)],
+ Job = _jobs[_random.Next(_jobs.Length)],
+ Title = _titles[_random.Next(_titles.Length)],
+ BirthDate = new DateTime(_random.Next(1950, 2000), _random.Next(1, 13), _random.Next(1, 29)),
+ HireDate = DateTime.Today.AddDays(-_random.Next(1, 3650)),
+ Address = $"{_random.Next(1, 9999)} Main St",
+ City = _cities[_random.Next(_cities.Length)],
+ Region = _regions[_random.Next(_regions.Length)],
+ PostalCode = $"{_random.Next(10000, 99999)}",
+ Country = _counties[_random.Next(_counties.Length)],
+ Phone = $"{_random.Next(100, 999)}-555-{_random.Next(1000, 9999)}",
+ Extension = $"{_random.Next(100, 9999)}",
Notes = "Generated demo data"
};
employees.Add(employee);
}
- return await employees.ToDynamicListAsync();
+ return employees;
}
}
}
diff --git a/ComponentPractice/ComponentPractice/Program.cs b/ComponentPractice/ComponentPractice/Program.cs
index 57c2d42..eef2770 100644
--- a/ComponentPractice/ComponentPractice/Program.cs
+++ b/ComponentPractice/ComponentPractice/Program.cs
@@ -1,6 +1,7 @@
using ComponentPractice.Components;
using ComponentPractice.Data;
using ComponentPractice.Services;
+using Microsoft.AspNetCore.Builder;
using Radzen;
namespace ComponentPractice
@@ -15,10 +16,19 @@ namespace ComponentPractice
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
+ // Add controllers
+ builder.Services.AddControllers();
+
+ // Add swagger
+ builder.Services.AddEndpointsApiExplorer();
+ builder.Services.AddSwaggerGen();
+
builder.Services.AddRadzenComponents();
- builder.Services.AddScoped();
+ builder.Services.AddSingleton();
builder.Services.AddScoped();
+ builder.Services.AddScoped();
+ builder.Services.AddScoped();
var app = builder.Build();
@@ -30,6 +40,13 @@ namespace ComponentPractice
app.UseHsts();
}
+ // Active swagger on Development Env.
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseSwagger();
+ app.UseSwaggerUI();
+ }
+
app.UseHttpsRedirection();
app.UseStaticFiles();
@@ -38,6 +55,8 @@ namespace ComponentPractice
app.MapRazorComponents()
.AddInteractiveServerRenderMode();
+ app.MapControllers();
+
//app.Urls.Add("http://*:5000");
app.Run();
diff --git a/ComponentPractice/ComponentPractice/Services/ExportService.cs b/ComponentPractice/ComponentPractice/Services/ExportService.cs
new file mode 100644
index 0000000..e1dcab5
--- /dev/null
+++ b/ComponentPractice/ComponentPractice/Services/ExportService.cs
@@ -0,0 +1,20 @@
+using Microsoft.AspNetCore.Components;
+using Radzen;
+
+namespace ComponentPractice.Services
+{
+ public class ExportService
+ {
+ private readonly NavigationManager _navigationManager;
+
+ public ExportService(NavigationManager navigationManager)
+ {
+ _navigationManager = navigationManager;
+ }
+
+ public void Export(string table, string type, Query query = null)
+ {
+ _navigationManager.NavigateTo(query != null ? query.ToUrl($"/export/InMemoryData/{table}/{type}") : $"/export/InMemoryData/{table}/{type}", true);
+ }
+ }
+}
diff --git a/ComponentPractice/ComponentPractice/Services/ToCSVService.cs b/ComponentPractice/ComponentPractice/Services/ToCSVService.cs
new file mode 100644
index 0000000..ee45964
--- /dev/null
+++ b/ComponentPractice/ComponentPractice/Services/ToCSVService.cs
@@ -0,0 +1,126 @@
+using Microsoft.AspNetCore.Mvc;
+using System.Globalization;
+using System.Linq;
+using System.Linq.Dynamic.Core;
+using System.Reflection;
+using System.Text;
+
+namespace ComponentPractice.Services
+{
+ public class ToCSVService
+ {
+ public IQueryable ApplyQuery(IQueryable items, IQueryCollection query = null) where T: class
+ {
+ if (query == null)
+ return items;
+
+ //// For EF
+ //if (query.ContainsKey("$expand"))
+ //{
+ // var propertiesToExpand = query["$expand"].ToString().Split(',');
+ // foreach (var p in propertiesToExpand)
+ // {
+ // items = items.Include(p);
+ // }
+ //}
+
+ var filter = query.ContainsKey("$filter") ? query["$filter"].ToString() : null;
+ if (!string.IsNullOrEmpty(filter))
+ {
+ items = items.Where(filter);
+ }
+
+ if (query.ContainsKey("$orderBy"))
+ {
+ items = items.OrderBy(query["$orderBy"].ToString());
+ }
+
+ if (query.ContainsKey("$skip"))
+ {
+ items = items.Skip(int.Parse(query["$skip"].ToString()));
+ }
+
+ if (query.ContainsKey("$top"))
+ {
+ items = items.Take(int.Parse(query["$top"].ToString()));
+ }
+
+ if (query.ContainsKey("$select"))
+ {
+ return items.Select($"new ({query["$select"].ToString()})");
+ }
+
+ return items;
+ }
+
+ public FileStreamResult ToCSV(IQueryable query, string fileName = null)
+ {
+ var columns = GetProperties(query.ElementType);
+
+ var sb = new StringBuilder();
+ foreach (var item in query)
+ {
+ var row = new List();
+ foreach (var col in columns)
+ {
+ if (col.Value == typeof(DateTime))
+ row.Add(((DateTime)GetValue(item, col.Key)).ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
+ else
+ row.Add($"{GetValue(item, col.Key)}".Trim());
+ }
+
+ sb.AppendLine(string.Join(",", row.ToArray()));
+ }
+
+ var result = new FileStreamResult(new MemoryStream(Encoding.UTF8.GetBytes($"{string.Join(",", columns.Select(c => c.Key))}{System.Environment.NewLine}{sb.ToString()}")), "text/csv");
+ result.FileDownloadName = $"{(!string.IsNullOrEmpty(fileName) ? fileName : "Export")}.csv";
+
+ return result;
+ }
+
+ public static object GetValue(object target, string name)
+ {
+ return target.GetType().GetProperty(name).GetValue(target);
+ }
+
+ public static IEnumerable> GetProperties(Type type)
+ {
+ return type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(p => p.CanRead && IsSimpleType(p.PropertyType)).Select(p => new KeyValuePair(p.Name, p.PropertyType));
+ }
+
+ public static bool IsSimpleType(Type type)
+ {
+ var underlyingType = type.IsGenericType &&
+ type.GetGenericTypeDefinition() == typeof(Nullable<>) ?
+ Nullable.GetUnderlyingType(type) : type;
+
+ if (underlyingType == typeof(System.Guid))
+ return true;
+
+ var typeCode = Type.GetTypeCode(underlyingType);
+
+ switch (typeCode)
+ {
+ case TypeCode.Boolean:
+ case TypeCode.Byte:
+ case TypeCode.Char:
+ case TypeCode.DateTime:
+ case TypeCode.Decimal:
+ case TypeCode.Double:
+ case TypeCode.Int16:
+ case TypeCode.Int32:
+ case TypeCode.Int64:
+ case TypeCode.SByte:
+ case TypeCode.Single:
+ case TypeCode.String:
+ case TypeCode.UInt16:
+ case TypeCode.UInt32:
+ case TypeCode.UInt64:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}