using System.Runtime.Serialization; namespace AOP03.DataContracts { [DataContract] public class User { [DataMember] public int Id { set; get; } [DataMember] public string Name { set; get; } } }
تهیه DataContractAspect جهت اعمال خودکار ویژگیهای DataContract و DataMember
using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; using PostSharp.Aspects; using PostSharp.Extensibility; using PostSharp.Reflection; namespace AOP03 { [Serializable] //این ویژگی تنها نیاز است به کلاسها اعمال شود [MulticastAttributeUsage(MulticastTargets.Class)] public class DataContractAspect : TypeLevelAspect, IAspectProvider { public IEnumerable<AspectInstance> ProvideAspects(object targetElement) { var targetType = (Type)targetElement; //همان نوعی است که ویژگی جاری به آن اعمال خواهد شد //این سطر معادل است با درخواست تولید ویژگی دیتاکانترکت var introduceDataContractAspect = new CustomAttributeIntroductionAspect( new ObjectConstruction(typeof(DataContractAttribute).GetConstructor(Type.EmptyTypes))); //این سطر معادل است با درخواست تولید ویژگی دیتاممبر var introduceDataMemberAspect = new CustomAttributeIntroductionAspect( new ObjectConstruction(typeof(DataMemberAttribute).GetConstructor(Type.EmptyTypes))); //در اینجا کار اعمال ویژگی دیتاکانترکت به کلاسی که به عنوان پارامتر متد جاری //دریافت شده انجام خواهد شد yield return new AspectInstance(targetType, introduceDataContractAspect); //مرحله بعد کار اعمال ویژگی دیتاممبر به خواص کلاس است foreach (var property in targetType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance)) { if (property.CanWrite) yield return new AspectInstance(property, introduceDataMemberAspect); } } } }
برای اعمال آن به سراسر برنامه تنها کافی است به فایل AssemblyInfo.cs پروژه مراجعه و سپس سطر زیر را به آن اضافه کنیم:
[assembly: DataContractAspect(AttributeTargetTypes = "AOP03.DataContracts.*")]
در این حالت اگر کلیه ویژگیهای کلاس User فوق را حذف و برنامه را کامپایل کنیم، با مراجعه به برنامه ILSpy میتوان صحت اعمال ویژگیها را به کمک PostSharp بررسی کرد:
الف) تزریق وابستگیها در سازنده کلاس
ب) تزریق وابستگیها در خواص عمومی کلاسها یا Setters injection
حالت الف متداولترین است و بیشتر زمانی کاربرد دارد که کار وهله سازی یک کلاس را میتوان راسا انجام داد. اما در فرمها یا یوزرکنترلهای ASP.NET Web forms به صورت پیش فرض کار وهله سازی فرمها و یوزرکنترلها توسط موتور ASP.NET انجام میشود و در این حالت اگر بخواهیم از تزریق وابستگیها استفاده کنیم، مدام به همان روش معروف Service locator و استفاده از container.Resolve در تمام قسمتهای برنامه میرسیم که آنچنان روش مطلوبی نیست.
اما ... در ASP.NET Web forms میتوان وهله سازی فرمها را نیز تحت کنترل قرار داد، که برای آن دو روش زیر وجود دارند:
الف) یک کلاس مشتق شده را از کلاس پایه PageHandlerFactory تهیه کنیم. این کلاس را پیاده سازی کرده و نهایتا بجای وهله ساز پیش فرض فرمهای موتور داخلی ASP.NET، در فایل وب کانفیگ برنامه استفاده کنیم. یک نمونه از پیاده سازی آنرا در اینجا میتوانید مشاهده کنید.
مشکلی که این روش دارد سازگاری آن با حالت Full trust است. یعنی برنامه شما در یک هاست Medium trust (اغلب هاستهای خوب) اجرا نخواهد شد.
ب) روش دوم، استفاده از یک Http Module است برای اعمال Setter injectionها، به صورت خودکار. اکنون که حالت الف را همه جا نمیتوان بکار برد یا به عبارتی نمیتوان وهله سازی فرمها را راسا در دست گرفت، حداقل میتوان خواص عمومی اشیاء صفحه تولید شده را مقدار دهی کرد که در ادامه، این روش را بررسی میکنیم.
تهیه ماژول انجام Setters injection به صورت خودکار در برنامههای ASP.NET Web forms به کمک StructureMap
پیشنیاز این بحث، مطلب «استفاده از StructureMap به عنوان یک IoC Container» میباشد که پیشتر مطالعه کردید (در حد نحوه نصب StructureMap و آشنایی با تنظیمات اولیه آن)
using System.Collections; using System.Web; using System.Web.UI; using StructureMap; namespace DI05.Core { /// <summary> /// تسهیل در کار تزریق خودکار وابستگیها در سطح فرمها و یوزرکنترلها /// </summary> public class StructureMapModule : IHttpModule { public void Dispose() { } public void Init(HttpApplication app) { app.PreRequestHandlerExecute += (sender, e) => { var page = HttpContext.Current.Handler as Page; // The Page handler if (page == null) return; WireUpThePage(page); WireUpAllUserControls(page); }; } private static void WireUpAllUserControls(Page page) { // در اینجا هم کار سیم کشی یوزر کنترلها انجام میشود page.InitComplete += (initSender, evt) => { var thisPage = (Page)initSender; foreach (Control ctrl in getControlTree(thisPage)) { // فقط یوزر کنترلها بررسی شدند // اگر نیاز است سایر کنترلهای قرار گرفته روی فرم هم بررسی شوند شرط را حذف کنید if (ctrl is UserControl) { ObjectFactory.BuildUp(ctrl); } } }; } private static void WireUpThePage(Page page) { ObjectFactory.BuildUp(page); // برقراری خودکار سیم کشیها در سطح صفحات } private static IEnumerable getControlTree(Control root) { foreach (Control child in root.Controls) { yield return child; foreach (Control ctrl in getControlTree(child)) { yield return ctrl; } } } } }
پس از تهیه ماژول فوق، باید آنرا در فایل وب کانفیگ برنامه معرفی کرد:
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> <httpModules> <add name="StructureMapModule" type="DI05.Core.StructureMapModule"/> </httpModules> </system.web> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="StructureMapModule" type="DI05.Core.StructureMapModule"/> </modules> <validation validateIntegratedModeConfiguration="false" /> </system.webServer> </configuration>
مثالی از نحوه استفاده از StructureMapModule تهیه شده
فرض کنید لایه سرویس برنامه دارای اینترفیسها و کلاسهای زیر است:
namespace DI05.Services { public interface IUsersService { string GetUserEmail(int id); } } namespace DI05.Services { public class UsersService: IUsersService { public string GetUserEmail(int id) { //فقط جهت بررسی تزریق وابستگیها return "test@test.com"; } } }
using System; using StructureMap; using DI05.Services; namespace DI05 { public class Global : System.Web.HttpApplication { private static void initStructureMap() { ObjectFactory.Initialize(x => { x.For<IUsersService>().Use<UsersService>(); x.SetAllProperties(y => { y.OfType<IUsersService>(); }); }); } protected void Application_Start(object sender, EventArgs e) { initStructureMap(); } void Application_EndRequest(object sender, EventArgs e) { ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects(); } } }
مرحله آخر هم استفاده از سرویسهای برنامه به شکل زیر است:
using System; using DI05.Services; namespace DI05 { public partial class Default : System.Web.UI.Page { public IUsersService UsersService { set; get; } protected void Page_Load(object sender, EventArgs e) { lblEmail1.Text = string.Format("From Default Page: {0}", UsersService.GetUserEmail(1)); } } }
دریافت مثال کامل قسمت جاری
DI05.zip
یک نکتهی تکمیلی
برای ارتقاء نکات مطلب جاری به نگارش سوم StructureMap نیاز است موارد ذیل را لحاظ کنید:
الف) نصب بستهی وب آن
PM> Install-Package structuremap.web
ج) x.SetAllProperties را به x.Policies.SetAllProperties ویرایش کنید.
Full C# Project: Inventory Management System | ASP.NET Core Blazor, EF Core, SQL Server, Identity - YouTube
00:00:00 Project Demo (ASP.Net Core Blazor Server)
00:05:26 View Inventories
00:14:29 Add Entity Framework Core
00:27:16 View Inventory Use Case
00:36:35 View Inventory Component (Blazor Component)
00:58:04 View Inventory Page
01:08:18 Adding new Inventory
01:34:46 Edit Inventory
02:10:26 View Products Use Case
02:45:52 Search Inventory Component
03:05:50 Add Product
03:52:39 Refactor Product Inventories
04:16:51 Validate Product Price
04:49:14 Edit Product
05:23:34 Delete Product
05:47:48 Purchase Inventory
07:07:40 Produce Products
07:36:29 UI of Producing Products
08:16:05 Sell Product
08:46:36 Inventory Transaction Report
09:43:48 Product Transaction Report
10:10:10 Print Reports
10:19:56 Switch to SQL Server
10:51:30 Add Authentication and Authorization with ASP.NET Core Identity
10:59:57 Look and Feel with Bootstrap 5
استفاده از StructureMap به عنوان یک IoC Container
دریافت StructureMap
برای دریافت آن نیاز است دستور پاورشل ذیل را در کنسول نیوگت ویژوال استودیو فراخوانی کنید:
PM> Install-Package structuremap
آشنایی با ساختار برنامه
ابتدا یک برنامه کنسول را آغاز کرده و سپس یک Class library جدید را به نام Services نیز به آن اضافه کنید. در ادامه کلاسها و اینترفیسهای زیر را به Class library ایجاد شده، اضافه کنید. سپس از طریق نیوگت به روشی که گفته شد، StructureMap را به پروژه اصلی (ونه پروژه Class library) اضافه نمائید و Target framework آنرا نیز در حالت Full قرار دهید بجای حالت Client profile.
namespace DI03.Services { public interface IUsersService { string GetUserEmail(int userId); } } namespace DI03.Services { public interface IEmailsService { void SendEmailToUser(int userId, string subject, string body); } } using System; namespace DI03.Services { public class UsersService : IUsersService { public UsersService() { //هدف صرفا نمایش وهله سازی خودکار این وابستگی است Console.WriteLine("UsersService ctor."); } public string GetUserEmail(int userId) { //برای مثال دریافت از بانک اطلاعاتی و بازگشت یک نمونه جهت آزمایش برنامه return "name@site.com"; } } } using System; namespace DI03.Services { public class EmailsService: IEmailsService { private readonly IUsersService _usersService; public EmailsService(IUsersService usersService) { Console.WriteLine("EmailsService ctor."); _usersService = usersService; } public void SendEmailToUser(int userId, string subject, string body) { var email = _usersService.GetUserEmail(userId); Console.WriteLine("SendEmailTo({0})", email); } } }
سرویس کاربران بر اساس آی دی یک کاربر، برای مثال از بانک اطلاعاتی ایمیل او را بازگشت میدهد. سرویس ارسال ایمیل، نیاز به ایمیل کاربری برای ارسال ایمیلی به او دارد. بنابراین وابستگی مورد نیاز خود را از طریق تزریق وابستگیها در سازنده کلاس و وهله سازی شده در خارج از آن (معکوس سازی کنترل)، دریافت میکند.
در سازندههای هر دو کلاس سرویس نیز از Console.WriteLine استفاده شدهاست تا زمان وهله سازی خودکار آنها را بتوان بهتر مشاهده کرد.
نکته مهمی که در اینجا وجود دارد، بیخبری لایه سرویس از وجود IoC Container مورد استفاده است.
استفاده از لایه سرویس و تزریق وابستگیها به کمک StructureMap
using DI03.Services; using StructureMap; namespace DI03 { class Program { static void Main(string[] args) { // تنظیمات اولیه برنامه که فقط یکبار باید در طول عمر برنامه انجام شود ObjectFactory.Initialize(x => { x.For<IEmailsService>().Use<EmailsService>(); x.For<IUsersService>().Use<UsersService>(); }); //نمونهای از نحوه استفاده از تزریق وابستگیهای خودکار var emailsService = ObjectFactory.GetInstance<IEmailsService>(); emailsService.SendEmailToUser(userId: 1, subject: "Test", body: "Hello!"); } } }
به این ترتیب IoC Container ما زمانیکه قرار است object graph مربوط به IEmailsService درخواستی را تشکیل دهد، خواهد دانست ابتدا به سازندهی کلاس EmailsService میرسد. در اینجا برای وهله سازی این کلاس به صورت خودکار، باید وابستگیهای آنرا نیز وهله سازی کند. بنابراین بر اساس تنظیمات آغازین برنامه میداند که باید از کلاس UsersService برای تزریق خودکار وابستگیها در سازنده کلاس ارسال ایمیل استفاده نماید.
در این حالت اگر برنامه را اجرا کنیم، به خروجی زیر خواهیم رسید:
UsersService ctor. EmailsService ctor. SendEmailTo(name@site.com)
ابتداییترین مزیت استفاده از تزریق وابستگیها امکان تعویض آنها است؛ خصوصا در حین Unit testing. اگر کلاسی برای مثال قرار است با شبکه کار کند، میتوان پیاده سازی آنرا با یک نمونه اصطلاحا Fake جایگزین کرد و در این نمونه تنها نتیجهی کار را بازگشت داد. کلاسهای لایه سرویس ما تنها با اینترفیسها کار میکنند. این تنظیمات قابل تغییر اولیه IoC container مورد استفاده هستند که مشخص میکنند چه کلاسهایی باید در سازندههای کلاسها تزریق شوند.
تعیین طول عمر اشیاء در StructureMap
برای اینکه بتوان طول عمر اشیاء را بهتر توضیح داد، کلاس سرویس کاربران را به نحو زیر تغییر دهید:
using System; namespace DI03.Services { public class UsersService : IUsersService { private int _i; public UsersService() { //هدف صرفا نمایش وهله سازی خودکار این وابستگی است Console.WriteLine("UsersService ctor."); } public string GetUserEmail(int userId) { _i++; Console.WriteLine("i:{0}", _i); //برای مثال دریافت از بانک اطلاعاتی و بازگشت یک نمونه جهت آزمایش برنامه return "name@site.com"; } } }
//نمونهای از نحوه استفاده از تزریق وابستگیهای خودکار var emailsService1 = ObjectFactory.GetInstance<IEmailsService>(); emailsService1.SendEmailToUser(userId: 1, subject: "Test1", body: "Hello!"); var emailsService2 = ObjectFactory.GetInstance<IEmailsService>(); emailsService2.SendEmailToUser(userId: 1, subject: "Test2", body: "Hello!");
UsersService ctor. EmailsService ctor. i:1 SendEmailTo(name@site.com) UsersService ctor. EmailsService ctor. i:1 SendEmailTo(name@site.com)
اگر به هر دلیلی نیاز بود تا این رویه تغییر کند، میتوان بر روی طول عمر اشیاء تشکیل شده نیز تاثیر گذار بود. برای مثال تنظیمات آغازین برنامه را به نحو ذیل تغییر دهید:
// تنظیمات اولیه برنامه که فقط یکبار باید در طول عمر برنامه انجام شود ObjectFactory.Initialize(x => { x.For<IEmailsService>().Use<EmailsService>(); x.For<IUsersService>().Singleton().Use<UsersService>(); });
UsersService ctor. EmailsService ctor. i:1 SendEmailTo(name@site.com) EmailsService ctor. i:2 SendEmailTo(name@site.com)
حالتهای دیگر تعیین طول عمر مطابق متدهای زیر هستند:
Singleton() HttpContextScoped() HybridHttpOrThreadLocalScoped()
در حالت ThreadLocal، به ازای هر Thread، وهلهای متفاوت در اختیار مصرف کننده قرار میگیرد.
حالت Hybrid ترکیبی است از حالتهای HttpContext و ThreadLocal. اگر برنامه وب بود، از HttpContext استفاده خواهد کرد در غیراینصورت به ThreadLocal سوئیچ میکند.
شاید بپرسید که کاربرد مثلا HttpContextScoped در کجا است؟
در یک برنامه وب نیاز است تا یک وهله از DbContext (مثلا Entity framework) را در اختیار کلاسهای مختلف لایه سرویس قرار داد. به این ترتیب چون هربار new Context صورت نمیگیرد، هربار هم اتصال جداگانهای به بانک اطلاعاتی باز نخواهد شد. نتیجه آن رسیدن به یک برنامه سریع، با سربار کم و همچنین کار کردن در یک تراکنش واحد است. چون هربار فراخوانی new Context به معنای ایجاد یک تراکنش جدید است.
همچنین در این برنامه وب قصد نداریم از حالت طول عمر Singleton استفاده کنیم، چون در این حالت یک وهله از Context در اختیار تمام کاربران سایت قرار خواهد گرفت (و DbContext به صورت Thread safe طراحی نشده است). نیاز است به ازای هر کاربر و به ازای طول عمر هر درخواست، تنها یکبار این وهله سازی صورت گیرد. بنابراین در این حالت استفاده از HttpContextScoped توصیه میشود. به این ترتیب در طول عمر کوتاه Object graphهای تشکیل شده، فقط یک وهله از DbContext ایجاد و استفاده خواهد شد که بسیار مقرون به صرفه است.
مزیت دیگر مشخص سازی طول عمر به نحو HttpContextScoped، امکان Dispose خودکار آن به صورت زیر است:
protected void Application_EndRequest(object sender, EventArgs e) { ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects(); }
تنظیمات خودکار اولیه در StructureMap
اگر نام اینترفیسهای شما فقط یک I در ابتدا بیشتر از نام کلاسهای متناظر با آنها دارد، مثلا مانند ITest و کلاس Test هستند؛ فقط کافی است از قراردادهای پیش فرض StructureMap برای اسکن یک یا چند اسمبلی استفاده کنیم:
// تنظیمات اولیه برنامه که فقط یکبار باید در طول عمر برنامه انجام شود ObjectFactory.Initialize(x => { //x.For<IEmailsService>().Use<EmailsService>(); //x.For<IUsersService>().Singleton().Use<UsersService>(); x.Scan(scan => { scan.AssemblyContainingType<IEmailsService>(); scan.WithDefaultConventions(); }); });
دریافت مثال قسمت جاری
DI03.zip
به روز شدهی این مثالها را بر اساس آخرین تغییرات وابستگیهای آنها از مخزن کد ذیل میتوانید دریافت کنید:
Dependency-Injection-Samples
RML Utilities X64
RML Utilities X86
که نهایتا در این مسیر C:\Program Files\Microsoft Corporation\RMLUtils نصب خواهد شد.
سپس در خط فرمان این سه دستور را امتحان کنید:
-- Insert 10000 records using 20 threads, Repeat Execution 3 times -- disk-based "C:\Program Files\Microsoft Corporation\RMLUtils\ostress.exe" –n20 –r3 -S. -E -dTestdb2 -q -Q"set statistics time off; SET STATISTICS IO Off; set nocount on; DECLARE @start datetime = getdate(); declare @insertCount int = 10000; declare @startId int = 1; while @startId < @insertCount begin insert into tblNormal values ('Test', '2013-01-01T00:00:00') set @startId +=1 end; Print DATEDIFF(ms,@start,getdate());" –oc:\temp\output -- memory-optimized, tblMemoryOptimized_Schema_And_Data "C:\Program Files\Microsoft Corporation\RMLUtils\ostress.exe" –n20 –r3 -S. -E -dTestdb2 -q -Q"set statistics time off; SET STATISTICS IO Off; set nocount on; DECLARE @start datetime = getdate(); declare @insertCount int = 10000; declare @startId int = 1; while @startId < @insertCount begin insert into tblMemoryOptimized_Schema_And_Data values ('Test', '2013-01-01T00:00:00') set @startId +=1 end; Print DATEDIFF(ms,@start,getdate());" –oc:\temp\output -- memory-optimized, tblMemoryOptimized_Schema_Only "C:\Program Files\Microsoft Corporation\RMLUtils\ostress.exe" –n20 –r3 -S. -E -dTestdb2 -q -Q"set statistics time off; SET STATISTICS IO Off; set nocount on; DECLARE @start datetime = getdate(); declare @insertCount int = 10000; declare @startId int = 1; while @startId < @insertCount begin insert into tblMemoryOptimized_Schema_Only values ('Test', '2013-01-01T00:00:00') set @startId +=1 end; Print DATEDIFF(ms,@start,getdate());" –oc:\temp\output
البته برای اجرای این دستورات نیاز است فیلد CustomerID را identity تعریف کنید (در هر سه جدول مطلب جاری).
-- It is not Memory Optimized CREATE TABLE tblNormal ( [CustomerID] int identity NOT NULL PRIMARY KEY NONCLUSTERED, [Name] nvarchar(250) NOT NULL, CustomerSince DATETIME not NULL INDEX [ICustomerSince] NONCLUSTERED ) -- DURABILITY = SCHEMA_AND_DATA CREATE TABLE tblMemoryOptimized_Schema_And_Data ( [CustomerID] INT identity NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 131072), [Name] NVARCHAR(250) NOT NULL, [CustomerSince] DATETIME NOT NULL INDEX [ICustomerSince] NONCLUSTERED ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA) -- DURABILITY = SCHEMA_ONLY CREATE TABLE tblMemoryOptimized_Schema_Only ( [CustomerID] INT identity NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 131072), [Name] NVARCHAR(250) NOT NULL, [CustomerSince] DATETIME NOT NULL INDEX [ICustomerSince] NONCLUSTERED ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)
using System.Web; namespace Intellisense.Models { public class Persons { // کلید public int Id { get; set; } // نام public string FirstName { get; set; } // نام خانوادگی public string LastName { get; set; } // نام پدر public string FatherName { get; set; } // سن public int Age { get; set; } // شماره تلفن public int Mobile { get; set; } // آدرس public string Address { get; set; } } }
using System.Collections.Generic; using System.Web.Mvc; using Intellisense.Models; namespace Intellisense.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { // List of person var listOfPerson = new List<Persons> { new Persons() {Id = 1, FirstName = "Jone", LastName = "liy", FatherName = "Sobin", Age = 36, Mobile = +982015222, Address = "..."}, new Persons() {Id = 2, FirstName = "kety", LastName = "sory", FatherName = "petter", Age = 19, Mobile = +962222155, Address = "..."}, }; // Set ViewBag.Persons data from listOfPerson ViewBag.Persons = listOfPerson; // Show and send ViewBag.Persons to view return View(); } } }
- استفاده از دات ( . )
- عمل Cast
@{ ViewBag.Title = "ViewBag"; } <table> <thead> <tr> <th> Id </th> <th> First Name </th> <th> Last Name </th> <th> Father Name </th> <th> Age </th> <th> Mobile </th> <th> Address </th> </tr> </thead> <tbody> @{foreach (var item in ViewBag.Persons) { <tr> <td> @item.Id </td> <td> @item.FirstName </td> <td> @item.LastName </td> <td> @item.FatherName </td> <td> @item.Age </td> <td> @item.Mobile </td> <td> @item.Address </td> </tr> } } </tbody> </table>
@using Intellisense.Models @{ ViewBag.Title = "ViewBag"; // روش اول // پیشنهاد میشود که از روش اول استفاده شود // var listOfPerson = ViewBag.Persons as IEnumerable<Persons>; // روش دوم // var listOfPerson = (IEnumerable<Persons>)ViewBag.Persons; var listOfPerson = ViewBag.Persons as IEnumerable<Persons>; } <table> <thead> <tr> <th> Id </th> <th> First Name </th> <th> Last Name </th> <th> Father Name </th> <th> Age </th> <th> Mobile </th> <th> Address </th> </tr> </thead> <tbody> @{foreach (var item in listOfPerson) { <tr> <td> @item.Id </td> <td> @item.FirstName </td> <td> @item.LastName </td> <td> @item.FatherName </td> <td> @item.Age </td> <td> @item.Mobile </td> <td> @item.Address </td> </tr> } } </tbody> </table>
من بلدم با set identity_insert table_name on/off کاری کنم که خودم دستی مقداری را برای خصیصه identity لحاظ کنم. ولی متاسفانه نتونستم مقدار یک ستون با خصیصه Identity رو بروز رسانی (یا همون update) کنم. لطفا بهم بگید که اصلا این کار ممکنه یا من بلد نیستم. البته براساس query زیر بمن SQL Server گفته که نمیشه این ستون را update کرد که ظاهرا هم همین طور(ستون id همانطور که در پیام آمده از نوع identity هست)
update t set id = new_id from (select id, row_number() over(order by id) new_id from #temp)t --Cannot update identity column 'id'.
اصلا اجازه بدین یه جور دیگه سوال رو مطرح کنم من نیاز دارم تمام مقادیر identity رو بروز رسانی کنم تا کاملا پشت سر هم و متوالی بشن این کار را میتونم با یک تابع row_number و یک derived table انجام بدم (اگر بذارن!) همانطور که قبلا نشان دادم، یا با روش زیر این کار را بکنم که البته اجرا نمیشه به این دلیل که در یک جدول نمیشه دو identity property داشت. با فرض اجرا شدن دستور select into باز هم در دستور update با مشکل بر میخوردیم (چون نمیشه ستون id را بروز رسانی کرد)
select id, identity(int, 1,1) new_id into #temptable from #temp order by id asc /* cannot add identity column, using the SELECT INTO statement, to table '#temptable', which already has column 'id' that inherits the identity property. */ update t set id = new_id from #temp t join #temptable d on t.id = d.id;
declare @t table(id int) insert into @t select id from #temp delete from #temp set identity_insert #temp on insert #temp (id) select row_number() over(order by id) from @t set identity_insert #temp off
من قصد ندارم صورت مساله نقد و بررسی بشه و اصولی بودن یا صحیح بودنش مورد ارزیابی قرار بگیره فقط برام این یک سوال شده.
مساله عمومی که راجب این ستون وجود داره استفاده کردن از Gapهای حاصل شده در این ستون برای درجهای بعدی است. که query آن نیز بسیار ساده و در دسترس است.
آیا شما میدانید که چگونه این مشکل با sequence ای که در نسخه 2012 معرفی شده است حل میشود؟