The .NET team has built a linker to reduce the size of .NET Core applications. It is built on top of the excellent and battle-tested mono linker. The Xamarin tools also use this linker so it makes sense to try it out and perhaps use it everywhere!
معرفی NET Standard.
With .NET Standard 2.0, we’re focusing on compatibility. In order to support .NET Standard 2.0 in .NET Core and UWP, we’ll be extending these platforms to include many more of the existing APIs. This also includes a compatibility shim that allows referencing binaries that were compiled against the .NET Framework.
تغییرات کتابخانه DNTFrameworkCore
public interface ICreationTracking { DateTime CreatedDateTime { get; set; } } public interface IModificationTracking { DateTime? ModifiedDateTime { get; set; } }
public interface IHasRowIntegrity { string Hash { get; set; } } public interface IHasRowVersion { byte[] Version { get; set; } }
public abstract class AggregateRoot<TKey> : Entity<TKey>, IAggregateRoot where TKey : IEquatable<TKey> { private readonly List<IDomainEvent> _events = new List<IDomainEvent>(); public IReadOnlyCollection<IDomainEvent> Events => _events.AsReadOnly(); protected virtual void AddDomainEvent(IDomainEvent newEvent) { _events.Add(newEvent); } public virtual void ClearEvents() { _events.Clear(); } }
public static class EventBusExtensions { public static Task TriggerAsync(this IEventBus bus, IEnumerable<IDomainEvent> events) { var tasks = events.Select(async domainEvent => await bus.TriggerAsync(domainEvent)); return Task.WhenAll(tasks); } public static async Task PublishAsync(this IEventBus bus, IAggregateRoot aggregateRoot) { await bus.TriggerAsync(aggregateRoot.Events); aggregateRoot.ClearEvents(); } }
public interface IKeyValueService : IApplicationService { Task SetValueAsync(string key, string value); Task<Maybe<string>> LoadValueAsync(string key); Task<bool> IsTamperedAsync(string key); } public class KeyValue : Entity, IModificationTracking, ICreationTracking, IHasRowIntegrity { public string Key { get; set; } [Encrypted] public string Value { get; set; } public string Hash { get; set; } public DateTime CreatedDateTime { get; set; } public DateTime? ModifiedDateTime { get; set; } }
public class ExceptionOptions { public List<ExceptionMapItem> Mappings { get; } = new List<ExceptionMapItem>(); [Required] public string DbException { get; set; } [Required] public string DbConcurrencyException { get; set; } [Required] public string InternalServerIssue { get; set; } public bool TryFindMapping(DbException dbException, out ExceptionMapItem mapping) { mapping = null; var words = new HashSet<string>(Regex.Split(dbException.ToStringFormat(), @"\W")); var mappingItem = Mappings.FirstOrDefault(a => a.Keywords.IsProperSubsetOf(words)); if (mappingItem == null) { return false; } mapping = mappingItem; return true; } }
"Exception": { "Mappings": [ { "Message": "به دلیل وجود اطلاعات وابسته امکان حذف وجود ندارد", "Keywords": [ "DELETE", "REFERENCE" ] }, { "Message": "یک تسک با این عنوان قبلا در سیستم ثبت شده است", "MemberName": "Title", "Keywords": [ "Task", "UIX_Task_NormalizedTitle" ] } ], "DbException": "امکان ذخیرهسازی اطلاعات وجود ندارد؛ دوباره تلاش نمائید", "DbConcurrencyException": "اطلاعات توسط کاربری دیگر در شبکه تغییر کرده است", "InternalServerIssue": "متأسفانه مشکلی در فرآیند انجام درخواست شما پیش آمده است!" }
_session.UserId _session.UserId<long>() _session.UserId<int>() _session.UserId<Guid>()
public static class UserClaimTypes { public const string UserName = ClaimTypes.Name; public const string UserId = ClaimTypes.NameIdentifier; public const string SerialNumber = ClaimTypes.SerialNumber; public const string Role = ClaimTypes.Role; public const string DisplayName = nameof(DisplayName); public const string BranchId = nameof(BranchId); public const string BranchName = nameof(BranchName); public const string IsHeadOffice = nameof(IsHeadOffice); public const string TenantId = nameof(TenantId); public const string TenantName = nameof(TenantName); public const string IsHeadTenant = nameof(IsHeadTenant); public const string Permission = nameof(Permission); public const string PackedPermission = nameof(PackedPermission); public const string ImpersonatorUserId = nameof(ImpersonatorUserId); public const string ImpersonatorTenantId = nameof(ImpersonatorTenantId); }
/// <summary> /// Adds a file logger named 'File' to the factory. /// </summary> /// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param> public static ILoggingBuilder AddFile(this ILoggingBuilder builder) { builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>(); return builder; } /// <summary> /// Adds a file logger named 'File' to the factory. /// </summary> /// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param> /// <param name="configure">Configure an instance of the <see cref="FileLoggerOptions" /> to set logging options</param> public static ILoggingBuilder AddFile(this ILoggingBuilder builder, Action<FileLoggerOptions> configure) { builder.AddFile(); builder.Services.Configure(configure); return builder; }
public interface ITenantResolutionStrategy { string TenantId(); } public interface ITenantStore { Task<Tenant> FindTenantAsync(string tenantId); }
public interface ITenantSession : IScopedDependency { /// <summary> /// Gets current TenantId or null. /// This TenantId should be the TenantId of the <see cref="IUserSession.UserId" />. /// It can be null if given <see cref="IUserSession.UserId" /> is a head-tenant user or no user logged in. /// </summary> string TenantId { get; } /// <summary> /// Gets current TenantName or null. /// This TenantName should be the TenantName of the <see cref="IUserSession.UserId" />. /// It can be null if given <see cref="IUserSession.UserId" /> is a head-tenant user or no user logged in. /// </summary> string TenantName { get; } /// <summary> /// Represents current tenant is head-tenant. /// </summary> bool IsHeadTenant { get; } /// <summary> /// TenantId of the impersonator. /// This is filled if a user with <see cref="IUserSession.ImpersonatorUserId" /> performing actions behalf of the /// <see cref="IUserSession.UserId" />. /// </summary> string ImpersonatorTenantId { get; } }
public static class SystemTime { public static Func<DateTime> Now = () => DateTime.UtcNow; public static Func<DateTime, DateTime> Normalize = (dateTime) => DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); }
public interface IClock : ITransientDependency { DateTime Now { get; } DateTime Normalize(DateTime dateTime); } internal sealed class Clock : IClock { public DateTime Now => SystemTime.Now(); public DateTime Normalize(DateTime dateTime) { return SystemTime.Normalize(dateTime); } }
public class Result { private static readonly Result _ok = new Result(false, string.Empty); private readonly List<ValidationFailure> _failures; protected Result(bool failed, string message) : this(failed, message, Enumerable.Empty<ValidationFailure>()) { Failed = failed; Message = message; } protected Result(bool failed, string message, IEnumerable<ValidationFailure> failures) { Failed = failed; Message = message; _failures = failures.ToList(); } public bool Failed { get; } public string Message { get; } public IEnumerable<ValidationFailure> Failures => _failures.AsReadOnly(); [DebuggerStepThrough] public static Result Ok() => _ok; [DebuggerStepThrough] public static Result Fail(string message) { return new Result(true, message); } //... }
services.AddFramework() .WithModelValidation() .WithFluentValidation() .WithMemoryCache() .WithSecurityService() .WithBackgroundTaskQueue() .WithRandomNumber();
تغییرات کتابخانه DNTFrameworkCore.EFCore
public abstract class TrackableEntity<TKey> : Entity<TKey>, ITrackable where TKey : IEquatable<TKey> { [NotMapped] public TrackingState TrackingState { get; set; } [NotMapped] public ICollection<string> ModifiedProperties { get; set; } }
public static class ConfigurationBuilderExtensions { public static IConfigurationBuilder AddEFCore(this IConfigurationBuilder builder, IServiceProvider provider) { return builder.Add(new EFConfigurationSource(provider)); } }
protected DbContextCore(DbContextOptions options, IEnumerable<IHook> hooks) : base(options) { _hooks = hooks ?? throw new ArgumentNullException(nameof(hooks)); }
public void IgnoreHook(string hookName) { _ignoredHookList.Add(hookName); }
internal sealed class RowIntegrityHook : PostActionHook<IHasRowIntegrity> { public override string Name => HookNames.RowIntegrity; public override int Order => int.MaxValue; public override EntityState HookState => EntityState.Unchanged; protected override void Hook(IHasRowIntegrity entity, HookEntityMetadata metadata, IUnitOfWork uow) { metadata.Entry.Property(EFCore.Hash).CurrentValue = uow.EntityHash(entity); } }
//DbContextCore : IUnitOfWork public string EntityHash<TEntity>(TEntity entity) where TEntity : class { var row = Entry(entity).ToDictionary(p => p.Metadata.Name != EFCore.Hash && !p.Metadata.ValueGenerated.HasFlag(ValueGenerated.OnUpdate) && !p.Metadata.IsShadowProperty()); return EntityHash<TEntity>(row); } protected virtual string EntityHash<TEntity>(Dictionary<string, object> row) where TEntity : class { var json = JsonConvert.SerializeObject(row, Formatting.Indented); using (var hashAlgorithm = SHA256.Create()) { var byteValue = Encoding.UTF8.GetBytes(json); var byteHash = hashAlgorithm.ComputeHash(byteValue); return Convert.ToBase64String(byteHash); } }
IsTamperedAsync HasTamperedEntryAsync TamperedEntryListAsync
public const string CreatedDateTime = nameof(ICreationTracking.CreatedDateTime); public const string CreatedByUserId = nameof(CreatedByUserId); public const string CreatedByBrowserName = nameof(CreatedByBrowserName); public const string CreatedByIP = nameof(CreatedByIP); public const string ModifiedDateTime = nameof(IModificationTracking.ModifiedDateTime); public const string ModifiedByUserId = nameof(ModifiedByUserId); public const string ModifiedByBrowserName = nameof(ModifiedByBrowserName); public const string ModifiedByIP = nameof(ModifiedByIP);
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.NormalizeDateTime(); modelBuilder.NormalizeDecimalPrecision(precision: 20, scale: 6); base.OnModelCreating(modelBuilder); }
MigrateDbContext<TContext>(this IHost host)
services.AddEFCore<ProjectDbContext>() .WithTrackingHook<long>() .WithDeletedEntityHook() .WithRowIntegrityHook() .WithNumberingHook(options => { options.NumberedEntityMap[typeof(Task)] = new NumberedEntityOption { Prefix = "Task", FieldNames = new[] {nameof(Task.BranchId)} }; });
تغییرات کتابخانه DNTFrameworkCore.Web.Tenancy
services.AddTenancy() .WithTenantSession() .WithStore<InMemoryTenantStore>() .WithResolutionStrategy<HostResolutionStrategy>();
app.UseTenancy();
سایر کتابخانهها تغییرات خاصی نداشتند و صرفا نحوه معرفی سرویسهای آنها ممکن است تغییر کند و یا وابستگیهای آنها به آخرین نسخه موجود ارتقاء داده شده باشند که در پروژه DNTFrameworkCore.TestAPI اعمال شدهاند.
PM> Install-Package DNTFrameworkCore PM> Install-Package DNTFrameworkCore.EFCore PM> Install-Package DNTFrameworkCore.EFCore.SqlServer PM> Install-Package DNTFrameworkCore.Web PM> Install-Package DNTFrameworkCore.FluentValidation PM> Install-Package DNTFrameworkCore.Web.Tenancy PM> Install-Package DNTFrameworkCore.Licensing
تنظیمات امنیتی دسترسی به سرور RavenDB
سلام
اعتبارسنجی OAuth را مطابق آنچه گفته شد انجام دادم منتها در هنگام ثبت پیام خطا میدهد!
- از اجرای workflow اضافی به هنگام تغییر فایل readme.md جلوگیری میکند (می توانید فایل یا پوشههای دیگری را هم اضافه کنید)
- مراحل Build و Test پروژه را در حالت Release انجام میدهد
- فایل .nupkg مورد نیاز برای پکیج Nuget را در حالت Release ایجاد میکند (عبارت src نام پوشه اصلی پروژه است; در صورت نیاز تغییر دهید)
- به هنگام Push شدن ریپازیتوری به همراه تگ "release " به صورت خودکار پکیج را به سایت nuget آپلود میکند (عبارت secrets.NUGET_TOKEN شامل مقدار API_KEY شما در سایت Nuget است که باید در قسمت Setting ریپازیتوری، قسمت Secrects ذخیره شده باشد)
name: .NET Core
on:
push:
paths-ignore:
- 'readme.md'
pull_request:
paths-ignore:
- 'readme.md'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.101
- name: Build (Release)
run: dotnet build --configuration Release
- name: Test (Release)
run: dotnet test --configuration Release
- name: Pack (Release)
run: dotnet pack src --configuration Release
- name: Publish (Nuget)
if: github.event_name == 'push'
run: |
if ( "${{github.ref}}" -match "^refs/tags/[0-9]+\.[0-9]+\.[0-9]+$" ) {
dotnet nuget push src\bin\Release\*.nupkg -s nuget.org -k ${{secrets.NUGET_TOKEN}}
} else {
echo "Publish is only enabled by tagging with a release tag."
}
کتابخانه Hyprlinkr
Example
Imagine that you're using the standard route configuration created by the Visual Studio project template:
name: "API Default", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }
In that case, you can create an URI to a the resource handled by the FooController.GetById Action Method like this:
var uri = linker.GetUri<FooController>(r => r.GetById(1337));
This will create a URI like this:
http://localhost/api/foo/1337
ExtCore allows you to decouple your application into the modules (or extensions) and reuse that modules in other applications in various combinations. Each ExtCore extension may consist of one or more projects and each project may include everything you want (as any other ASP.NET Core project). Controllers, view components, views (added as resources and/or precompiled), static content (added as resources) will be resolved automatically. These projects (extension pieces) may be added to the application directly as dependencies in project.json of your main application project (as source code or NuGet packages), or by copying compiled DLL-files to the Extensions folder. ExtCore supports both of these approaches out of the box and at the same time.
Learn how you can save time by creating your own reusable .NET Core templates in just a few steps.
Do you ever develop prototypes, or starter projects/accelerators, that you’d like to use again in the future? A good way to do that is by creating custom templates for dotnet. Once completed, anytime you want to create a new project of that type in the future, you can key in “dotnet new ” and you’re off, complete with correct namespaces. You can even do conditional checks, or variable replacements.
Summary of What's New in this Release of Visual Studio 2022 version 17.8.2
- Fixed an issue where, in certain situations, a document window can get stuck showing a loading message.
- In some cases (when a project is located under a solution folder) you may see an error when saving the project. The project would get saved but you would see an error about unable to cast a COM object. This issue is now fixed so the error is no longer displayed.
- RemoteSemanticClassificationService.GetClassificationsAsync: SyntaxTree is not part of the compilation
- When change to another file, VS get stuck on "loading editor components"
- After upgrade to Visual Studio 17.8.0, Interactive REST Tests no longer work
- ASP.NET Core web apps targeting .NET 5.0 and below stopped working after 17.8.0 update
- could not create a .net framework console app