我应该将 View Model 传递给我的服务吗?如果是,该怎么做?

Should I pass View Model to my service, and if yes, how to do it?

我有一个解决方案,它使用 Autofac,并且我将一些方法从我的视图模型移到了服务文件中。现在我面临一个问题,服务方法中的一些变量引用了 VM 属性。所以我想通了,也许我应该将视图模型传递给服务。

首先,为了层分离和可测试性,我不确定我是否应该这样做。不过我猜的,mocking应该是没问题的,不对请指正

另一件事是我还不确定如何将虚拟机传递给服务。使用我当前的解决方案,您可以在下面找到,我在 Model 道具的 setter 上遇到 Whosebug 异常。问题是,现在这是我拥有的最好的,我没有任何新想法。

另一件事,如果将 VM 传递到服务层是一种反模式,我应该如何从服务层引用 VM 属性?我是否应该为此创建一些包装器,VM 和服务将引用它?

代码将在简化示例中显示。 MainViewModel:

public class MainViewModel : ViewModelBase
  {
        private MainViewModel _vm;
        private Person _person;
        private ISomeService _someService;
        public MainViewModel(ISomeService someService)
        {
            _person = new Person();
            _someService = someService;
            Name = "Slim Shady";
            _vm = new MainViewModel(_someService);

            Execute();
        }

        public string Name
        {
            get
            {
                return _person.Name;
            }
            set
            {
                _person.Name = value;
                OnPropertyChanged();
            }
        }

        private void Execute()
        {
            string dupa = _someService.GetTheName(_vm);
            System.Windows.MessageBox.Show(dupa);
        }
    }

型号:

public class Person
    {
        public string Name { get; set; }
    }

服务:

public interface ISomeService
    {
        string GetTheName(ViewModel.MainViewModel _vm);
    }
    public class SomeService : ISomeService
    {

        public string GetTheName(MainViewModel _vm)
        {
            return _vm.Name;
        }
    }

Autofac:

public class BootStrapper
    {
        public IContainer BootStrap()
        {
            var builder = new ContainerBuilder();

            builder.RegisterType<SomeService>()
              .As<ISomeService>().SingleInstance();

            builder.RegisterType<MainWindow>().AsSelf();
            builder.RegisterType<MainViewModel>().AsSelf().SingleInstance();

            return builder.Build();
        }
    }

更新 还需要考虑一些其他服务也会引用 Person.

的 Name 属性 的情况

我认为这里存在责任倒置。

如果您的服务必须产生数据,它必须保存这些数据。这不是 VM 的工作。

因此,在您的示例中,Name 是 Person 的 属性,并且反过来成为您 VM 中 getter 方法的结果。

我不知道 Autofac,但我认为您的服务应该持有 Person 对象,并将其提供给 VM(通过 IoC)。

不过,我们仍然缺少上下文信息以获取完整且合适的答案。

如果 Person 需要在 VM 中实例化,您可以简单地在 VM 中告诉您的服务实例化它,然后 return 它:

public MainViewModel(ISomeService someService)
{
    _someService = someService;
    _person = _someService.GetPerson("John");
    //etc
}

public interface ISomeService
{
    Person GetPerson(string name);
}

public class SomeService : ISomeService
{
    private List<Person> personRepository;

    //insert constructor...

    public Person GetPerson(string name)
    {
        return personRepository.Single(person => person.Name == name);
    }
}

简而言之,服务不应该知道 VM,甚至不应该知道 VM 是什么。

Should I pass View Model to my service, and if yes, how to do it?

不,你不应该。服务不应该对视图模型有任何依赖性。正好相反。

Another thing, if passing a VM to the Service layer is an antipattern, how should I refer a VM property from the Service layer?

你不知道。该服务应该 return 视图模型可能需要的任何数据,但它不应该知道任何关于视图模型类型本身的信息。

在您的示例中,服务应该只是 return 一个字符串:

public string GetTheName()
{
    return "...";
}

如果这个字符串来自视图模型,那么首先使用服务来检索它是没有意义的。然后您可以直接在视图模型 class 中访问 this.Name

如果服务以某种方式操纵名称 string,它应该接受 string 作为参数和 return 另一个字符串,例如:

public string GetTheName(string name)
{
    return name.Trim();
}

最终解决方案

在我创建的服务层中:

public interface IPersonService
    {
        string Name { get; set; }
    }

    public class PersonService : IPersonService
    {
        Person _personModel = new Person();
        public string Name
        {
            get
            {
                return _personModel.Name;
            }
            set
            {
                _personModel.Name = value;
            }
        }
    }

并修改:

public interface ISomeService
    {
        string GetTheName(IPersonService personService);
    }
    public class SomeService : ISomeService
    {

        public string GetTheName(IPersonService personService)
        {
            return personService.Name;
        }
    }

所以现在我将两种服务都注入到我的 VM 中,当从 SomeService 中检索名称时,我正在通过注入 PersonServie:

public class MainViewModel : ViewModelBase
    {
        private IPersonService _personService;
        private ISomeService _someService;
        public MainViewModel(ISomeService someService, IPersonService personService)
        {
            _personService = personService;
            _someService = someService;
            Name = "Slim Shady";

            Execute();
        }

        public string Name
        {
            get
            {
                return _personService.Name;
            }
            set
            {
                _personService.Name = value;
                OnPropertyChanged();
            }
        }

        private void Execute()
        {
            string dupa = _someService.GetTheName(_personService);
            System.Windows.MessageBox.Show(dupa);
            System.Windows.MessageBox.Show(Name);
        }
    }