class Employee(string firstName, string lastName) { private readonly string _firstName = firstName.Length >= 5 ? firstName : throw new ArgumentException("There must be atleast 5 characters"); private readonly string _lastName = lastName; public string FullName() { return $"Full name = {_firstName} {_lastName}"; } }
در ادامه قواعد زیر را در نظر بگیرید :
1 - برای استفاده از یک کتابخانه خارجی (dll) در داخل کد Text Template از بلوک <#@ assembly #> استفاده میشود .
مثلا برای استفاده از System.xml کد <#@ assembly name="System.xml" #> رو قرار بدید .
2- برای import کردن یک فضای نام نیز میتوانید از بلوک #> <#@ استفاده نمایید .
به عنوان مثال : <#@ import namespace="System.Data" #>
3- encoding خروجی قابل تظیم میباشد. مثال: <#@ " output extension = ".html" encoding = "utf-8 #>
4- برای استفاده از یک فایل tt درون فایل دیگر میتوان از include استفاده کرد. مثال : <#@ " include file=" testpath \basetest.tt #>
حالا به مثال زیر توجه کنید:
در بسیاری از پروژهها، معادل تمامی جداول موجود در یک دیتابیس Class هایی به نام DTO یا Data transfer object ساخته میشود که عموما" کلاسهای سبکی هستند که فقط شامل خصوصیتهای معادل فیلدهای جداول میباشند و از آنها جهت مدل کردن دادهها و ... استفاده میگردد. تولید این کلاسهای ساده میتواند بصورت اتوماتیک صورت گیرد و از این جهت در زمان تولید پروژه صرفه جویی شود. همچنین با تغییر ساختار دیتابیس میتوان همواره کلاسها را بروزرسانی کرد. نمونه ای از کد T4 برای تولید تمامی کلاسهای DTO به شکل زیر میباشد . فقط برای تست کد زیر دقت کنید که ConnectionString را در داخل کد متناسب با دیتابیس خود تغییر دهید:
<#@ template language="C#" debug="True" hostspecific="True" #> <#@ output extension=".cs" #> <#@ assembly name="System.Data" #> <#@ assembly name="System.xml" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Data.SqlClient" #> <#@ import namespace="System.Data" #> using System; namespace MyProject.Entities { <# string connectionString = "Password=22125110;Persist Security Info=True;User ID=sa;Initial Catalog=DnnDB;Data Source=ABBASPOOR299"; SqlConnection conn = new SqlConnection(connectionString); conn.Open(); System.Data.DataTable schema = conn.GetSchema("TABLES"); string selectQuery = "select * from @tableName"; SqlCommand command = new SqlCommand(selectQuery,conn); SqlDataAdapter ad = new SqlDataAdapter(command); System.Data.DataSet ds = new DataSet(); foreach(System.Data.DataRow row in schema.Rows) { #> public class <#= row["TABLE_NAME"].ToString().Trim('s') #> { <# command.CommandText = selectQuery.Replace("@tableName",row["TABLE_NAME"].ToString()); ad.FillSchema(ds, SchemaType.Mapped, row["TABLE_NAME"].ToString()); foreach (DataColumn dc in ds.Tables[row["TABLE_NAME"].ToString()].Columns) { #> private <#= dc.DataType.Name #> _<#= dc.ColumnName.Replace(dc.ColumnName[0].ToString(), dc.ColumnName[0].ToString().ToLower()) #>; public <#= dc.DataType.Name #> <#= dc.ColumnName #> { get { return _<#= dc.ColumnName.Replace(dc.ColumnName[0].ToString(), dc.ColumnName[0].ToString().ToLower()) #>; } set { _<#= dc.ColumnName.Replace(dc.ColumnName[0].ToString(), dc.ColumnName[0].ToString().ToLower()) #> = value; } } <# } #> } <# } #> }
Blazor 5x - قسمت 31 - احراز هویت و اعتبارسنجی کاربران Blazor WASM - بخش 1 - انجام تنظیمات اولیه
public static IEnumerable<Claim> ParseClaimsFromJwt(string jwt) { var claims = new List<Claim>(); var payload = jwt.Split('.')[1]; var jsonBytes = ParseBase64WithoutPadding(payload); var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes); claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString()))); ExtractRolesFromJwt(claims, keyValuePairs); return claims; }
private static void ExtractRolesFromJwt(List<Claim> claims, Dictionary<string, object> keyValuePairs) { keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles); if (roles != null) { var parsedRoles = roles.ToString().Trim().TrimStart('[').TrimEnd(']').Split(','); if (parsedRoles.Length > 1) { claims.AddRange(parsedRoles.Select(parsedRole => new Claim(ClaimTypes.Role, parsedRole.Trim('"')))); } else { claims.Add(new Claim(ClaimTypes.Role, parsedRoles[0])); } keyValuePairs.Remove(ClaimTypes.Role); } }
privatestaticbyte[] encrypt(byte[] key, byte[] data) { SecretKeySpec sKeySpec = new SecretKeySpec(key, "AES"); Cipher cipher; byte[] ciphertext = null; try { cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, sKeySpec); ciphertext = cipher.doFinal(data); } catch (NoSuchAlgorithmException e) { Log.e(TAG, "NoSuchAlgorithmException"); } catch (NoSuchPaddingException e) { Log.e(TAG, "NoSuchPaddingException"); } catch (IllegalBlockSizeException e) { Log.e(TAG, "IllegalBlockSizeException"); } catch (BadPaddingException e) { Log.e(TAG, "BadPaddingException"); } catch (InvalidKeyException e) { Log.e(TAG, "InvalidKeyException"); } return ciphertext; }
SecretKeySpec sKeySpec = new SecretKeySpec(key,"AES"); Cipher cipher; byte[] ciphertext = null;
cipher = Cipher.getI nstance ( " AES " ) ; cipher.init ( Cipher.ENCR ypt _ MODE , sKeySpec ) ;
ciphertext = cipher.doFinal(data);
publicstaticbyte[] generateKey(byte[] randomNumberSeed) { SecretKey sKey = null; try { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.setSeed(randomNumberSeed); keyGen.init(256, random); sKey = keyGen.generateKey(); } catch (NoSuchAlgorithmException e) { Log.e(TAG, "No such algorithm exception"); } return sKey.getEncoded(); }
صفحات مودال در بوت استرپ 3
OpenCVSharp #11
using (var src = new Mat(@"test.jpg", LoadMode.Color)) { image1.Source = KMeans(src, 2).ToWriteableBitmap(); image2.Source = Binary(src).ToWriteableBitmap(); }
private static Mat Binary(Mat src) { // Convert the image to Gray src = src.CvtColor(ColorConversion.RgbToGray); // Convert the Gray to Binary with auto threshold Cv2.Threshold(src, src, 0, 255, ThresholdType.Binary | ThresholdType.Otsu); // Convert the Gray to Binary with manual threshold //Cv2.Threshold(src, src, 127, 255, ThresholdType.Binary); return src; } private static Mat KMeans(Mat src, int k) { var columnVector = src.Reshape(cn: 3, rows: src.Rows * src.Cols); var samples = new Mat(); columnVector.ConvertTo(samples, MatType.CV_32FC3); var bestLabels = new Mat(); var centers = new Mat(); Cv2.Kmeans( data: samples, k: k, bestLabels: bestLabels, criteria: new TermCriteria(type: CriteriaType.Epsilon | CriteriaType.Iteration, maxCount: 10, epsilon: 1.0), attempts: 3, flags: KMeansFlag.PpCenters, centers: centers); var dst = new Mat(src.Rows, src.Cols, src.Type()); for (var size = 0; size < src.Cols * src.Rows; size++) { var clusterIndex = bestLabels.At<int>(0, size); var newPixel = new Vec3b { Item0 = (byte)(centers.At<float>(clusterIndex, 0)), // B Item1 = (byte)(centers.At<float>(clusterIndex, 1)), // G Item2 = (byte)(centers.At<float>(clusterIndex, 2)) // R }; dst.Set(size / src.Cols, size % src.Cols, newPixel); } return dst; }
نتایج :
<?xml version="1.0" encoding="utf-8"?> <license id="17d46246-a6cb-4196-98a0-ff6fc08cb67f" expiration="2012-06-12T00:00:00.0000000" type="Trial" prof="EFProf"> <name>MyName</name> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> <Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> <DigestValue>b8N0bDE4gTakfdGKtzDflmmyyXI=</DigestValue> </Reference> </SignedInfo> <SignatureValue>IPELgc9BbkD8smXSe0sGqp5vS57CtZo9ME2ZfXSq/thVu...=</SignatureValue> </Signature> </license>
در ادامه به نحوه تولید و استفاده از یک چنین مجوزهای امضاء شدهای در برنامههای دات نتی خواهیم پرداخت.
تولید کلیدهای RSA
برای تهیه امضای دیجیتال یک فایل XML نیاز است از الگوریتم RSA استفاده شود.
برای تولید فایل XML امضاء شده، از کلید خصوصی استفاده خواهد شد. برای خواندن اطلاعات مجوز (فایل XML امضاء شده)، از کلیدهای عمومی که در برنامه قرار میگیرند کمک خواهیم گرفت (برای نمونه برنامه EF Prof این کلیدها را در قسمت Resourceهای خود قرار داده است).
استفاده کننده تنها زمانی میتواند مجوز معتبری را تولید کند که دسترسی به کلیدهای خصوصی تولید شده را داشته باشد.
public static string CreateRSAKeyPair(int dwKeySize = 1024) { using (var provider = new RSACryptoServiceProvider(dwKeySize)) { return provider.ToXmlString(includePrivateParameters: true); } }
تهیه ساختار مجوز
در ادامه یک enum که بیانگر انواع مجوزهای برنامه ما است را مشاهده میکنید:
namespace SignedXmlSample { public enum LicenseType { None, Trial, Standard, Personal } }
using System; using System.Xml.Serialization; namespace SignedXmlSample { public class License { [XmlAttribute] public Guid Id { set; get; } public string Domain { set; get; } [XmlAttribute] public string IssuedTo { set; get; } [XmlAttribute] public DateTime Expiration { set; get; } [XmlAttribute] public LicenseType Type { set; get; } } }
تولید و خواندن مجوز دارای امضای دیجیتال
کدهای کامل کلاس تولید و خواندن یک مجوز دارای امضای دیجیتال را در اینجا مشاهده میکنید:
using System; using System.IO; using System.Security.Cryptography; // needs a ref. to `System.Security.dll` asm. using System.Security.Cryptography.Xml; using System.Text; using System.Xml; using System.Xml.Serialization; namespace SignedXmlSample { public static class LicenseGenerator { public static string CreateLicense(string licensePrivateKey, License licenseData) { using (var provider = new RSACryptoServiceProvider()) { provider.FromXmlString(licensePrivateKey); var xmlDocument = createXmlDocument(licenseData); var xmlDigitalSignature = getXmlDigitalSignature(xmlDocument, provider); appendDigitalSignature(xmlDocument, xmlDigitalSignature); return xmlDocumentToString(xmlDocument); } } public static string CreateRSAKeyPair(int dwKeySize = 1024) { using (var provider = new RSACryptoServiceProvider(dwKeySize)) { return provider.ToXmlString(includePrivateParameters: true); } } public static License ReadLicense(string licensePublicKey, string xmlFileContent) { var doc = new XmlDocument(); doc.LoadXml(xmlFileContent); using (var provider = new RSACryptoServiceProvider()) { provider.FromXmlString(licensePublicKey); var nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("sig", "http://www.w3.org/2000/09/xmldsig#"); var xml = new SignedXml(doc); var signatureNode = (XmlElement)doc.SelectSingleNode("//sig:Signature", nsmgr); if (signatureNode == null) throw new InvalidOperationException("This license file is not signed."); xml.LoadXml(signatureNode); if (!xml.CheckSignature(provider)) throw new InvalidOperationException("This license file is not valid."); var ourXml = xml.GetXml(); if (ourXml.OwnerDocument == null || ourXml.OwnerDocument.DocumentElement == null) throw new InvalidOperationException("This license file is coruppted."); using (var reader = new XmlNodeReader(ourXml.OwnerDocument.DocumentElement)) { var xmlSerializer = new XmlSerializer(typeof(License)); return (License)xmlSerializer.Deserialize(reader); } } } private static void appendDigitalSignature(XmlDocument xmlDocument, XmlNode xmlDigitalSignature) { xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(xmlDigitalSignature, true)); } private static XmlDocument createXmlDocument(License licenseData) { var serializer = new XmlSerializer(licenseData.GetType()); var sb = new StringBuilder(); using (var writer = new StringWriter(sb)) { var ns = new XmlSerializerNamespaces(); ns.Add("", ""); serializer.Serialize(writer, licenseData, ns); var doc = new XmlDocument(); doc.LoadXml(sb.ToString()); return doc; } } private static XmlElement getXmlDigitalSignature(XmlDocument xmlDocument, AsymmetricAlgorithm key) { var xml = new SignedXml(xmlDocument) { SigningKey = key }; var reference = new Reference { Uri = "" }; reference.AddTransform(new XmlDsigEnvelopedSignatureTransform()); xml.AddReference(reference); xml.ComputeSignature(); return xml.GetXml(); } private static string xmlDocumentToString(XmlDocument xmlDocument) { using (var ms = new MemoryStream()) { var settings = new XmlWriterSettings { Indent = true, Encoding = Encoding.UTF8 }; var xmlWriter = XmlWriter.Create(ms, settings); xmlDocument.Save(xmlWriter); ms.Position = 0; return new StreamReader(ms).ReadToEnd(); } } } }
در حین کار با متد CreateLicense، پارامتر licensePrivateKey همان اطلاعاتی است که به کمک متد CreateRSAKeyPair قابل تولید است. توسط پارامتر licenseData، اطلاعات مجوز در حال تولید اخذ میشود. در این متد به کمک provider.FromXmlString، اطلاعات کلیدهای RSA دریافت خواهند شد. سپس توسط متد createXmlDocument، محتوای licenseData دریافتی به یک فایل XML نگاشت میگردد (بنابراین اهمیتی ندارد که حتما از ساختار کلاس مجوز یاد شده استفاده کنید). در ادامه متد getXmlDigitalSignature با در اختیار داشتن معادل XML شیء مجوز و کلیدهای لازم، امضای دیجیتال متناظری را تولید میکند. با استفاده از متد appendDigitalSignature، این امضاء را به فایل XML اولیه اضافه میکنیم. از این امضاء جهت بررسی اعتبار مجوز برنامه در متد ReadLicense استفاده خواهد شد.
برای خواندن یک فایل مجوز امضاء شده در برنامه خود میتوان از متد ReadLicense استفاده کرد. توسط آرگومان licensePublicKey، اطلاعات کلید عمومی دریافت میشود. این کلید دربرنامه، ذخیره و توزیع میگردد. پارامتر xmlFileContent معادل محتوای فایل XML مجوزی است که قرار است مورد ارزیابی قرار گیرد.
مثالی در مورد نحوه استفاده از کلاس تولید مجوز
در ادامه نحوه استفاده از متدهای CreateLicense و ReadLicense را ملاحظه میکنید؛ به همراه آشنایی با نمونه کلیدهایی که باید به همراه برنامه منتشر شوند:
using System; using System.IO; namespace SignedXmlSample { class Program { static void Main(string[] args) { //Console.WriteLine(LicenseGenerator.CreateRSAKeyPair()); writeLicense(); readLicense(); Console.WriteLine("Press a key..."); Console.ReadKey(); } private static void readLicense() { var xml = File.ReadAllText("License.xml"); const string publicKey = @"<RSAKeyValue> <Modulus> mBNKFIc/LkMfaXvLlB/+6EujPkx3vBOvLu8jdESDSQLisT8K96RaDMD1ORmdw2XNdMw/6ZBuJjLhoY13qCU9t7biuL3SIxr858oJ1RLM4PKhA/wVDcYnJXmAUuOyxP/vfvb798o6zAC1R2QWuzG+yJQR7bFmbKH0tXF/NOcSgbc= </Modulus> <Exponent> AQAB </Exponent> </RSAKeyValue>"; var result = LicenseGenerator.ReadLicense(publicKey, xml); Console.WriteLine(result.Domain); Console.WriteLine(result.IssuedTo); } private static void writeLicense() { const string rsaData = @"<RSAKeyValue> <Modulus> mBNKFIc/LkMfaXvLlB/+6EujPkx3vBOvLu8jdESDSQLisT8K96RaDMD1ORmdw2XNdMw/6ZBuJjLhoY13qCU9t7biuL3SIxr858oJ1RLM4PKhA/wVDcYnJXmAUuOyxP/vfvb798o6zAC1R2QWuzG+yJQR7bFmbKH0tXF/NOcSgbc= </Modulus> <Exponent> AQAB </Exponent> <P> xwPKN77EcolMTD2O2Csv6k9Y4aen8UBVYjeQ4PtrNGz0Zx6I1MxLEFzRpiKC/Ney3xKg0Icwj0ebAQ04d5+HAQ== </P> <Q> w568t0Xe6OBUfCyAuo7tTv4eLgczHntVLpjjcxdUksdVw7NJtlnOLApJVJ+U6/85Z7Ji+eVhuN91yn04pQkAtw== </Q> <DP> svkEjRdA4WP5uoKNiHdmMshCvUQh8wKRBq/D2aAgq9fj/yxlj0FdrAxc+ZQFyk5MbPH6ry00jVWu3sY95s4PAQ== </DP> <DQ> WcRsIUYk5oSbAGiDohiYeZlPTBvtr101V669IUFhhAGJL8cEWnOXksodoIGimzGBrD5GARrr3yRcL1GLPuCEvQ== </DQ> <InverseQ> wIbuKBZSCioG6MHdT1jxlv6U1+Y3TX9sHED9PqGzWWpVGA+xFJmQUxoFf/SvHzwbBlXnG0DLqUvxEv+BkEid2w== </InverseQ> <D> Yk21yWdT1BfXqlw30NyN7qNWNuM/Uvh2eaRkCrhvFTckSucxs7st6qig2/RPIwwfr6yIc/bE/TRO3huQicTpC2W3aXsBI9822OOX4BdWCec2txXpSkbZW24moXu+OSHfAdYoOEN6ocR7tAGykIqENshRO7HvONJsOE5+1kF2GAE= </D> </RSAKeyValue>"; string data = LicenseGenerator.CreateLicense( rsaData, new License { Id = Guid.NewGuid(), Domain = "dotnettips.info", Expiration = DateTime.Now.AddYears(2), IssuedTo = "VahidN", Type = LicenseType.Standard }); File.WriteAllText("License.xml", data); } } }
همانطور که مشاهده میکنید، اطلاعات کامل یک نمونه از آن، در متد writeLicense مورد نیاز است. اما در متد readLicense تنها به قسمت عمومی آن یعنی Modulus و Exponent نیاز خواهد بود (موارد قابل انتشار به همراه برنامه).
سؤال: امنیت این روش تا چه اندازه است؟
پاسخ: تا زمانیکه کاربر نهایی به کلیدهای خصوصی شما دسترسی پیدا نکند، امکان تولید معادل آنها تقریبا در حد صفر است و به طول عمر او قد نخواهد داد!
اما ... مهاجم میتواند کلیدهای عمومی و خصوصی خودش را تولید کند. مجوز دلخواهی را بر این اساس تهیه کرده و سپس کلید عمومی جدید را در برنامه، بجای کلیدهای عمومی شما درج (patch) کند! بنابراین روش بررسی اینکه آیا برنامه جاری patch شده است یا خیر را فراموش نکنید. یا عموما مطابق معمول قسمتی از برنامه که در آن مقایسهای بین اطلاعات دریافتی و اطلاعات مورد انتظار وجود دارد، وصله میشوند؛ این مورد عمومی است و منحصر به روش جاری نمیباشد.
دریافت نسخه جنریک این مثال:
SignedXmlSample.zip
(JSON (JavaScript Object Notation یک راه مناسب برای نگهداری اطلاعات است و از لحاظ ساختاری شباهت زیادی به XML، رقیب قدیمی خود دارد.
وب سرویس و آجاکس برای انتقال اطلاعات از این روش استفاده میکنند و بعضی از پایگاههای داده مانند RavenDB بر مبنای این تکنولوژی پایه گذاری شده اند.
هیچ چیزی نمیتواند مثل یک مثال؛ خوانایی ، سادگی و کم حجم بودن این روش را نشان دهد :
اگر یک شئ با ساختار زیر در سی شارپ داشته باشید :
class Customer { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }
ساختار JSON متناظر با آن ( در صورت این که مقدار دهی شده باشد ) به صورت زیر است:
{ "Id":1, "FirstName":"John", "LastName":"Doe" }
و در یک مثال پیچیدهتر :
class Customer { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Car Car { get; set; } public IEnumerable<Location> Locations { get; set; } } class Location { public int Id { get; set; } public string Address { get; set; } public int Zip { get; set; } } class Car { public int Id { get; set; } public string Model { get; set; } }
{ "Id":1, "FirstName":"John", "LastName":"Doe", "Car": { "Id":1, "Model":"Nissan GT-R" }, "Locations":[ { "Id":1, "Address":"30 Mortensen Avenue, Salinas", "Zip":93905 }, { "Id":2, "Address":"65 West Alisal Street, #210, Salinas", "Zip":95812 } ] }
ساختار JSON را مجموعه ای از ( نام - مقدار ) تشکیل میدهد. ساختار مشابه آن در زبان سی شارپ KeyValuePair است.
مشاهده این تصاویر، بهترین درک را از ساختار JSON به شما میدهد.
Json.net یکی از بهترین کتابخانه هایی است که برای کار با این تکنولوژی در net. ارائه شده است. بهترین روش اضافه نمودن آن به پروژه NuGet است.برای این کار دستور زیر را در Package Manager Console وارد کنید.
PM> Install-Package Newtonsoft.Json
با استفاده از کد زیر میتوانید یک Object را به فرمت JSON تبدیل کنید.
var customer = new Customer { Id = 1, FirstName = "John", LastName = "Doe", Car = new Car { Id = 1, Model = "Nissan GT-R" }, Locations = new[] { new Location { Id = 1, Address = "30 Mortensen Avenue, Salinas", Zip = 93905 }, new Location { Id = 2, Address = "65 West Alisal Street, #210, Salinas", Zip = 95812 }, } };
var data = Newtonsoft.Json.JsonConvert.SerializeObject(customer);
خروجی تابع SerializeObject رشته ای است که محتوی آن را در چهارمین بلاک کد که در بالاتر آمده است، میتوانید مشاهده کنید.
برای Deserialize کردن (Cast اطلاعات با فرمت JSON به کلاس موردنظر) از روش زیر بهره میگیریم :
var customer = Newtonsoft.Json.JsonConvert.DeserializeObject<Customer>(data);
آشنایی با این تکنولوژی، پیش درآمدی برای چشیدن طعم NoSQL و معرفی کارآمدترین روشهای آن است که در آینده خواهیم آموخت...
خوشحال میشوم اگر نظرات شما را در باره این موضوع بدانم.
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
سؤال: در زمان اجرای ScalarExecuted دقیقا در کجا قرار داریم؟ چه متدی در برنامه، در کدام کلاس، سبب رسیدن به این نقطه شدهاست؟
تمام این اطلاعات را در زمان اجرا توسط کلاس StackTrace میتوان بدست آورد:
public static string GetCallingMethodInfo() { var stackTrace = new StackTrace(true); var frameCount = stackTrace.FrameCount; var info = new StringBuilder(); var prefix = "-- "; for (var i = frameCount - 1; i >= 0; i--) { var frame = stackTrace.GetFrame(i); var methodInfo = getStackFrameInfo(frame); if (string.IsNullOrWhiteSpace(methodInfo)) continue; info.AppendLine(prefix + methodInfo); prefix = "-" + prefix; } return info.ToString(); }
خاصیت stackTrace.FrameCount مشخص میکند که در زمان فراخوانی متد GetCallingMethodInfo که اکنون برای مثال درون متد ScalarExecuted قرار گرفتهاست، از چند سطح بالاتر این فراخوانی صورت گرفتهاست. سپس با استفاده از متد stackTrace.GetFrame میتوان به اطلاعات هر سطح دسترسی یافت.
در هر StackFrame دریافتی، با فراخوانی stackFrame.GetMethod میتوان نام متد فراخوان را بدست آورد. متد stackFrame.GetFileLineNumber دقیقا شماره سطری را که فراخوانی از آن صورت گرفته، بازگشت میدهد و stackFrame.GetFileName نیز نام فایل مرتبط را مشخص میکند.
یک نکته:
شرط عمل کردن متدهای stackFrame.GetFileName و stackFrame.GetFileLineNumber در زمان اجرا، وجود فایل PDB اسمبلی در حال بررسی است. بدون آن اطلاعات محل قرارگیری فایل سورس مرتبط و شماره سطر فراخوان، قابل دریافت نخواهند بود.
اکنون بر اساس این اطلاعات، متد getStackFrameInfo چنین پیاده سازی را خواهد داشت:
private static string getStackFrameInfo(StackFrame stackFrame) { if (stackFrame == null) return string.Empty; var method = stackFrame.GetMethod(); if (method == null) return string.Empty; if (isFromCurrentAsm(method) || isMicrosoftType(method)) { return string.Empty; } var methodSignature = method.ToString(); var lineNumber = stackFrame.GetFileLineNumber(); var filePath = stackFrame.GetFileName(); var fileLine = string.Empty; if (!string.IsNullOrEmpty(filePath)) { var fileName = Path.GetFileName(filePath); fileLine = string.Format("[File={0}, Line={1}]", fileName, lineNumber); } var methodSignatureFull = string.Format("{0} {1}", methodSignature, fileLine); return methodSignatureFull; }
Void Main(System.String[]) [File=Program.cs, Line=28]
در اینجا خروجی نهایی متد GetCallingMethodInfo به شکل زیر است که در آن چند سطح فراخوانی را میتوان مشاهده کرد:
-- Void Main(System.String[]) [File=Program.cs, Line=28] --- Void disposedContext() [File=Program.cs, Line=76] ---- Void Opened(System.Data.Common.DbConnection, System.Data.Entity.Infrastructure.Interception.DbConnectionInterceptionContext) [File=DatabaseInterceptor.cs,Line=157]
جهت تعدیل خروجی متد GetCallingMethodInfo، عموما نیاز است مثلا از کلاس یا اسمبلی جاری صرفنظر کرد یا اسمبلیهای مایکروسافت نیز در این بین شاید اهمیتی نداشته باشند و بیشتر هدف بررسی سورسهای موجود است تا فراخوانیهای داخلی یک اسمبلی ثالث:
private static bool isFromCurrentAsm(MethodBase method) { return method.ReflectedType == typeof(CallingMethod); } private static bool isMicrosoftType(MethodBase method) { if (method.ReflectedType == null) return false; return method.ReflectedType.FullName.StartsWith("System.") || method.ReflectedType.FullName.StartsWith("Microsoft."); }
کد کامل CallingMethod.cs را از اینجا میتوانید دریافت کنید:
CallingMethod.cs
var d = new Date(); $.jCookies({ name: 'dotnettips.info', value: { Title: 'ساخت کوکی با jcookie', Author: 'علی یگانه مقدم', Seen: d.getDate(), Favorite: true } });
$.jCookies ({ name : 'User', value : { username : 'Bob' , level : 5 }, minutes : 60 });
$.jCookies.defaults = { name : '', value : '', days : 27 }
var values = $.jCookies ({ get: 'dotnettips.info' });
var values = $.jCookies({ get: 'Rutabaga', error: true });
Error : { arguments : undefined, message : "Invalid base64 data", stack : "—", type : undefined }
var values = $.jCookies({ get: '*' }); alert(values["dotnettips.info"].Title); alert(values["data2"].Title);
var value = $.jCookies({ erase: 'dotnettips.info' });
- برگشت دادهها از حالت رمزگذاری base64
- دادهها در فرمت json هستند و باید به حالتی قابل استفاده در محیط شی گرا تبدیل شوند.
Title | ایجاد کوکی با jcookie |
Author | علی یگانه مقدم |
Seen | 2015/1/14 |
Favorite | true |
byte[] from64 = Convert.FromBase64String(Page.Request.Cookies["dotnettips.info"].Value); string json = Encoding.UTF8.GetString(from64); Dictionary<string, object> article =new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json); Page.Response.Write("Title: "+ (string)article["Title"]);
byte[] from64 = Convert.FromBase64String(Page.Request.Cookies["dotnettips.info"].Value); string json = Encoding.Unicode.GetString(from64); Dictionary<string, object> article =new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json); Page.Response.Write("Title: "+ (string)article["Title"]);