סקריפט ליצירה אוטומטית של מחלקות C# מבסיס נתונים ב-SQL Server
-
טוב, אז אני מניח שהכותרת די ברורה:
כידוע, לפי הספר, כל טבלה ב-SQL קלאסי אמורה לייצג יישות (Entity), שבד"כ מיוצגת על ידי מחלקה (Class) תואמת בתוכנה.
כל פעם שאני בא להמיר איזה פרוייקט מאקסס ל-C# אני נתקל בעבודה הסיזיפית הזו.חיפשתי פיתרון שיתן לי ליצור בקלות את כל המודלים של הנתונים מתוך הגדרות הטבלאות.
יש איזו דרך מתוסבכת לעשות זאת על ידי EntityFramework, אבל עד עכשיו הייתי משתמש באתר CodVerter ליצירה של מחלקה על ידי הכנסה של הגדרות הסקריפט SQL שיוצר אותה,
ז"א אם אני מכניס את הסקריפט הבא:
CREATE TABLE [dbo].[Sender] ( [SenderID] INT NOT NULL, [SenderName] NVARCHAR (50) NOT NULL );
אני מקבל בתמורה:
public class Sender { public int SenderID {get;set;} public string SenderName {get;set;} }
וזה כמובן נחמד כשמדובר בכמה טבלאות בודדות, אבל כשצריך להעביר עשרות טבלאות, זה מאוד לא נוח.
אבל היום גיליתי סקריפט נהדר ב-CodeProject, שיוצר בבת אחת את כל הקלאסים עבור כל הטבלאות ב-DB, וחסך לי כאב ראש גדול.
אז הנה הוא לפניכם (בתיקון שגיאה קטנה שהיתה במקור): פשוט לפתוח את בסיס הנתונים ב-SSMS, להדביק את הסקריפט הבא, להריץ, לקבל את כל הקלאסים על מגש של כסף, ולהגיש חם עם כוס נס-קפה:
SET NOCOUNT ON; DECLARE @table_name NVARCHAR(250), @message VARCHAR(80); DECLARE table_cursor CURSOR FOR select distinct tab.name as table_name from sys.tables as tab inner join sys.columns as col on tab.object_id = col.object_id left join sys.types as t on col.user_type_id = t.user_type_id order by table_name; OPEN table_cursor FETCH NEXT FROM table_cursor INTO @table_name WHILE @@FETCH_STATUS = 0 BEGIN PRINT ' ' --SELECT @message = 'Table Name: ' + -- @table_name --PRINT @message --declare @TableName sysname = 'TableName' declare @Result varchar(max) = 'public class ' + @table_name + ' {' select @Result = @Result + ' public ' + ColumnType + NullableSign + ' ' + ColumnName + ' { get; set; }' from ( select replace(col.name, ' ', '_') ColumnName, column_id ColumnId, case typ.name when 'bigint' then 'long' when 'binary' then 'byte[]' when 'bit' then 'bool' when 'char' then 'string' when 'date' then 'DateTime' when 'datetime' then 'DateTime' when 'datetime2' then 'DateTime' when 'datetimeoffset' then 'DateTimeOffset' when 'decimal' then 'decimal' when 'float' then 'float' when 'image' then 'byte[]' when 'int' then 'int' when 'money' then 'decimal' when 'nchar' then 'char' when 'ntext' then 'string' when 'numeric' then 'decimal' when 'nvarchar' then 'string' when 'real' then 'double' when 'smalldatetime' then 'DateTime' when 'smallint' then 'short' when 'smallmoney' then 'decimal' when 'text' then 'string' when 'time' then 'TimeSpan' when 'timestamp' then 'DateTime' when 'tinyint' then 'byte' when 'uniqueidentifier' then 'Guid' when 'varbinary' then 'byte[]' when 'varchar' then 'string' else 'UNKNOWN_' + typ.name end ColumnType, case when col.is_nullable = 1 and typ.name in ('bigint', 'bit', 'date', 'datetime', 'datetime2', 'datetimeoffset', 'decimal', 'float', 'int', 'money', 'numeric', 'real', 'smalldatetime', 'smallint', 'smallmoney', 'time', 'tinyint', 'uniqueidentifier') then '?' else '' end NullableSign from sys.columns col join sys.types typ on col.system_type_id = typ.system_type_id AND col.user_type_id = typ.user_type_id where object_id = object_id(@table_name) ) t order by ColumnId set @Result = @Result + ' }' print @Result FETCH NEXT FROM table_cursor INTO @table_name END CLOSE table_cursor; DEALLOCATE table_cursor;
נ.ב. יתכן ואני מגלה את אמריקה ויש דרך יותר קלה\סטנדרטית, אם כן - אשמח לשמוע.
-
הדרך הרשמית לבצע זאת בEntity Framework Core היא שימוש בפקודה
dotnet ef dbcontext scaffold
, אשר תיצור את הDbContext ואת הEntities בצורה אוטומטית, אחת מהמעלות הבולטות של השימוש בפקודה היא השינוי של שמות המחלקות והProperties כך שיתאימו למוסכמות של C# (ניתן לבטל את הפיצר באמצעות הפרמטרuse-database-names--
) כמו כן באמצעות הכלי ניתן להגדיר דברים רבים נוספים הקשורים ליצירה של המחלקות.דוגמא בסיסית (שימוש בlocaldb):
dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DatabaseName" Microsoft.EntityFrameworkCore.SqlServer
דוגמא נרחבת:
dotnet ef dbcontext scaffold "Server=ServerName; Database=DatabaseName; User Id=UserId; Password=Password" Microsoft.EntityFrameworkCore.SqlServer --context=DbContext --context-dir DbDir --data-annotations --output-dir DbDir/Entities
לשימוש בכלי מומלץ להתקינו גלובלית:
dotnet tool install --global dotnet-ef
-
-
תודה @רפאל על הדוגמא המפורטת, כהרגלך הטוב!
@dovid @חגי, אם כוונתכם למקור שציינתי, אני משתמש ב-Core, והתקנת התבנית DAO.Entity בכלל לא הופיעה לי בתור אופציה. (שמא זה בגלל התמיכה רק ב Framework הישן) זה מה שסיבך עבורי את העניין...
@dovid אמר בסקריפט ליצירה אוטומטית של מחלקות C# מבסיס נתונים ב-SQL Server:
זה נכון אם אתה משתמש בORM או שאתה חובב טיפוסיות קשוחה.
מודה באשמה...