How do I properly append data to my collection using MVVM? - c#

So I have created a service that is going to connect to my database and grab a few proxies every here and there so it's going to be doing is contiguously, I am going to have to make it async or with a backgroundworker so it won't deadlock the UI.
However, I've gotten to the part where I've setup my relay command and I want to invoke that function that grabs the proxies.
I have created a service that has the function in it, I didnt add the connecting stuff etc yet so this is mostly hypothetical but the question still stands.
public class ProxyDeliveryService
{
public ProxyDeliveryService()
{
}
public Proxy GrabProxy()
{
//Do work..
//Return the proxy
return null;
}
}
How do I append the data to my collection in my ViewModel with a good MVVM approach? No singletons or anything like that.
This here is throwing an error because it's expecting a delegate with a object parameter. Action<object> and a predicate so just like any other RelayCommand
public class ProxyContainerViewModel : ObservableObject
{
private ProxyDeliveryService pds = new ProxyDeliveryService();
public ObservableCollection<Proxy> Proxies { get; set; } = new ObservableCollection<Proxy>();
public RelayCommand Grabproxies { get; set; } = new RelayCommand(pds.GrabProxy(), true);
public ProxyContainerViewModel()
{
}
}

I think you are overcomplicating this. What's wrong with:
public ICommand Grabproxies { get; set; } = new RelayCommand(CreateProxy, true);
private void CreateProxy(object param)
{
Proxies.Add(pds.GrabProxy());
}

Related

Fluxor Blazor how to save a List?

I am developing a Blazor app and in this app, I need to store the state of a List of user-selected Items.
when the user presses the 'Save Changes' button I would like to store the list in the state.
So far I have written the four mandatory classes that are written in the Fluxor doc:
ServiceState:
public record ServiceState
{
public List<ServiceModel> SelectedService { get; init; }
}
ServiceFeature
public override string GetName() => nameof(ServiceState);
protected override ServiceState GetInitialState()
{
return new ServiceState
{
SelectedService = new List<ServiceModel>()
};
}
SelectServiceAction:
public class SelectServiceAction
{
public List<ServiceModel> _serviceList;
public SelectServiceAction(List<ServiceModel> choosenServices)
{
_serviceList = choosenServices;
}
}
and SelectServiceReducer:
public class SelectServiceReducer
{
[ReducerMethod]
public static ServiceState OnSelectService(ServiceState state, SelectServiceAction action)
{
return state with
{
SelectedService = action._serviceList
};
}
}
I have tried many things and nothing seems to work the List stored in the state appears always empty
but the funny thing is that in the SelectServiceAction class:
public SelectServiceAction(List<ServiceModel> choosenServices)
{
_serviceList = choosenServices;
}
if I put a breakpoint in the last } _serviceList contains correctly all the items that were contained in the list I passed to the dispatcher. It seems like the problem is in the ServiceState itself,
Do you happen to know what am I doing wrong?
If you need me to show more code, I will post it
I thank you kindly in advance.
I found a way to do this. I don't know if this is the best way but here we are.
Your SelectServiceAction should have a ServiceModel in the constructor. I also changed the name of your method. I think its good to place the verb in the method name because you're likely to have a remove as well.
public class SelectServiceAddAction
{
public ServiceModel _service {get; set; }
public SelectServiceAddAction(ServiceModel service)
{
_service = service;
}
}
then in your reducer you call the method.
public static class SelectServiceReducer
{
[ReducerMethod]
public static ServiceState OnSelectService(ServiceState state, SelectServiceAddAction action)
{
var SelectedService = state.SelectedService;
SelectedService.Add(action._service);
return state with
{
SelectedService = SelectedService
};
}
}
Also consider changing "SelectedService" to a name that involves the state such as "CurrentSelectedServices" hope this helps!

Is this pattern OK for loading&caching data by Akavache in ViewModel and binding them to UI in Xamarin.Forms?

I am trying to find some 'best practice' sample how to use Xamarin.Forms, ReactiveUI and Akavache in realworld scenario.
Lets say there is simple page representing Customer Detail. It should retrieve data from server when activated (navigated to). I like the idea of GetAndFetchLatest extension method from Akavache so I would like to use it.
I ended up with something like this:
public class CustomerDetailViewModel : ViewModelBase //(ReactiveObject, ISupportsActivation)
{
private readonly IWebApiClient webApiClient;
public Customer Customer { get; }
public ReactiveCommand<Unit, Unit> GetDataCommand { get; }
public CustomerDetailViewModel(Customer customer, IWebApiClient webApiClient = null)
{
this.Customer = customer;
this.webApiClient = webApiClient ?? Locator.Current.GetService<IWebApiClient>();
GetDataCommand = ReactiveCommand.CreateFromTask(GetData);
}
private Task GetData()
{
BlobCache.LocalMachine.GetAndFetchLatest($"customer_{Customer.Id.ToString()}",
() => webApiClient.GetCustomerDetail(Customer.Id))
.Subscribe(data =>
{
CustomerDetail = data;
});
return Task.CompletedTask;
}
private CustomerDetail customerDetail;
public CustomerDetail CustomerDetail
{
get => customerDetail;
set => this.RaiseAndSetIfChanged(ref customerDetail, value);
}
}
DTOs
public class Customer
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class CustomerDetail
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
View binding
this.WhenActivated(disposables =>
{
this.OneWayBind(this.ViewModel, x => x.Customer.Name, x => x.nameLabel.Text)
.DisposeWith(disposables);
this.OneWayBind(this.ViewModel, x => x.CustomerDetail.Description, x => x.descriptionLabel.Text)
.DisposeWith(disposables);
this.ViewModel?.GetDataCommand.Execute().Subscribe();
}
But I think this is not 100% bullet proof. There are some possible problems with this:
Is it ok to call this.ViewModel?.GetDataCommand.Execute().Subscribe(); in this.WhenActivated(d => ...) on the view when I want to load data on activation?
Binding to CustomerDetail.Description can cause NullReferenceException am I right? Or is it safe?
I want to do somethin like: "If there is CustomerDetail, show CustomerDetail.Name. When its not loaded yet, show Customer.Name". Do I need to make specific Property on ViewModel because of it?
How to indicate loading?
Am I missing something important here? Some other problems I can have with this?
You could use the WhenActivated in your ViewModel, there is a interface you can implement ISupportActivation. You can then invoke or run GetData from your ViewModel. There is also a helper extension method called InvokeCommand()
We deliberately don't propogate down. We use our own form of null propagation.
You could potentially set the text on your control in that case is one way. The WhenActivated won't happen until your View is shown.
I usually have done this as a boolean property on the ViewModel, the ViewModel can take into account different commands etc. You could potentially do a ObservableAsPropertyHelper to a command calling StartsWith(false)
Potentially I would use a ObservableAsPropertyHelper on your BlobCache but looks reasonable code.

How can I prevent MVVM Light Messenger to Not track Registered Object

I have a Good Model:
public class Good
{
public int GoodId { get; set; }
public string Name { get; set; }
}
And a GoodListViewModel :
public class GoodListViewModel()
{
// ...
ObservableCollection<Good> goods;
public Good SelectedGood
{
get { return selectedGood; }
set
{
selectedGood = value;
RaisePropertyChanged("SelectedGood");
}
}
// ...
//This will send selected Good to GoodDetailViewModel For Edit
Messenger.Default.Send(SelectedGood, "GoodForEdit");
// ...
}
And a GoodDetailViewModel :
public class GoodDetailViewModel
{
public GoodDetailViewModel(IGoodService gs)
{
// ...
Messenger.Default.Register<Good>(this,"GoodForEdit", instance);
}
public void instance(Good good)
{
good.Name = "ChangedName";
}
}
I use MVVM Light Messenger For send and recive and also ViewmodelLocator ..
Questions
for example I open a good for edit and change some data but do not save it and cancel editing ... here changed data comeback to GoodListViewModel !!!
why this happend ... because of ObservableCollection propertyChange or Messenger implementation ?
Please note that you don't produce a copy of your instance of Good here. What you effectively do is to send your original instance to the GoodDetailViewModel. If the GoodDetailViewModel makes any changes to the Good instance, the GoodListViewModel will see them as well.
You would have to implement ICloneable for the Good class to avoid this behavior - then you could send the copy, edit it, and then send it back (given that you don't want to discard all changes).

Create CSV with columns selected by the user

I have a little design problem. Let's say I have a project that contains a large number of people. I want to allow the user to export those people to a CSV file with the information he chooses.
For example, He could choose Id, Name, Phone number and according to his choice I would create the file.
Of course, there is a simple of way doing it like if(idCheckBox.Checked) getId(); etc.
I'm looking for something better. I don't want that for each new option I would like to add I would need to change the UI (e.g. New checkbox).
I thought of reading the possible options from a file, but that will only solved the UI problem. How would I know which values to get without using all those "if's" again?
You don't need a fancy design pattern for this task. However I understand you have identified a reason to change (added options in future). So you want to minimize amount of classes to be modified.
Your real problem is how to decouple CSV creation from the objects whose structure is going to change. You don't want your parsing logic to be affected whenever your Person class is changed.
In the following example the CSV object is truly decoupled from the objects it receives and parses. To achieve this, we are coding to an abstraction rather to an implementation. This way we are not even coupled to the Person object, but will welcome any objects that implement the AttributedObject interface. This dependency is being injected to our CSV parser.
I implemented this in PHP, but the idea is the same. C# is a static language, so fetching the attributes would be with a bit of change. You might use some kind of ArrayAccess interface.
interface AttributedObject {
public function getAttribute($attribute);
}
class Person implements AttributedObject {
protected $firstName;
protected $lastName;
protected $age;
protected $IQ;
public function __construct($firstName, $lastName, $age, $IQ)
{
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->age = $age;
$this->IQ = $IQ;
}
public function getAttribute($attribute)
{
if(property_exists($this, $attribute)) {
return $this->$attribute;
}
throw new \Exception("Invalid attribute");
}
}
class CSV {
protected $attributedObject = null;
protected $attributesToDisplay = null;
protected $csvRepresentation = null;
protected $delimiter = null;
public function __construct(AttributedObject $attributedObject, array $attributesToDisplay, $delimiter = '|')
{
$this->attributedObject = $attributedObject;
$this->attributesToDisplay = $attributesToDisplay;
$this->delimiter = $delimiter;
$this->generateCSV();
}
protected function generateCSV()
{
$tempCSV = null;
foreach ($this->attributesToDisplay as $attribute) {
$tempCSV[] = $this->attributedObject->getAttribute($attribute);
}
$this->csvRepresentation = $tempCSV;
}
public function storeCSV()
{
$file = fopen("tmp.csv", "w");
fputcsv($file, $this->csvRepresentation, $this->delimiter);
}
}
$person1 = new Person('John', 'Doe', 30, 0);
$csv = new CSV($person1, array('firstName', 'age', 'IQ'));
$csv->storeCSV();
You can build a mapping set of fields based what fields the user is allowed to select, and which fields are required. This data can be read from a file or database. Your import/export can be as flexible as needed.
Here is a conceivable data structure that could hold info for your import/export sets.
public class FieldDefinition
{
public FieldDataTypeEnum DataType { get; set; }
public string FieldName{get;set;}
public int MaxSize { get; set; }
public bool Required { get; set; }
public bool AllowNull { get; set; }
public int FieldIndex { get; set; }
public bool CompositeKey { get; set; }
}
public class BaseImportSet
{
private List<FieldDefinition> FieldDefinitions { get; set; }
protected virtual void PerformImportRecord(Fields selectedfields)
{
throw new ConfigurationException("Import set is not properly configured to import record.");
}
protected virtual void PerformExportRecord(Fields selectedfields)
{
throw new ConfigurationException("Export set is not properly configured to import record.");
}
public LoadFieldDefinitionsFromFile(string filename)
{
//Implement reading from file
}
}
public class UserImportSet : BaseImportSet
{
public override void PerformImportRecord(Fields selectedfields)
{
//read in data one record at a time based on a loop in base class
}
public override string PerformExportRecord(Fields selectedfields)
{
//read out data one record at a time based on a loop in base class
}
}

Binding recurring connection string constructor parameters using DI

I'm looking for advice on how best to bind a couple of connection
strings which recur throughout my dependencies.
Currently I have (using ninject):
Bind<IFoo>().To<SqlFoo>()
.WithConstructorArgument("db1ConnStr", db1ConnectionString)
.WithConstructorArgument("db2ConnStr", db2ConnectionString);
Bind<IBar>().To<SqlBar>()
.WithConstructorArgument("db1ConnStr", db1ConnectionString)
.WithConstructorArgument("db2ConnStr", db2ConnectionString);
etc.
which obviously is not the most elegant code.
Is there a way to bind the value of db1ConnectionString to every string constructor parameter named "db1ConnStr"?
I would create a class which holds the connection strings:
public class ConnectionStringProvider
{
public string Db1ConnectionString { get; set; }
public string Db2ConnectionString { get; set; }
}
Note: You can also create an interface IConnectionStringProvider for it if you want.
Then the classes SqlFoo and SqlBar can use it as a dependency
public class SqlFoo
{
public SqlFoo(ConnectionStringProvider connectionStringProvider)
{
//do something with connectionStringProvider.Db1ConnectionString
}
}
And the registration would look like this:
Bind<ConnectionStringProvider>().ToConstant(
new ConnectionStringProvider
{
Db1ConnectionString = db1ConnectionString,
Db2ConnectionString = db2ConnectionString,
});
Bind<IFoo>().To<SqlFoo>();
Bind<IBar>().To<SqlBar>();

Categories

Resources