Entity persitance inside Domain Events using a repository and Entity Framework? - entity-framework

I am delving into domain events and need some advice about persisting updates to an entity for history reasons. My example deals with a User entity and Signing In:
public class UserService
{
private UserRepository _repository;
public UserService()
{
_repository = new UserRepository();
}
public User SignIn(string username, string password)
{
var user = _repository.FindByUsernameAndPassword(username, password);
//As long as the found object is valid and an exception has not been thrown we can raise the event.
user.LastLoginDate = DateTime.Now;
user.SignIn();
return user;
}
}
public class User
{
public User(IEntityContract entityContract)
{
if (!entityContract.IsValid)
{
throw new EntityContractException;
}
}
public Guid Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public DateTime LastLoginDate { get; set; }
public void SignIn()
{
DomainEvent.Raise(new UserSignInEvent() {User = this});
}
}
public class UserSignInEvent : IDomainEvent
{
public User User { get; set; }
}
public class UserSignInHandler : Handles<UserSignInEvent>
{
public void Handle(UserSignInEvent arguments)
{
//do the stuff
}
}
So where I have the do the stuff, I want to update the User object LastLoginDate and possibly log the date and time the user logged in for historical reasons.
My question is, would I create a new instance of my repository and context to save the changes in the handler or pass something into the Event? This is what I am struggling with right now.

So where I have the do the stuff, I want to update the User object LastLoginDate and possibly log the date and time the user logged in for historical reasons.
Remembering last login date should be concern of user itself.
You already have nice extension point - user has signIn method.
My question is, would I create a new instance of my repository and context to save the changes in the handler or pass something into the Event?
User shouldn't know anything about entity framework.
Therefore - User.Events shouldn't know anything either.
Domain event handlers shouldn't know too.
Those handlers that live "outside" (e.g. in application layer) are allowed to.
But they would figure out entity framework context from elsewhere and not from user or events if necessary.
As I see it - events here are necessary for logging functionality only.
I would write something like this:
public class LoginService{
private Users _users;
public LoginService(Users users){
_users = users;
}
public User SignIn(string username, string password){
var user = _users.ByUsernameAndPassword(username, password);
user.SignIn();
return user;
}
}
public class User{
public DateTime LastLoginDate { get; set; }
public void SignIn(){
LastLoginDate = DateTime.Now;
Raise(new SignedIn(this));
}
public class SignedIn:DomainEvent<User>{
public SignedIn(User user):base(user){}
}
}
//outside of domain model
public class OnUserSignedIn:IEventHandler<User.SignedIn>{
public void Handle(User.SignedIn e){
var u=e.Source;
var message="User {0} {1} logged in on {1}"
.With(u.Name,u.LastName,u.LastLoginDate);
Console.WriteLine(message);
}
}
Bad thing about this code is that service method is command and query simultaneously
(it modifies state and returns result).
I would resolve that with introducing UserContext which would be notified that user has signed in.
That would make need for returning signed in user unnecessary,
responsibility of serving current user would be shifted to UserContext.
About repository and updating Your user - I'm pretty sure entity framework is smart enough to know how to track entity state changes. At least in NHibernate - only thing I'm doing is flushing changes when httprequest finishes.

Related

How to retrieve new ID in EF Core using UoW pattern

I'm having trouble retrieving the ID of newly added object in EF Core using the UoW pattern. I have this service:
public class OrderService : IOrderService
{
private IUnitOfWork _uow;
private IOrderRepository _orderRepository;
private IPaymentRepository _paymentRepository;
public OrderService(IUnitOfWork uow,
IOrderRepository orderRepository,
IPaymentRepository paymentRepository)
{
_uow = uow;
_orderRepository = orderRepository;
_paymentRepository = paymentRepository;
}
public int CreateOrder(Logic.Order order)
{
var id = _orderRepository.CreateOrder(order);
var payment = new Data.Payment();
payment.OrderId = id; // at this point, this is -2147353458 something
_paymentRepository.CreatePayment(payment);
// committed at this point but I can't get id unless I re-query
_uow.Commit();
// this is still -2147353458
return id;
}
}
So CreateOrder just adds a new order and then the newly generated ID is returned and used by the Payment object in CreatePayment. The problem with this since after adding, it is not committed yet so EF Core generates a temp id (something like -2147483324) so this is what I get. I then pass this to payment but this part is ok since I think EF is tracking it. The problem is what I return to the UI.
The service is called by the UI and after comitting, I can't get the ID. That's been my problem for hours now.
I've recently came across the same problem as well. Am here just to share my solution for reference.
Rather than to committing the transaction for the Id, you could try utilizing EF relationships.
Ex: the payment and order Model
public class Order
{
public int Id{ get; set; }
public Payment Payment { get; set; }
}
public class Payment
{
public int Id{ get; set; }
public int OrderId{ get; set; }
[ForeignKey("OrderId")]
public Order Order { get; set; }
}
Then in your transaction, you could simply assign the order to payment, EF will automatically insert the created Order Id to payment upon committing the transaction :
public int CreateOrder(Logic.Order order)
{
_orderRepository.CreateOrder(order);
var payment = new Data.Payment();
payment.Order = order;
_paymentRepository.CreatePayment(payment);
_uow.Commit();
return order.id;
}
You need to create an abstract method just like that "void Commit(EntityBase entity)" in your Uow, inside the method call your saveChanges this way you ensure that memory address is the same, so outside of the method you are able to access the property Id, be careful if you're using some Mapper because this may change you Memory Address. Make sure that your are using mapper only after Call UoW.Commit!
public class EntityBase
{
public int Id {get;set}
}
public class AnyOtherEntity : EntityBase
{
}
public void Commit(EntityBase entity)
{
yourContext.SaveChanges(entity);
}
Uow.Commit(AnyOtherEntity);
AnyOtherEntity.Id;
There is no option except commiting the transaction if you want to retrieve the generated unique Id immediately. Also,
// this is still -2147353458
return id;
You can't expect to be changed primitive type after commit. You should get it by order.Id, because after the transaction committed the EF will update entity because EF is tracking the entity.
public int CreateOrder(Logic.Order order)
{
_orderRepository.CreateOrder(order);
// commit the transaction
_uow.Commit();
var payment = new Data.Payment();
// Get the id of inserted row
payment.OrderId = order.Id;
_paymentRepository.CreatePayment(payment);
_uow.Commit();
return order.Id;
}

Add Columns/Properties to AspNetUserLogins/Logins in IdentityDbContext

Is it possible to add columns to the AspNetUserLogins table, or subclass the IdentityUserLogin class, such that the Identity Framework will use that class properly?
This is an answer but I'm sure it's not going to end up the best one:
It can be done, but it's ugly.
First, you'll want to make a class of all the generics you're about to use, just to make your life easier. Those are:
[Table("AspNetUserRoles")]
public class StandardUserRole : IdentityUserRole<string>
[Table("AspNetRoles")]
public class StandardRole : IdentityRole<string, StandardUserRole>
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
(The above superclasses can be found in Microsoft.AspNet.Identity.EntityFramework).
This is going to make the following generic definitions shorter, and harder to get into a place where they won't compile due to clerical errors.
While you're here may as well add these to the DbContext, which normally does not leave them available to you:
public DbSet<LoginIdentity> LoginIdentities { get; set; }
public DbSet<StandardUserRole> UserRoles { get; set; }
Now, here comes the crazy:
public class Db :
// Replace this with a custom implementation
//IdentityDbContext<Visitor>,
IdentityDbContext<Visitor, StandardRole, string, LoginIdentity,
StandardUserRole, IdentityUserClaim>,
And, Visitor is going to need its own adjustment to match this declaration:
public class Visitor : IdentityUser<string, LoginIdentity, StandardUserRole,
IdentityUserClaim>
That satisfies the Models (which btw, are best to have in their own Project for Migrations performance reasons). But, you've still got all the Identity/OWIN stuff to deal with.
By default you're provided with an ApplicationUserManager that involves a UserStore. It normally inherits from UserManager, but that's going to be too restrictive now - you need to slightly expand it:
public class VisitorManager : UserManager<Visitor, string>
{
public VisitorManager(IUserStore<Visitor, string> store)
: base(store)
{
}
public static VisitorManager Create(
IdentityFactoryOptions<VisitorManager> options,
IOwinContext context)
{
var manager = new VisitorManager(new UserStore<Visitor,
StandardRole, string, LoginIdentity, StandardUserRole,
IdentityUserClaim>(context.Get<Db>()));
I warned you about crazy. SignInManager:
public class SignInManager : SignInManager<Visitor, string>
{
public SignInManager(VisitorManager userManager,
IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(
Visitor user)
{
return user.GenerateUserIdentityAsync((VisitorManager)UserManager);
}
public static SignInManager Create(
IdentityFactoryOptions<SignInManager> options, IOwinContext context)
{
return new SignInManager(context.GetUserManager<VisitorManager>(),
context.Authentication);
}
}
That should get you through most of the dirty work. Not easy. But, having done that, you've got a working implementation where you can add extra fields to the Logins table! You can now extend the OWIN Auth stuff to provide events, and listen for the creation of new Logins. You can then respond to those by adding that extra info.
In our case, the goal was to have multiple Logins from multiple OpenId/OAuth Providers (Google, Facebook, etc) across multiple email addresses, on a single User/Visitor account. The framework does support that, but, it doesn't make a record of what Email is associated with what Login row, which is important when merging more Logins with a given account.
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
{
/// <summary>
/// The email address associated with this identity at this provider
/// </summary>
[MaxLength(300)]
public string Email { get; set; }
}
There's more you'll need to do to get the whole thing working, but it should be relatively obvious from the above starting point - with one exception, which I'll point out here.
By migrating from UserManager<TVisitor> to UserManager<TVisitor, string>, you quietly lose the ID-generation functionality built-in to the former. You'll need to emulate it yourself. As another gotcha, along the way you'll most likely implement Visitor as IUser<string>. Doing so will prevent you from setting the Id property, because it's read-only (no setter). You can avoid that with a second interface:
public interface IVisitor
{
string Id { get; set; }
string Uid { get; set; }
string UserName { get; set; }
string Email { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
ICollection<StandardUserRole> Roles { get; }
ICollection<LoginIdentity> Logins { get; }
}
With that in place you can set Id safely (even in an abstracted class):
public override Task<IdentityResult> CreateAsync(Visitor user)
{
var guid = Guid.NewGuid();
string id = guid.ToString();
((IVisitor)user).Id = id;
return base.CreateAsync(user);
}
Remember to do same for CreateAsync(Visitor user, string password). Otherwise created users explode with DbEntityValidationException complaining Id is a required field.

ASP.NET MVC 4 error updating entity framework models with related entities

I feel like this should be a pretty common thing to do. I have a model with a related object on it. Let's say it's a User and a user has one Role.
public class User
{
public int Id { get; set; }
public virtual Role Role { get; set; }
/* other stuff that saves fine */
}
public class Role
{
public int Id {get;set;}
public string Name { get;set;}
}
So if I save a new user, or if I edit a user (but don't change his Role), I have no issues. If I have a user without a role, and add a role to him, again no problem (though I manually lookup the role and assign it). If I try and change a role, I get a modelstate error on the Role property that the ID is part of the object's key and can't be changed. So how do folks go about making updates like this? Whitelist the simple values and then manually update the Role?
My controller code in question is here:
[HttpPost]
public ActionResult Save(int id, FormCollection form)
{
var user = data.Users.FirstOrDefault(d=> d.Id == id);
if (user != null)
{
TryUpdateModel(user, form.ToValueProvider());
if (!ModelState.IsValid)
{
var messages = ModelState.Values.Where(m => m.Errors.Count() > 0).SelectMany(m=>m.Errors).Select(e => e.ErrorMessage);
if (Request.IsAjaxRequest())
return Json(new { message = "Error!", errors = messages });
return RedirectToAction("index"); // TODO: more robust Flash messaging
}
updateDependencies(user);
/* negotiate response */
}
}
I'll probably just do it manually for now, but it seems like a scenario that I would have expected to work out of the box, at least to some degree.
Your User model should have a foreign key:
public int? RoleId { get; set; }
public virtual Role Role { get; set; }
You can assign a Role.Id to this value, or make it null when the user does not have a role.
I'm also not sure if your Save function is correct. I'm always using this pattern (not sure if it is correct either...), but of course it depends on the data you post to the server:
[HttpPost]
public ActionResult Save(User model)
{
if (ModelState.IsValid)
{
// Save logic here, for updating an existing entry it is something like:
context.Entry(model).State = EntityState.Modified;
context.SaveChanges();
return View("Success");
}
return View("Edit", model);
}

How to add data while inserting or updating entities

We're using RIA Services in our Silverlight app, and for one of our entities we want to track who creates and update them and when. For this we've added these properties:
public class Person
{
public string CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public string LastModifiedBy { get; set; }
public DateTime LastModifiedOn { get; set; }
}
We would like to update these values in the domain service so that we don't have to do this on the client (and because entitities will also be added/updated server side(. I tried to do it by modified the domain service method like this:
public void InsertPerson(Person person)
{
person.CreatedBy = GetCurrentUser();
person.CreatedOn = DateTime.Now();
DbEntityEntry<Person> entityEntry = this.DbContext.Entry(person);
if ((entityEntry.State != EntityState.Detached))
{
entityEntry.State = EntityState.Added;
}
else
{
this.DbContext.Persons.Add(person);
}
}
public void UpdatePerson(Person person)
{
person.LastModifiedBy = GetCurrentUser();
person.LastModifiedOn = DateTime.Now();
DbContext.Persons.AttachAsModified(person, ChangeSet.GetOriginal(person), DbContext);
}
but that didn't seem to add this data at all. I then tried to do it with sql queries after inserting/updating entities with
DbContext.Database.ExecuteSqlCommand("UPDATE Persons SET LastModifiedById = {0}, LastModifiedOn = {1} where Id = {2}", GetCurrentUser(), DateTime.Now, person.Id);
which actually updates the database, but the client is not updated/notified of the changes until the entities is fetch from the database again.
Does anyone have a good idea of how to best achieve this?
yes call the
DBContext.SaveChanges()
to actually commit the changes into the database

Fetching Strategy example in repository pattern with pure POCO Entity framework

I'm trying to roll out a strategy pattern with entity framework and the repository pattern using a simple example such as User and Post in which a user has many posts.
From this answer here, I have the following domain:
public interface IUser {
public Guid UserId { get; set; }
public string UserName { get; set; }
public IEnumerable<Post> Posts { get; set; }
}
Add interfaces to support the roles in which you will use the user.
public interface IAddPostsToUser : IUser {
public void AddPost(Post post);
}
Now my repository looks like this:
public interface IUserRepository {
User Get<TRole>(Guid userId) where TRole : IUser;
}
Strategy (Where I'm stuck). What do I do with this code? Can I have an example of how to implement this, where do I put this?
public interface IFetchingStrategy<TRole> {
TRole Fetch(Guid id, IRepository<TRole> role)
}
My basic problem was what was asked in this question. I'd like to be able to get Users without posts and users with posts using the strategy pattern.
If we talk about strategy pattern then IFetchingStrategy must be passed to IUserRepository so I think you should modify Get operation:
public interface IUserRepository
{
User Get<TRole>(Guid userId, IFetchingStrategy<TRole> strategy) where TRole : IUser;
}
But I'm not sure how to implement such interfaces with EF.
If we return to your former question, it can also be accomplished this way:
public interface IUserRepository
{
User Get(Guid userId, IEnumerable<Expression<Func<User,object>>> eagerLoading);
}
public class UserRepository : IUserRepository
{
public User Get(Guid userId, IEnumerable<Expression<Func<User,object>>> eagerLoading)
{
ObjectQuery<User> query = GetBaseQuery(); // get query somehow, for example from ObjectSet<User>
if (eagerLoading != null)
{
foreach(var expression in eagerLoading)
{
// This is not supported out of the box. You need this:
// http://msmvps.com/blogs/matthieu/archive/2008/06/06/entity-framework-include-with-func-next.aspx
query = query.Include(expression);
}
}
return query.SingleOrDefault(u => u.Id == userId);
}
}
You will use the method this way:
User userWithoutPosts = repository.Get(guid, null);
User userWithPosts = repository.Get(guid, new List<Expression<Func<User,object>>>
{
u => u.Posts
});
But I guess that this implementation works only for first level of navigation properties.
An answer in the following post uses strategy pattern to manipulate a query.
https://codereview.stackexchange.com/questions/3560/is-there-a-better-way-to-do-dynamic-filtering-and-sorting-with-entity-framework