نظرات مطالب
EF Code First #12
با سلام.
من کل پوشه Controllers را درون یک اسمبلی جداگانه قرار دادم. ولی هنگام اجرای برنامه خطای زیر رخ میدهد.
public class StructureMapControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
            {
                throw new InvalidOperationException(string.Format("Page not found: {0}", requestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture)));
            }
            return ObjectFactory.GetInstance(controllerType) as Controller;
        }
    }

StructureMap Exception Code:  202
No Default Instance defined for PluginFamily MyProject.Controllers.HomeController, MyProject.Controllers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
با تشکر.
نظرات مطالب
مروری بر کاربردهای Action و Func - قسمت دوم
سلام میشه یه توضیحی درباره کد زیر بدید؟
    public static T CacheRead<T>(this HttpContextBase httpContext, string key, int durationMinutes, Func<T> ifNullRetrievalMethod)
        {
            var item = httpContext.Cache[key];
            if (item == null)
            {
                item = ifNullRetrievalMethod();
                if (item == null)
                    return default(T);
 
                CacheInsert(httpContext, key, item, durationMinutes);
            }
            return (T)item;
        }
مطالب
لیست کردن ایمیل‌های موجود در Global address list

Global Address List یا به اختصار GAL و یا همان Microsoft Exchange Global Address Book ، حاوی اطلاعات تمامی کاربران تعریف شده در Exchange server مایکروسافت است و زمانیکه outlook در شبکه به exchange server متصل می‌شود، کاربران می‌توانند با کمک آن لیست اعضاء را مشاهده کرده ، یک یا چند نفر را انتخاب نموده و به آن‌ها ایمیل ارسال کنند (شکل زیر):


نیاز بود تا این لیست تعریف شده در مایکروسافت اکسچنج، با اطلاعات یک دیتابیس مقایسه شوند که آیا این اطلاعات مطابق رکوردهای موجود تعریف شده یا خیر.
بنابراین اولین قدم، استخراج email های موجود در GAL بود (دسترسی به همین برگه‌ی email address که در شکل فوق ملاحظه می‌کنید از طریق برنامه نویسی) که خلاصه آن تابع زیر است:
جهت استفاده از آن ابتدا باید یک ارجاع به کتابخانه COM ایی به نام Microsoft Outlook Object Library اضافه شود.

using System.Collections.Generic;
using System.Reflection;
using Microsoft.Office.Interop.Outlook;

namespace GAL
{
//add a reference to Microsoft Outlook 12.0 Object Library
class COutLook
{
public struct User
{
public string Name;
public string Email;
}

public static List<User> ExchangeServerEmailAddresses(string userName)
{
List<User> res = new List<User>();
//Create Outlook application
Application outlookApp = new Application();
//Get Mapi NameSpace and Logon
NameSpace ns = outlookApp.GetNamespace("MAPI");
ns.Logon(userName, Missing.Value, false, true);

//Get Global Address List
AddressLists addressLists = ns.AddressLists;
AddressList globalAddressList = addressLists["Global Address List"];
AddressEntries entries = globalAddressList.AddressEntries;
foreach (AddressEntry entry in entries)
{
ExchangeUser user = entry.GetExchangeUser();
if (user != null && user.PrimarySmtpAddress != null && entry.Name != null)
res.Add(new User
{
Name = entry.Name,
Email = user.PrimarySmtpAddress
});
}

ns.Logoff();

// Clean up.
outlookApp = null;
ns = null;
addressLists = null;
globalAddressList = null;
entries = null;

return res;
}
}
}
و نحوه استفاده از آن هم به صورت زیر می‌تواند باشد:

List<COutLook.User> data = COutLook.ExchangeServerEmailAddresses("nasiri");
foreach (var list in data)
{
//....
}
در اینجا Nasiri نام کاربری شخص در دومین است (کاربر جاری لاگین کرده در سیستم).
تنها نکته‌ی مهم این کد، مهیا نبودن فیلد ایمیل در شیء AdderssEntry است که باید از طریق متد GetExchangeUser آن اقدام شود.


پاسخ به بازخورد‌های پروژه‌ها
مشکل در خودکار کردن تعاریف DbSet ها در EF Code first
من همه گزینه‌ها را تست کردم ولی بازم مدل ایجاد نمیکنه داخل دیتابیس
یکی از ایرادات این پروژه در مورد ایجاد اتوماتیک dbset‌ها در خط زیر است 
در فایل فوق  دستور زیر دارای ایراد 
private static void LoadEntities(Assembly asm, DbModelBuilder modelBuilder, string nameSpace)
        {
            var entityTypes = asm.GetTypes()
                .Where(type => type.BaseType != null &&
                               type.Namespace == nameSpace &&
                               type.BaseType == null)
                .ToList();

            entityTypes.ForEach(modelBuilder.RegisterEntityType);
        }
که هیچ وقت مسلما   entityTypes  دارای مقدار نخواهد بود 
 
پاسخ به بازخورد‌های پروژه‌ها
ایجاد گزارش با داده های ثابت و متغیر
با کمی Reflection می‌شود این list را تهیه کرد:
                var listOfRows = new List<Rpt>();
                foreach (var property in doc.GetType().GetProperties())
                {
                    var attr = property.GetCustomAttributes(true).OfType<DisplayNameAttribute>().FirstOrDefault();
                    if (attr == null)
                        continue;

                    listOfRows.Add(new Rpt
                    {
                        Title = attr.DisplayName, 
                        Value = property.GetValue(doc, null).ToSafeString()
                    });
                }

                dataSource.StronglyTypedList(listOfRows);
بجای DisplayNameAttribute، ویژگی مدنظر را قرار دهید. یک مثال
بازخوردهای پروژه‌ها
نحوه پیاده سازی متد savechange در کلاس unitofwork
جناب ربال با توجه به اینکه کلاس ApplicationDbContext شما از اینترفیس  IUnitOfWork ارث بری کرده است اما دو متد زیر
int SaveAllChanges(bool invalidateCacheDependencies = true, Guid? auditUserId = null);
Task<int> SaveAllChangesAsync(bool invalidateCacheDependencies = true, Guid? auditUserId = null);
در پیاده سازی‌های کلاس ApplicationDbContext دیده نمی‌شود و پیاده سازی این متد‌ها در کلاس BaseDbContext نوشته شده . کمی برای من مبهم بود نحوه این پیاده سازی ، اگر ممکنه این بخش از کد رو توضیح بدهید.
نظرات مطالب
بررسی ساختارهای جدید DateOnly و TimeOnly در دات نت 6
پشتیبانی از انواع داده‌ایی DateOnly, TimeOnly در EF8 اضافه شده است (برای پروایدر SQL Server):
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;


await using var context = new MyDbContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

context.Users.Add(new User
{
    Name = "John Doe",
    Birthday = new(1980, 1, 20),
    ShiftStart = new (8, 0),
    ShiftLength = TimeSpan.FromHours(8)
});
await context.SaveChangesAsync();

public class MyDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"...")
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();
    }
    public DbSet<User> Users { get; set; }
}

public class User
{
    public int Id { get; set; }
    public required string Name { get; set; }
    public DateOnly Birthday { get; set; }
    public TimeOnly ShiftStart { get; set; }
    public TimeSpan ShiftLength { get; set; }
}
با این DDL:
CREATE TABLE [Users] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    [Birthday] date NOT NULL,
    [ShiftStart] time NOT NULL,
    [ShiftLength] time NOT NULL,
    CONSTRAINT [PK_Users] PRIMARY KEY ([Id])
);

نظرات مطالب
مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger - قسمت ششم - تکمیل مستندات محافظت از API
HandleChallengeAsync  رو در حالت دیباگ تست کردم اصلا اجرا نمیشه!
private readonly SignInKeysOption _signInKeyOptions;
private string failReason;

public CredentialAuthenticationHandler(
  IOptionsMonitor < CustomAuthenticationOptions > options,
  IOptionsMonitor < SignInKeysOption > signInKeyOptions,
  ILoggerFactory logger,
  UrlEncoder encoder,
  ISystemClock clock): base(options, logger, encoder, clock) {
  _signInKeyOptions = signInKeyOptions.CurrentValue ??
    throw new ArgumentNullException(nameof(signInKeyOptions));
}

protected override Task HandleChallengeAsync(AuthenticationProperties properties) {
  Response.StatusCode = 401;

  if (failReason != null) {
    Response.HttpContext.Features.Get < IHttpResponseFeature > () !.ReasonPhrase = failReason;
  }

  return Task.CompletedTask;
}

protected async override Task < AuthenticateResult > HandleAuthenticateAsync() {
  string authorizationHeader = Request.Headers["Authorization"];
  if (authorizationHeader == null) {
    Logger.LogWarning("Authorization key in header is null or empty");
    //string result;
    //Context.Response.StatusCode = StatusCodes.Status401Unauthorized;
    //result = JsonConvert.SerializeObject(new { error = "Authorization key in header is null or empty" });
    //Context.Response.ContentType = "application/json";
    //await Context.Response.WriteAsync(result);
    failReason = "Authorization key in header is null or empty";
    return AuthenticateResult.Fail("request doesn't contains header");

    //return await Task.FromResult(AuthenticateResult.Fail("UnAuthenticate"));
  }
}
نظرات مطالب
معرفی پروژه فروشگاهی Iris Store
 تخفیف در بخش کالاهای مشابه  نمایش داده نمی‌شود. زیرا در ایندکس لوسین لیست تخفیف‌ها وارد نشده است. لیست تخفیف‌ها به ویو  ارسال می‌شود و در آنجا بررسی می‌شود که تخفیف فعال وجود دارد یا خیر! 

راه حل بنده
کنترلر:
            // در این قسمت discounts
            // در کالاهای مشابه اضافه نشده است
            var serchResult = LuceneIndex.GetMoreLikeThisProjectItems(id)
                    .Where(item => item.Category == "کالا‌ها").Skip(1).Take(8).ToList();

            List<ProductWidgetViewModel> productToDel = new List<ProductWidgetViewModel>();
            if (serchResult.Count > 0)
            {
                foreach (var product in serchResult)
                {
                    if (product.Discount > 0)
                    {
                        var resul = await _productService.GetProductDiscount(product.Id);
                        // محصول در دیتابیس وجود نداشته
                        if (resul == null)
                        {
                            productToDel.Add(product);
                            //error
                            //serchResult.Remove(product);
                        }
                        else if (resul.Discount != 0)
                        {
                            product.Discounts = new List<ProductPageDiscountWidgetViewModel>();
                            product.Discounts.Add(resul);
                        }
                    }
                }
            }

            foreach (var product in productToDel)
            {
                serchResult.Remove(product);
            }
            ViewData["SimilarProducts"] = serchResult;

سرویس:
 public async Task<ProductPageDiscountWidgetViewModel> GetProductDiscount(int productId)
        {
            var product = await _products.FirstOrDefaultAsync(p => p.Id == productId);
            //by SYA
            // در ایندکس هست اما در دیتابیس نیست product == null

            if (product == null)
            {
                return null;
            }
            else if (product.Discounts == null)
            {
                return new ProductPageDiscountWidgetViewModel { Discount = 0 };
            }
            //_mappingEngine.Map<ProductDiscount, ProductPageDiscountWidgetViewModel>(
            //    product.Discounts.OrderByDescending(p => p.EndDate).FirstOrDefault());
            foreach (var dic in product.Discounts.OrderByDescending(p => p.StartDate).ToList())
            {
                if (dic.Discount > 0 && dic.EndDate >= DateTimeExtentionService.NowIranZone())
                {
                    return _mappingEngine.Map<ProductDiscount, ProductPageDiscountWidgetViewModel>(dic);
                }
            }
            return new ProductPageDiscountWidgetViewModel { Discount = 0 };
        }

در موقع نوشتن کد حالتی را در نظر گرفتم که کالا در ایندکس لوسین وجود داشته باشد اما در دیتابیس نه!


نظرات مطالب
روشی برای DeSerialize کردن QueryString به یک کلاس
جهت افزودن پشتیبانی از حالت POST کد متد را به شکل زیر تغییر دهید:
 public T GetFromQueryString<T>(RequestType type=RequestType.GET) where T : new()
        {
            var obj = new T();
            var properties = typeof(T).GetProperties();

            var queries =new NameValueCollection();
            if(type==RequestType.GET)
                queries= HttpContext.Current.Request.QueryString;
            else
            {
                queries = HttpContext.Current.Request.Form;
            }

            foreach (var property in properties)
            {
                foreach (Attribute attribute in property.GetCustomAttributes(true))
                {
                    var requestBodyField = attribute as RequestBodyField; 
                    if (requestBodyField == null) continue;

                    //get value of query string
                    var valueAsString = queries[requestBodyField.Field];

                    if (valueAsString == null)
                    {
                        var keys = from key in queries.AllKeys where key.StartsWith(requestBodyField.Field) select key;

                        if(!keys.Any())
                            continue;

                        var collection = new NameValueCollection();

                        foreach (var key in keys)
                        {
                            var openBraketIndex = key.IndexOf("[", StringComparison.Ordinal);
                            var closeBraketIndex = key.IndexOf("]", StringComparison.Ordinal);

                            if (openBraketIndex < 0 || closeBraketIndex < 0)
                                throw new Exception("query string is crupted.");

                            openBraketIndex++;
                            //get key in [...]
                            var fieldName = key.Substring(openBraketIndex, closeBraketIndex - openBraketIndex);
                            collection.Add(fieldName, queries[key]);
                        }
                        property.SetValue(obj, collection, null);
                        continue;
                    }

                    var converter = TypeDescriptor.GetConverter(property.PropertyType);
                    var value = converter.ConvertFrom(valueAsString);

                    if (value == null)
                        continue;

                    property.SetValue(obj, value, null);
                }
            }
            return obj;
        }