{"id":175,"date":"2025-12-21T23:37:54","date_gmt":"2025-12-21T23:37:54","guid":{"rendered":"https:\/\/blogs.giamkichsan.com\/?p=175"},"modified":"2025-12-21T23:37:54","modified_gmt":"2025-12-21T23:37:54","slug":"iuseremailstore-trong-netcore","status":"publish","type":"post","link":"https:\/\/blogs.giamkichsan.com\/index.php\/2025\/12\/21\/iuseremailstore-trong-netcore\/","title":{"rendered":"IUserEmailStore trong netcore"},"content":{"rendered":"\n<p>N\u00f3 <strong>ch\u1ec9 l\u01b0u tr\u1eef &amp; qu\u1ea3n l\u00fd d\u1eef li\u1ec7u email c\u1ee7a User<\/strong>, kh\u00f4ng g\u1eedi email \u0111i \u0111\u00e2u c\u1ea3<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 N\u00f3 L\u00c0M:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>L\u01b0u email v\u00e0o database<\/li>\n\n\n\n<li>L\u1ea5y email c\u1ee7a user<\/li>\n\n\n\n<li>L\u01b0u tr\u1ea1ng th\u00e1i <code>EmailConfirmed<\/code><\/li>\n\n\n\n<li>T\u00ecm user theo email<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u274c N\u00f3 KH\u00d4NG L\u00c0M:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>G\u1eedi email<\/li>\n\n\n\n<li>T\u1ea1o n\u1ed9i dung email<\/li>\n\n\n\n<li>G\u1eedi link x\u00e1c nh\u1eadn<\/li>\n\n\n\n<li>G\u1eedi OTP \/ reset password<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">1. <code>IUserEmailStore&lt;TUser&gt;<\/code> l\u00e0 g\u00ec?<\/h2>\n\n\n\n<p><code>IUserEmailStore&lt;TUser&gt;<\/code> l\u00e0 <strong>interface<\/strong> d\u00f9ng \u0111\u1ec3 <strong>qu\u1ea3n l\u00fd Email c\u1ee7a User<\/strong> trong ASP.NET Core Identity.<\/p>\n\n\n\n<p>N\u00f3 cho ph\u00e9p h\u1ec7 th\u1ed1ng:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>L\u01b0u email ng\u01b0\u1eddi d\u00f9ng<\/li>\n\n\n\n<li>L\u1ea5y email<\/li>\n\n\n\n<li>Ki\u1ec3m tra email \u0111\u00e3 x\u00e1c nh\u1eadn ch\u01b0a<\/li>\n\n\n\n<li>Set tr\u1ea1ng th\u00e1i x\u00e1c nh\u1eadn email<\/li>\n<\/ul>\n\n\n\n<p>\ud83d\udc49 Identity d\u00f9ng interface n\u00e0y \u0111\u1ec3 <strong>kh\u00f4ng ph\u1ee5 thu\u1ed9c v\u00e0o database c\u1ee5 th\u1ec3<\/strong> (SQL, MongoDB, In-memory, v.v.).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. <code>IUserEmailStore<\/code> d\u00f9ng \u0111\u1ec3 l\u00e0m g\u00ec?<\/h2>\n\n\n\n<p>M\u1ed9t s\u1ed1 ch\u1ee9c n\u0103ng ch\u00ednh:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0110\u0103ng nh\u1eadp b\u1eb1ng email<\/li>\n\n\n\n<li>X\u00e1c nh\u1eadn email (Email Confirmation)<\/li>\n\n\n\n<li>Reset m\u1eadt kh\u1ea9u qua email<\/li>\n\n\n\n<li>Ki\u1ec3m tra email c\u00f3 t\u1ed3n t\u1ea1i ch\u01b0a<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">3. C\u00e1c method quan tr\u1ecdng<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>public interface IUserEmailStore&lt;TUser&gt; : IUserStore&lt;TUser&gt;\n{\n    Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken);\n    Task&lt;string&gt; GetEmailAsync(TUser user, CancellationToken cancellationToken);\n\n    Task&lt;bool&gt; GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken);\n    Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken);\n\n    Task&lt;TUser&gt; FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken);\n    Task&lt;string&gt; GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken);\n    Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken);\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4. <code>UserEmailStore<\/code> n\u1eb1m \u1edf \u0111\u00e2u trong project th\u1ef1c t\u1ebf?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">N\u1ebfu b\u1ea1n d\u00f9ng Identity m\u1eb7c \u0111\u1ecbnh (Entity Framework):<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>services.AddIdentity&lt;ApplicationUser, IdentityRole&gt;()\n        .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;();\n<\/code><\/pre>\n\n\n\n<p>\ud83d\udc49 L\u00fac n\u00e0y:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>UserManager<\/code> <strong>t\u1ef1 \u0111\u1ed9ng d\u00f9ng<\/strong> <code>UserStore<\/code><\/li>\n\n\n\n<li><code>UserStore<\/code> <strong>\u0111\u00e3 implement s\u1eb5n<\/strong> <code>IUserEmailStore<\/code><\/li>\n<\/ul>\n\n\n\n<p>B\u1ea1n <strong>kh\u00f4ng c\u1ea7n t\u1ef1 vi\u1ebft<\/strong> <code>UserEmailStore<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. Khi n\u00e0o c\u1ea7n t\u1ef1 implement <code>IUserEmailStore<\/code>?<\/h2>\n\n\n\n<p>B\u1ea1n c\u1ea7n t\u1ef1 implement n\u1ebfu:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Kh\u00f4ng d\u00f9ng Entity Framework<\/li>\n\n\n\n<li>D\u00f9ng database ri\u00eang (MongoDB, API, file, v.v.)<\/li>\n\n\n\n<li>Mu\u1ed1n custom c\u00e1ch l\u01b0u User<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class CustomUserStore :\n    IUserStore&lt;ApplicationUser&gt;,\n    IUserEmailStore&lt;ApplicationUser&gt;\n{\n    public Task SetEmailAsync(ApplicationUser user, string email, CancellationToken ct)\n    {\n        user.Email = email;\n        return Task.CompletedTask;\n    }\n\n    public Task&lt;string&gt; GetEmailAsync(ApplicationUser user, CancellationToken ct)\n    {\n        return Task.FromResult(user.Email);\n    }\n\n    public Task&lt;bool&gt; GetEmailConfirmedAsync(ApplicationUser user, CancellationToken ct)\n    {\n        return Task.FromResult(user.EmailConfirmed);\n    }\n\n    public Task SetEmailConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken ct)\n    {\n        user.EmailConfirmed = confirmed;\n        return Task.CompletedTask;\n    }\n\n    \/\/ C\u00e1c method kh\u00e1c b\u1eaft bu\u1ed9c ph\u1ea3i implement...\n}\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6. Quan h\u1ec7 v\u1edbi <code>UserManager<\/code><\/h2>\n\n\n\n<p><code>UserManager&lt;TUser&gt;<\/code> s\u1ebd g\u1ecdi <code>IUserEmailStore<\/code> <strong>\u1edf b\u00ean d\u01b0\u1edbi<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>await _userManager.SetEmailAsync(user, \"test@gmail.com\");\nawait _userManager.IsEmailConfirmedAsync(user);\n<\/code><\/pre>\n\n\n\n<p>\ud83d\udc49 B\u1ea1n <strong>kh\u00f4ng g\u1ecdi tr\u1ef1c ti\u1ebfp <code>UserEmailStore<\/code><\/strong>, m\u00e0 g\u1ecdi qua <code>UserManager<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"mh-excerpt\"><p>N\u00f3 ch\u1ec9 l\u01b0u tr\u1eef &amp; qu\u1ea3n l\u00fd d\u1eef li\u1ec7u email c\u1ee7a User, kh\u00f4ng g\u1eedi email \u0111i \u0111\u00e2u c\u1ea3 \u2705 N\u00f3 L\u00c0M: \u274c N\u00f3 KH\u00d4NG L\u00c0M: <a class=\"mh-excerpt-more\" href=\"https:\/\/blogs.giamkichsan.com\/index.php\/2025\/12\/21\/iuseremailstore-trong-netcore\/\" title=\"IUserEmailStore trong netcore\">[&#8230;]<\/a><\/p>\n<\/div>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-175","post","type-post","status-publish","format-standard","hentry","category-netcore-lap-trinh-netframework"],"_links":{"self":[{"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/posts\/175","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/comments?post=175"}],"version-history":[{"count":1,"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/posts\/175\/revisions"}],"predecessor-version":[{"id":176,"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/posts\/175\/revisions\/176"}],"wp:attachment":[{"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/media?parent=175"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/categories?post=175"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.giamkichsan.com\/index.php\/wp-json\/wp\/v2\/tags?post=175"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}