클라우드 스토리지 관리 애플리케이션을 위한 C# 클래스 디자인
요약
최근에 했던 C# 클래스 디자인 패턴을 공유하고 싶습니다. 저는 다양한 디자인 패턴이 있고 단 하나의 최선의 방법이 없다는 것을 알고 있지만 다양한 디자인 패턴을 배우는 것은 확장 가능하고 확장 가능한 애플리케이션을 개발하는 데 도움이 됩니다.
샘플 코드: blob-console-app
목차
Codes
건축물
- Operators place files which they want to upload to Azure Blob Storage.
- The .NET console app checks the pointed directory in a certain interval and if those files are valid, it uploads to the Azure Blob Storage.
- The app deletes those files from the local file system once the files are uploaded.
- The app extracts metadata and attaches the blob on cloud.
- Currently the file types are
logs
and events
which are located different directory in the local file system.
클래스 디자인
I decided to use Interface, Abstract, and sub classes because:
- Currently file types are only
Logs
and Events
, but the system will extend. For example, DeviceInfo
and Requests
will be added in the future.
- It is better for future extended file types to divide the base features and ones with different requirements for each file type so the system reuses the base class features in the future.
- In order to avoid changing the base class when adding new file types, an Interface class and abstract class defines base properties, and sub classes define metadata properties.
-
FolderName
is a base property but it points to the directory for each file type. It is defined as Abstract property because Sub classes must override it and define for their own.
-
JudgeValidFile()
and GetMetadata()
have to be implemented in sub classes, and then they are defined as Abstract. UploadBlobAsync()
, UploadMetadataAsync()
, and DeleteFiles()
are defined as Abstract because they are implemented in the base class but can be overriden in sub classes accordingly.
코드
파일 유형 인스턴스 나열
- Currently the system has only two file types,
Logs
and Events
. If you want to add another file type, only thing to do is to implement a new class for the file type and add a new instance here.
Program.cs
List<IUploadData> uploadDataList = new ()
{
new Logs(),
new Events(),
};
foreach 대신에
for (int i = 0; i < uploadDataList.Count; ++i)
processes differently depending on class instance added to the list.
It uses for
instead of foreach
because the class method GetMetadata()
gets metadata and put into its own property
Program.cs
uploadDataList[i] = uploadDataList[i].GetMetadata();
의존성 주입
logs
and events
which are located different directory in the local file system.I decided to use Interface, Abstract, and sub classes because:
- Currently file types are only
Logs
andEvents
, but the system will extend. For example,DeviceInfo
andRequests
will be added in the future. - It is better for future extended file types to divide the base features and ones with different requirements for each file type so the system reuses the base class features in the future.
- In order to avoid changing the base class when adding new file types, an Interface class and abstract class defines base properties, and sub classes define metadata properties.
-
FolderName
is a base property but it points to the directory for each file type. It is defined as Abstract property because Sub classes must override it and define for their own. -
JudgeValidFile()
andGetMetadata()
have to be implemented in sub classes, and then they are defined as Abstract.UploadBlobAsync()
,UploadMetadataAsync()
, andDeleteFiles()
are defined as Abstract because they are implemented in the base class but can be overriden in sub classes accordingly.
코드
파일 유형 인스턴스 나열
- Currently the system has only two file types,
Logs
and Events
. If you want to add another file type, only thing to do is to implement a new class for the file type and add a new instance here.
Program.cs
List<IUploadData> uploadDataList = new ()
{
new Logs(),
new Events(),
};
foreach 대신에
for (int i = 0; i < uploadDataList.Count; ++i)
processes differently depending on class instance added to the list.
It uses for
instead of foreach
because the class method GetMetadata()
gets metadata and put into its own property
Program.cs
uploadDataList[i] = uploadDataList[i].GetMetadata();
의존성 주입
Logs
and Events
. If you want to add another file type, only thing to do is to implement a new class for the file type and add a new instance here.List<IUploadData> uploadDataList = new ()
{
new Logs(),
new Events(),
};
for (int i = 0; i < uploadDataList.Count; ++i)
processes differently depending on class instance added to the list.
It uses for
instead of foreach
because the class method GetMetadata()
gets metadata and put into its own property
uploadDataList[i] = uploadDataList[i].GetMetadata();
BlobClient
다른 파일 및 다른 파일 유형에 대해 인스턴스화하려면 파일 이름과 디렉토리가 필요합니다. 생성자에서 인스턴스를 만들 수 없습니다. Program.cs
List<IUploadData> uploadDataList = new()
{
new Logs(),
new Events(),
};
while (true)
{
for (int i = 0; i < uploadDataList.Count; ++i)
{
uploadDataList[i].FileFullPaths = Directory.GetFiles(Path.Combine(localPath, uploadDataList[i].FolderName), "*", SearchOption.AllDirectories);
foreach (string fileFullPath in uploadDataList[i].FileFullPaths)
{
uploadDataList[i].FileFullPath = fileFullPath;
uploadDataList[i].FileName = Path.GetFileName(fileFullPath);
uploadDataList[i].BlobClient = containerClient.GetBlobClient($"/{uploadDataList[i].FolderName}/{DateTime.Now.Year}/{DateTime.Now.Month}/{uploadDataList[i].FileName}");
업로드 파일 검증
- If a file found in the directory is not valid to upload, it stops the for loop and goes to a next file.
- How to valid files is different depending on file types. That is why
JudgeValidFile()
is overriden in each file type class.
uploadDataList[i].JudgeValidFile();
if (uploadDataList[i].IsValidFile is false) continue;
Logs.cs
this.IsValidFile = (Path.GetExtension(this.FileName) == ".csv" && this.FileFullPaths.Contains(this.FileFullPath + ".json"));
Events.cs
this.IsValidFile = (Path.GetExtension(this.FileName) == ".json");
제네릭 클래스
I wanted to use a generic class for UploadMetadataAsync()
because it has to retrieve the properties dynamically depending on the sub class. I thought UploadMetadataAsync<uploadDataList[i].GetType()>()
works but actually not. After investigating sometime, I gave up using Generic class here.
Generic class
public virtual async Task UploadMetadataAsync<T>() where T : IUploadData
{
PropertyInfo[] propertyCollection = typeof(T).GetProperties();
foreach (PropertyInfo property in propertyCollection)
{
blobMetadata[property.Name.ToString()] = property.GetValue(this).ToString();
}
}
Caller
await uploadDataList[i].UploadMetadataAsync<uploadDataList[i].GetType()>().ConfigureAwait(true);
메타데이터를 사전으로
- I realized I cannot use Generic class, and I was thinking how to insert only metadata properties into the dictionary for uploading. What I did is to see the difference of property between Abstract class and sub class. For example, the Abstract class
UploadData
has only the base propertiesFolderName
,IsValidFile
,FileFullPaths
,FileFullPath
,FileName
,BlobClient
, but the sub classLogs
has the metadata propertiesBeginTime
,EndTime
,Temperature
,Humidity
,Location
in addition to the base properties.
this.blobMetadata.Clear();
PropertyInfo[] propertiesIUploadData = typeof(IUploadData).GetProperties();
IEnumerable<PropertyInfo> propertiesThis = this.GetType().GetRuntimeProperties();
foreach (PropertyInfo propertyThis in propertiesThis)
{
int count = 0;
foreach (PropertyInfo propertyIUploadData in propertiesIUploadData)
{
if (propertyThis.Name == propertyIUploadData.Name) ++count;
}
if (count == 0)
{
this.blobMetadata[propertyThis.Name.ToString()] = propertyThis.GetValue(this).ToString();
}
}
await this.BlobClient.SetMetadataAsync(this.blobMetadata).ConfigureAwait(false);
Json 역직렬화
- It is elegant to deserialize the value from a json file because I do not need to change the code
JsonSerializer.Deserialize<Logs>(jsonString)!;
for any model property. However, the problem was that the sub class has both the base property and metadata property, and when in deserialization, it sets the metadata properties with the value from a json file and also the base properties with null. - I do not like this, but I did not come up with an elegant workaround, and what I implemented is to keep the value to local variables and then set them back after deserialization.
json file example
{
"BeginTime": "2022-03-07T12:21:55Z",
"EndTime": "2022-03-07T14:01:36Z",
"Temperature": 25,
"Humidity": 63,
"Location": "Tokyo"
}
Logs class property
public string FolderName { get; }
public bool IsValidFile { get; set; }
public string[] FileFullPaths { get; set; }
public string FileFullPath { get; set; }
public string FileName { get; set; }
public BlobClient BlobClient { get; set; }
public DateTime BeginTime { get; set; }
public DateTime EndTime { get; set; }
public int Temperature { get; set; }
public int Humidity { get; set; }
public string Location { get; set; }
public override IUploadData GetMetadata()
{
bool IsValidFile = this.IsValidFile;
string[] FileFullPaths = this.FileFullPaths;
string FileFullPath = this.FileFullPath;
string FileName = this.FileName;
BlobClient BlobClient = this.BlobClient;
string jsonString = File.ReadAllText(this.FileFullPath + ".json");
Logs log = JsonSerializer.Deserialize<Logs>(jsonString)!;
log.IsValidFile = IsValidFile;
log.FileFullPaths = FileFullPaths;
log.FileFullPath = FileFullPath;
log.FileName = FileName;
log.BlobClient = BlobClient;
return log;
}
Reference
이 문제에 관하여(클라우드 스토리지 관리 애플리케이션을 위한 C# 클래스 디자인), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/koheikawata/c-class-design-for-cloud-storage-management-application-3pdn텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)