public virtual User User { get; set; } public virtual Role Role { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; } = new HashSet<UserRole>();
public virtual User User { get; set; } public virtual Role Role { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; } = new HashSet<UserRole>();
[HtmlTargetElement("img-gravatar")] public class GravatarTagHelper : TagHelper { [HtmlAttributeName("email")] public string Email { get; set; } [HtmlAttributeName("alt")] public string Alt { get; set; } [HtmlAttributeName("class")] public string Class { get; set; } [HtmlAttributeName("size")] public int Size { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { if (!string.IsNullOrWhiteSpace(Email)) { var hash = Md5HashHelper.GetHash(Email); output.TagName = "img"; if (!string.IsNullOrWhiteSpace(Class)) { output.Attributes.Add("class", Class); } if (!string.IsNullOrWhiteSpace(Alt)) { output.Attributes.Add("alt", Alt); } output.Attributes.Add("src", GetAvatarUrl(hash, Size)); output.TagMode = TagMode.SelfClosing; } } private static string GetAvatarUrl(string hash, int size) { var sizeArg = size > 0 ? $"?s={size}" : ""; return $"https://www.gravatar.com/avatar/{hash}{sizeArg}"; }
<img-gravatar email="@Model.Email" class="img-thumbnail" size="150" />
public class Blog { public int BlogId { get; set; } public string Url { get; set; } public List<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } }
public class BloggingContext : DbContext { public BloggingContext() { } public BloggingContext(DbContextOptions options) : base(options) { } public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Demo.Includes;Trusted_Connection=True;"); optionsBuilder.ConfigureWarnings(warnings => warnings.Log(CoreEventId.IncludeIgnoredWarning)); optionsBuilder.UseLoggerFactory(new LoggerFactory().AddConsole((message, logLevel) => { return true; /*return logLevel == LogLevel.Debug && message.StartsWith("Microsoft.EntityFrameworkCore.Database.Command");*/ })); } } }
var blogs = context.Blogs .Include(blog => blog.Posts) .Select(blog => new { Id = blog.BlogId, Url = blog.Url }) .ToList();
warn: Microsoft.EntityFrameworkCore.Query[100106] The Include operation for navigation '[blog].Posts' is unnecessary and was ignored because the navigation is not reachable in the final query results. See https://go.microsoft.com/fwlink/?linkid=850303 for more information.
SELECT [blog].[BlogId] AS [Id], [blog].[Url] FROM [Blogs] AS [blog]
optionsBuilder.ConfigureWarnings(warnings => warnings.Throw(CoreEventId.IncludeIgnoredWarning));
[HttpGet] public ActionResult GetSimpleJsonData() { return new ContentResult { Content = JsonConvert.SerializeObject(new { id = 1 }), ContentType = "application/json", ContentEncoding = Encoding.UTF8 }; }
using System; using System.Web.Mvc; using Newtonsoft.Json; namespace MvcJsonNetTests.Utils { public class JsonNetResult : JsonResult { public JsonNetResult() { Settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Error }; } public JsonSerializerSettings Settings { get; set; } public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("To allow GET requests, set JsonRequestBehavior to AllowGet."); } if (this.Data == null) return; var response = context.HttpContext.Response; response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType; if (this.ContentEncoding != null) response.ContentEncoding = this.ContentEncoding; var serializer = JsonSerializer.Create(this.Settings); using (var writer = new JsonTextWriter(response.Output)) { serializer.Serialize(writer, Data); writer.Flush(); } } } }
[HttpGet] public ActionResult GetJsonData() { return new JsonNetResult { Data = new { Id = 1, Name = "Test 1" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet, Settings = { ReferenceLoopHandling = ReferenceLoopHandling.Ignore } }; }
using System; using System.Dynamic; using System.Globalization; using System.IO; using System.Web.Mvc; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace MvcJsonNetTests.Utils { public class JsonNetValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { if (controllerContext == null) throw new ArgumentNullException("controllerContext"); if (controllerContext.HttpContext == null || controllerContext.HttpContext.Request == null || controllerContext.HttpContext.Request.ContentType == null) { return null; } if (!controllerContext.HttpContext.Request.ContentType.StartsWith( "application/json", StringComparison.OrdinalIgnoreCase)) { return null; } using (var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream)) { var bodyText = reader.ReadToEnd(); return string.IsNullOrEmpty(bodyText) ? null : new DictionaryValueProvider<object>( JsonConvert.DeserializeObject<ExpandoObject>(bodyText, new JsonSerializerSettings { Converters = { new ExpandoObjectConverter() } }), CultureInfo.CurrentCulture); } } } }
[HttpPost] public ActionResult TestValueProvider(string data1, dynamic data2)
using System.Linq; using System.Web.Mvc; using System.Web.Routing; using MvcJsonNetTests.Utils; namespace MvcJsonNetTests { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); ValueProviderFactories.Factories.Remove( ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault()); ValueProviderFactories.Factories.Add(new JsonNetValueProviderFactory()); } } }
در اکثر موارد در یک Landscape عملیاتی، چنانچه به تجمیع و انتقال دادهها از بانکهای اطلاعاتی مختلف نیاز باشد، از SSIS Package اختصار (SQL Server Integration Service) استفاده میشود و معمولاً با تعریف یک Job در سطح SQL Server به اجرای Package در زمانهای مشخص میپردازند. چنانچه در موقعیتی لازم باشد که از طریق برنامه کاربردی توسعه یافته، به اجرای Package مبادرت ورزیده شود و البته نخواهیم Job تعریف شده را از طریق کد برنامه، اجرا کنیم و در واقع این امکان را داشته باشیم که همانند یک رویه ذخیره شده تعریف شده در سطح بانک اطلاعاتی به اجرای عمل فوق بپردازیم، یک راه حل میتواند تعریف یک CLR Stored Procedures باشد. در این مقاله به بررسی این موضوع پرداخته میشود، در ابتدا لازم است به بیان تئوری موضوع پرداخته شود (قسمتهای 1 الی 5) در ادامه به ذکر پیاده سازی روش پیشنهادی پرداخته میشود.
• command-line ابزار خط فرمان dtexec.exeتوجه: همچنین یک Package را در زمان طراحی در Business Intelligence Development Studio) BIDS) میتوان اجرا نمود.
• ابزار اجرائی پکیج dtexecui.exe
• استفاده از SQL Server Agent job
مقدار | توصیف |
0 | Package با موفقیت اجرا شده است. |
1 | Package با خطا مواجه شده است. |
3 | Package در حال اجرا توسط کاربر لغو شده است. |
4 | Package پیدا نشده است. |
5 | Package بارگذاری نشده است. |
6 | ابزار با یک خطای نحوی یا خطای معنایی در خط فرمان برخورد کرده است. |
dtexec /option [value] [/option [value]]…
• تعدادی از گزینههای خط فرمان dtsrun به طور مستقیم در dtexec معادل دارند برای مثال نام Server و نام Package.
• تعدادی از گزینههای dtsrun به طور مستقیم در dtexec معادل ندارند.
• تعدادی گزینههای خط فرمان جدید dtsexec وجود دارد که در ویژگیهای جدید Integration Service پشتیبانی میشود.
dtexec /sq <Package Name> /ser <Server Name>
dtexec /dts “\File System\<Package File Name>”
dtexec /f “c:\<Package File Name>” /l “DTS.LogProviderTextFile; <Log File Name>”
dtexec /server “<Server Name>” /sql “<Package Name>” / user “ssis” /Password “ssis@ssis” /De “123”
Write action | Read action | Role |
Import packages Delete own packages Delete all packages Change own package roles Change all package roles * به نکته رجوع شود | Enumerate own packages Enumerate all packages View own packages View all packages Execute own packages Execute all packages Export own packages Export all packages Execute all packages in SQL Server Agent | db_ssisadmin or sysadmin |
Import packages Delete own packages Change own package roles | Enumerate own packages Enumerate all packages View own packages Execute own packages Export own packages | db_ssisltduser |
None | Enumerate all packages View all packages Execute all packages Export all packages Execute all packages in SQL Server Agent | db_ssisoperator |
Stop all currently running packages | View execution details of all running packages | Windows administrators |
- Component Service را باز نمایید ( در Run عبارت dcomcnfg را تایپ کنید).مجوز دسترسی Lunch به منظور شروع و خاتمه سرویس، اعطا یا رد میشود و مجوز دسترسیActivation به منظور متصل شدن به سرویس، اعطا (grant) یا رد (deny) میشود.
- گره Component Service را باز کنید، گره Computer و سپس My Computer را باز نمایید و روی DCOM Config کلیک نمایید.
- گره DCOM Config را باز کنید و از لیست برنامه هایی که میتوانند پیکربندی شوند MsDtsServer را انتخاب کنید.
- روی Properties برنامه MsDtsServer رفته و قسمت Security را انتخاب کنید.
- در قسمت Lunch and Activation Permissions، مورد Customize را انتخاب و سپس روی Edit کلیک نمایید تا پنجره Lunch Permission باز شود.
- در پنجره Lunch Permission، کاربران را اضافه و یا حذف کنید و مجوزهای مناسب را به کاربران یا گروههای مناسب نسبت دهید. مجوزهای موجود عبارتند از Local Lunch، Remote Lunch، Local Activation و Remote Activation .
- در قسمت Access Permission مراحل فوق را به منظور نسبت دادن مجوزهای مناسب به کاربران یا گروههای مناسب انجام دهید.
- سرویس Integration را Restart کنید.
Partial Public Class StoredProcedures '------------------------------------------------ 'exec dbo.Spc_NtDtexec 'Package','ssis','ssis@ssis','1234512345' '------------------------------------------------ <Microsoft.SqlServer.Server.SqlProcedure()> _ Public Shared Sub Spc_NtDtexec(ByVal PackageName As String, _ ByVal UserName As String, _ ByVal Password As String, _ ByVal Decrypt As String) Dim p As New System.Diagnostics.Process() p.StartInfo.FileName = "C:\Program Files\Microsoft SQL Server\100\DTS\Binn\DTExec.exe" p.StartInfo.RedirectStandardOutput = True p.StartInfo.Arguments = "/sql " & PackageName & " /User " & UserName & " /Password " & Password & " /De " & Decrypt p.StartInfo.UseShellExecute = False p.Start() p.WaitForExit() Dim output As String output = p.StandardOutput.ReadToEnd() Microsoft.SqlServer.Server.SqlContext.Pipe.Send(output) End Sub End Class
SP_CONFIGURE 'clr enabled',1 GO RECONFIGURE
ALTER DATABASE <Database Name> SET TRUSTWORTHY ON GO RECONFIGURE
USE <Database Name> GO CREATE ASSEMBLY [RunningPackage] AUTHORIZATION [dbo] FROM 'C:\RunningPackage.dll' WITH PERMISSION_SET = UNSAFE Go CREATE PROCEDURE [dbo].[Spc_NtDtexec] @PackageName [nvarchar](50), @UserName [nvarchar](50), @Password [nvarchar](50), @Decrypt [nvarchar](50) WITH EXECUTE AS CALLER AS EXTERNAL NAME [RunningPackage].[RunningPackage.StoredProcedures].[Spc_NtDtexec] GO
Private Sub ExecutePackage() Dim oSqlConnection As SqlClient.SqlConnection Dim oSqlCommand As SqlClient.SqlCommand Dim strCnt As String = String.Empty strCnt = "Data Source=" & txtServer.Text & ";User ID=" & txtUsername.Text & ";Password=" & txtPassword.Text & ";Initial Catalog=" & cmbDatabaseName.SelectedValue.ToString() & ";" Try oSqlConnection = New SqlClient.SqlConnection(strCnt) oSqlCommand = New SqlClient.SqlCommand With oSqlCommand .Connection = oSqlConnection .CommandType = System.Data.CommandType.StoredProcedure .CommandText = "dbo.Spc_NtDtexec" .Parameters.Clear() .Parameters.Add("@PackageName", System.Data.SqlDbType.VarChar, 50) .Parameters.Add("@UserName", System.Data.SqlDbType.VarChar, 50) .Parameters.Add("@Password", System.Data.SqlDbType.VarChar, 50) .Parameters.Add("@Decrypt", System.Data.SqlDbType.VarChar, 50) .Parameters("@PackageName").Value = txtPackageName.Text.Trim() .Parameters("@UserName").Value = txtUsername.Text.Trim() .Parameters("@Password").Value = txtPassword.Text.Trim() .Parameters("@Decrypt").Value = txtDecrypt.Text.Trim() End With If (oSqlCommand.Connection.State <> System.Data.ConnectionState.Open) Then oSqlCommand.Connection.Open() oSqlCommand.ExecuteNonQuery() System.Windows.Forms.MessageBox.Show("Success") End If If (oSqlCommand.Connection.State = System.Data.ConnectionState.Open) Then oSqlCommand.Connection.Close() End If Catch ex As Exception MessageBox.Show(ex.Message, "Error") End Try End Sub 'ExecutePackage
public interface IBookView : IView { void Show(); void Close(); }
Install-Package WAF
<Window x:Class="Shell.BookShell" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Book View" Height="350" Width="525"> <Grid> <DataGrid ItemsSource="{Binding Books}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="400" Height="200"> <DataGrid.Columns> <DataGridTextColumn Header="Code" Binding="{Binding Code}" Width="100"></DataGridTextColumn> <DataGridTextColumn Header="Title" Binding="{Binding Title}" Width="300"></DataGridTextColumn> </DataGrid.Columns> </DataGrid> </Grid> </Window>
[Export(typeof(IBookView))] [PartCreationPolicy(CreationPolicy.NonShared)] public partial class BookShell : Window, IBookView { public BookShell() { InitializeComponent(); } }
public class Book { public int Code { get; set; } public string Title { get; set; } }
[Export] [Export(typeof(ViewModel<IBookView>))] public class BookViewModel : ViewModel<IBookView> { [ImportingConstructor] public BookViewModel(IBookView view) : base(view) { } public ObservableCollection<Book> Books { get; set; } }
[Export] public class BookController { [ImportingConstructor] public BookController(BookViewModel viewModel) { ViewModelCore = viewModel; } public BookViewModel ViewModelCore { get; private set; } public void Run() { var result = new List<Book>(); result.Add(new Book { Code = 1, Title = "Book1" }); result.Add(new Book { Code = 2, Title = "Book2" }); result.Add(new Book { Code = 3, Title = "Book3" }); ViewModelCore.Books = new ObservableCollection<Models.Book>(result); (ViewModelCore.View as IBookView).Show(); } }
public class AppBootstrapper { public CompositionContainer Container { get; private set; } public AggregateCatalog Catalog { get; private set; } public void Run() { Catalog = new AggregateCatalog(); Catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); Catalog.Catalogs.Add(new AssemblyCatalog(String.Format("{0}\\{1}", Environment.CurrentDirectory, "Shell.dll"))); Catalog.Catalogs.Add(new AssemblyCatalog(String.Format("{0}\\{1}", Environment.CurrentDirectory, "ViewModels.dll"))); Catalog.Catalogs.Add(new AssemblyCatalog(String.Format("{0}\\{1}", Environment.CurrentDirectory, "Controllers.dll"))); Container = new CompositionContainer(Catalog); var batch = new CompositionBatch(); batch.AddExportedValue(Container); Container.Compose(batch); var bookController = Container.GetExportedValue<BookController>(); bookController.Run(); } }
نکته بخش Startup را از فایل App.Xaml خذف نمایید و در متد Startup این فایل کد زیر را کپی کنید:
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { new Bootstrapper.AppBootstrapper().Run(); } }
نکته: میتوان بخش اسکن اسمبلیها را توسط یک DirecotryCatalog به صورت زیر خلاصه کرد:
Catalog.Catalogs.Add(new DirectoryCatalog(Environment.CurrentDirectory));
using System.Security.Claims; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Server; namespace BlazorServerTestDynamicAccess.Services; public class CustomAuthenticationStateProvider : RevalidatingServerAuthenticationStateProvider { private readonly IServiceScopeFactory _scopeFactory; public CustomAuthenticationStateProvider(ILoggerFactory loggerFactory, IServiceScopeFactory scopeFactory) : base(loggerFactory) => _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); protected override TimeSpan RevalidationInterval { get; } = TimeSpan.FromMinutes(30); protected override async Task<bool> ValidateAuthenticationStateAsync( AuthenticationState authenticationState, CancellationToken cancellationToken) { // Get the user from a new scope to ensure it fetches fresh data var scope = _scopeFactory.CreateScope(); try { var userManager = scope.ServiceProvider.GetRequiredService<IUsersService>(); return await ValidateUserAsync(userManager, authenticationState?.User); } finally { if (scope is IAsyncDisposable asyncDisposable) { await asyncDisposable.DisposeAsync(); } else { scope.Dispose(); } } } private async Task<bool> ValidateUserAsync(IUsersService userManager, ClaimsPrincipal? principal) { if (principal is null) { return false; } var userIdString = principal.FindFirst(ClaimTypes.UserData)?.Value; if (!int.TryParse(userIdString, out var userId)) { return false; } var user = await userManager.FindUserAsync(userId); return user is not null; } }
public class Customer { public int Id { get; set; } public string Surname { get; set; } public string Forename { get; set; } public decimal Discount { get; set; } public string Address { get; set; } }
using FluentValidation; public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator { RuleFor(customer => customer.Surname).NotNull(); } }
اعتبارسنجی زنجیره ای برای یک خاصیت
RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");
Customer customer = new Customer(); CustomerValidator validator = new CustomerValidator(); ValidationResult results = validator.Validate(customer);
خروجی متد Validate، یک ValidationResult است که شامل دو خاصیت زیر میباشد:
Customer customer = new Customer(); CustomerValidator validator = new CustomerValidator(); ValidationResult results = validator.Validate(customer); if(! results.IsValid) { foreach(var failure in results.Errors) { Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage); } }
پرتاب استثناها (Throwing Exceptions)
Customer customer = new Customer(); CustomerValidator validator = new CustomerValidator(); validator.ValidateAndThrow(customer);
public class Customer { public string Name { get; set; } public Address Address { get; set; } } public class Address { public string Line1 { get; set; } public string Line2 { get; set; } public string Town { get; set; } public string County { get; set; } public string Postcode { get; set; } } public class AddressValidator : AbstractValidator<Address> { public AddressValidator() { RuleFor(address => address.Postcode).NotNull(); //etc } } public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { RuleFor(customer => customer.Name).NotNull(); RuleFor(customer => customer.Address).SetValidator(new AddressValidator()) } }
استفاده از Validatorها برای مجموعهها (Collections)
public class Customer { public IList<Order> Orders { get; set; } } public class Order { public string ProductName { get; set; } public decimal? Cost { get; set; } } var customer = new Customer(); customer.Orders = new List<Order> { new Order { ProductName = "Foo" }, new Order { Cost = 5 } };
public class OrderValidator : AbstractValidator<Order> { public OrderValidator() { RuleFor(x => x.ProductName).NotNull(); RuleFor(x => x.Cost).GreaterThan(0); } }
این Validator میتواند داخل CustomerValidator مورد استفاده قرار بگیرد (با استفاده از متد SetCollectionValidator):
public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator()); } }
می توان با استفاده از متد Where یا Unless روی اعتبارسنجی شرط گذاشت:
RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator()).Where(x => x.Cost != null);
گروه بندی قوانین اعتبارسنجی
public class PersonValidator : AbstractValidator<Person> { public PersonValidator() { RuleSet("Names", () => { RuleFor(x => x.Surname).NotNull(); RuleFor(x => x.Forename).NotNull(); }); RuleFor(x => x.Id).NotEqual(0); } }
var validator = new PersonValidator(); var person = new Person(); var result = validator.Validate(person, ruleSet: "Names");
using System; using System.Web; namespace OPMLCleaner { public static class UrlNormalization { public static bool AreTheSameUrls(this string url1, string url2) { url1 = url1.NormalizeUrl(); url2 = url2.NormalizeUrl(); return url1.Equals(url2); } public static bool AreTheSameUrls(this Uri uri1, Uri uri2) { var url1 = uri1.NormalizeUrl(); var url2 = uri2.NormalizeUrl(); return url1.Equals(url2); } public static string[] DefaultDirectoryIndexes = new[] { "default.asp", "default.aspx", "index.htm", "index.html", "index.php" }; public static string NormalizeUrl(this Uri uri) { var url = urlToLower(uri); url = limitProtocols(url); url = removeDefaultDirectoryIndexes(url); url = removeTheFragment(url); url = removeDuplicateSlashes(url); url = addWww(url); url = removeFeedburnerPart(url); return removeTrailingSlashAndEmptyQuery(url); } public static string NormalizeUrl(this string url) { return NormalizeUrl(new Uri(url)); } private static string removeFeedburnerPart(string url) { var idx = url.IndexOf("utm_source=", StringComparison.Ordinal); return idx == -1 ? url : url.Substring(0, idx - 1); } private static string addWww(string url) { if (new Uri(url).Host.Split('.').Length == 2 && !url.Contains("://www.")) { return url.Replace("://", "://www."); } return url; } private static string removeDuplicateSlashes(string url) { var path = new Uri(url).AbsolutePath; return path.Contains("//") ? url.Replace(path, path.Replace("//", "/")) : url; } private static string limitProtocols(string url) { return new Uri(url).Scheme == "https" ? url.Replace("https://", "http://") : url; } private static string removeTheFragment(string url) { var fragment = new Uri(url).Fragment; return string.IsNullOrWhiteSpace(fragment) ? url : url.Replace(fragment, string.Empty); } private static string urlToLower(Uri uri) { return HttpUtility.UrlDecode(uri.AbsoluteUri.ToLowerInvariant()); } private static string removeTrailingSlashAndEmptyQuery(string url) { return url .TrimEnd(new[] { '?' }) .TrimEnd(new[] { '/' }); } private static string removeDefaultDirectoryIndexes(string url) { foreach (var index in DefaultDirectoryIndexes) { if (url.EndsWith(index)) { url = url.TrimEnd(index.ToCharArray()); break; } } return url; } } }
<?xml version="1.0" encoding="utf-8"?> <opml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1.0"> <body> <outline text="آی تی ایرانی"> <outline type="rss" text="فید کلی آخرین نظرات، مطالب، اشتراکها و پروژههای .NET Tips" title="فید کلی آخرین نظرات، مطالب، اشتراکها و پروژههای .NET Tips" xmlUrl="https://www.dntips.ir/Feed/LatestChanges" htmlUrl="https://www.dntips.ir/" /> </outline> </body> </opml>
using System.Xml.Serialization; namespace OPMLCleaner { [XmlType(TypeName="outline")] public class Opml { [XmlAttribute(AttributeName="text")] public string Text { get; set; } [XmlAttribute(AttributeName = "title")] public string Title { get; set; } [XmlAttribute(AttributeName = "type")] public string Type { get; set; } [XmlAttribute(AttributeName = "xmlUrl")] public string XmlUrl { get; set; } [XmlAttribute(AttributeName = "htmlUrl")] public string HtmlUrl { get; set; } } }
var document = XDocument.Load("it-92-03-01.opml"); var results = (from node in document.Descendants("outline") where node.Attribute("htmlUrl") != null && node.Parent.Attribute("text") != null && node.Parent.Attribute("text").Value == "آی تی ایرانی" select new Opml { HtmlUrl = (string)node.Attribute("htmlUrl"), Text = (string)node.Attribute("text"), Title = (string)node.Attribute("title"), Type = (string)node.Attribute("type"), XmlUrl = (string)node.Attribute("xmlUrl") }).ToList();
using System.Collections.Generic; namespace OPMLCleaner { public class OpmlCompare : EqualityComparer<Opml> { public override bool Equals(Opml x, Opml y) { return UrlNormalization.AreTheSameUrls(x.HtmlUrl, y.HtmlUrl); } public override int GetHashCode(Opml obj) { return obj.HtmlUrl.GetHashCode(); } } }
var distinctResults = results.Distinct(new OpmlCompare()).ToList();
اگر با MVC کار کرده باشید حتما با ModelBinding آن آشنا هستید؛ DefaultModelBinder توکار آن که در اکثر مواقع، باری زیادی را از روی دوش برنامه نویسان بر میدارد و کار را برای آنان راحتتر میکند.
اما در بعضی مواقع این مدل بایندر پیش فرض ممکن است پاسخگوی نیاز ما در
بایند کردن یک خصوصیت از یک مدل خاص نباشد، برای همین ما نیاز داریم که کمی آن را سفارشی سازی کنیم.
برای این کار ما دو راه داریم:
1) یک مدل بایندر جدید را با پیاده سازی IModelBinder تهیه کنیم. (در این حالت ما مجبوریم که مدل بایندر را از ابتدا جهت بایند
کردن کلیه مقادیر شی مدل خود، بازنویسی کنیم و در واقع امکان انتساب آنرا در سطح فقط یک خصوصیت نداریم.) (نحوه پیاده سازی قبلا در اینجا مطرح شده)
2) ModelBinder پیش فرض را جهت پاسخگویی به نیازمان توسعه دهیم. (که در این مطلب قصد آموزشش را داریم.)
فرض کنید که میخواهید بر اساس یک Enum در صفحه، یک DropDownFor معادل را قرار بدید که به طور خودکار رشته انتخاب شده را به یک خصوصیت مدل که از نوع بایت هست بایند بکند.
@Html.DropDownListFor(model => model.AccountType, new SelectList(Enum.GetNames(typeof(Enums.AccountType))))
public enum AccountType : byte { مدیر = 0, کاربر_حقیقی = 1, کاربر_حقوقی = 2, }
namespace MvcApplication1.Models { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public abstract class PropertyBindAttribute : Attribute { public abstract bool BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor); } public class ExtendedModelBinder : DefaultModelBinder { protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) { if (propertyDescriptor.Attributes.OfType<PropertyBindAttribute>().Any()) { var modelBindAttr = propertyDescriptor.Attributes.OfType<PropertyBindAttribute>().FirstOrDefault(); if (modelBindAttr.BindProperty(controllerContext, bindingContext, propertyDescriptor)) return; } base.BindProperty(controllerContext, bindingContext, propertyDescriptor); } } }
در کد بالا ما تمام کلاس هایی را که از PropertyBindAttribute مشتق شده باشند را به DefaultModelBinder اضافه میکنیم. این کد فقط یک بار نوشته میشود و از این به بعد هر بایندر سفارشی که بسازیم به بایندر پیشفرض اضافه خواهد شد.
public class AccountTypeBindAttribute : PropertyBindAttribute { public override bool BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) { if (propertyDescriptor.PropertyType == typeof(byte)) { HttpRequestBase request = controllerContext.HttpContext.Request; byte accountType = (byte)Enum.Parse(typeof(Enums.AccountType), request.Form["AccountType"]); propertyDescriptor.SetValue(bindingContext.Model, accountType); return true; } return false; } }
[AccountTypeBindAttribute] public byte AccountType { get; set; }
ModelBinders.Binders.DefaultBinder = new ExtendedModelBinder();
The field نوع کاربر : must be a number.
@Html.DropDownListFor(model => model.AccountType, new SelectList(Enum.GetNames(typeof(Enums.AccountType))),new Dictionary<string, object>() {{ "data-val", "false" }})