dsuryd / dotNetify

Simple, lightweight, yet powerful way to build real-time web apps.
https://dotnetify.net
Other
1.17k stars 164 forks source link

Factory pattern for Dotnetify? #190

Closed kinetiq closed 5 years ago

kinetiq commented 5 years ago

I had to do some digging to figure out how to create data-driven Dotnetify models, and the best I found was a pattern like this:

var args = { ProjectId: 3 };
created: function () { dotnetify.vue.connect("ProjectStatusVM", this, { vmArg: args }) },

And the VM looks like this:

public class ProjectStatusVM : BaseVM
    {
        private readonly ProjectStatusFactory ProjectStatusFactory;

        private int _projectId;

        /// <summary>
        /// ProjectId is set automatically by Dotnetify. When it gets set,
        /// the factory is called, and this triggers model initialization.
        /// </summary>
        public int ProjectId
        {
            get => _projectId;
            set
            {
                _projectId = value;
                ProjectStatusFactory.Create(value, this);   // note that this is passed in
            }
        }

 ... 
}

This works, but I'm wondering if it's really the recommended pattern, especially on the web. When the VM holds a reference to its own factory, and that factory has a scoped DbContext, I think that's going to keep a connection to the database open for the lifetime of the VM unless I go out of my way to dispose it, which is currently making me feel like Dotnetify requires more architectural upheaval than I want to introduce. But it also seems avoidable.

It seems like it should be possible to instead have Dotnetify call a factory method, pass in parameters, and then use the result as the model. Does something like that exist?

I'm thinking something like:

created: function () { dotnetify.vue.connect("ProjectStatusFactory.Create", this, { vmArg: args }) },

...ProjectStatusFactory would be created via dependency injection, Create would be called with the args provided, and whatever it returns becomes the VM.

Please let me know if there's some guidance out there on this or if I'm thinking about this all wrong. Thanks!

dsuryd commented 5 years ago

I think the lifetime issue can be addressed by injecting the factory and keep the dbcontext short-lived:

public class ProjectStatusVM : BaseVM
{
   private readonly IProjectStatusFactory _projectStatusFactory;
   private int _projectId;

   public int ProjectId
   {
      get => _projectId;
      set
      {
          _projectId = value;
          _projectStatusFactory.Create(value, this);
      }
   }

   public ProjectStatusVM(IProjectStatusFactory factory)
   {
      _projectStatusFactory = factory;
   }
}

public class ProjectStatusFactory : IProjectStatusFactory
{
   private readonly IDbContextFactory _dbContextFactory;

   public ProjectStatusFactory(IDbContextFactory factory)
   {
      _dbContextFactory = factory;
   }

   public void Create(int projectId, ProjectStatusVM vm)
   {
      using (var dbContext = _dbContextFactory.Create())
      {
         // ...
      }
   }
}