2

I am working through this Xaml Brewer article and altering it slightly to use my models. It uses a generic MasterDetailViewModel base class of type T.

The base class is declared thus

    public abstract partial class MasterDetailViewModel<T> : ObservableObject
    {
        private readonly ObservableCollection<T> items = new();
etc...

My derived class declares as .

    public partial class HomePageViewModel : MasterDetailViewModel<Tenant>
    {

etc..

Class Tenant is

[Table("tenants")]
public partial class Tenant
{
    //[Column("email")]
    public string Email { get; set; }

    //[Column("first_name")]
    public string FirstName { get; set; }

    [Key]
    public int Id { get; set; }
etc..

My derived class contains a RelayCommand and a method

    [RelayCommand]
        private void Delete(int param) //param is Id of selected list item
    {
        DeleteCommand_Executed(param);
    }

        private void DeleteCommand_Executed(int parm)
        {
            if (parm > 0)
            {
                var toBeDeleted = Items.First(c => c.Id == parm); //get the record with this Id
                DeleteItem(toBeDeleted); //delete from the database
            }
        }

I want to move the RelayCommand and DeleteCommand_Executed method up into the base class. If I just move the relay command into the base class and add this protected abstract void DeleteCommand_Executed(int parm); then with this in the derived class protected override void DeleteCommand_Executed(int parm) etc. it all works fine but if I move DeleteCommand_Executed into the base class I get this error

Error (active) CS1061 'T' does not contain a definition for 'Id' and no accessible extension method 'Id' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)

I have tried adding a type identifier var toBeDeleted = Items.First<T>(c => c.Id == parm); I have looked at a few similar questions on SO and a couple of googled items and I think what I'm being told is that the T is not known until the expression has run so it cant know what properties T has? So is it possible to achieve my aim of putting everything into the base class?

Thanks in advance

2 Answers 2

0

First, define an interface like below:

public interface IHasId
{
    int Id { get; set; }
}

Then, redefine MasterDetailViewModel and Tenant classes like this:

public abstract partial class MasterDetailViewModel<T> : ObservableObject where T: IHasId
public partial class Tenant : IHasId

Now, moving the DeleteCommand_Executed method to the MasterDetailViewModel class should work fine.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank You for your help. I will work through and see what I can learn
@Ivan Thank You for your help. I will work through and see what I can learn
its better to use FirstOrDefault in case it might return no value. First - when you expect more than one result always
@Ivan Your suggestion works perfectly, thankyou but I am going to accept Hadi Fooladi Talari's answer as using an Interface has allowed me to solve some other issues
@HadiFooladiTalari thank you
|
0

You are getting the error because the compiler can't infer that a class used as T will have an Id - it can be anything at this point.

One option would be to introduce a generic constraint on T such as in the other answer by Hadi Fooladi Talari. This makes T a bit less generic because you would have to maintain certain contract either by having the same class as base for all Ts or that all Ts that are valid should implement an interface.

Another option would be to have a an abstract selector property on the base class of type Func<T,int,bool> - this way you would depend on this selector in the abstract class without needing to put any constraints on T:

public abstract class MasterDetailViewModel<T> {
    protected abstract Func<T, int, bool> DeleteSelector
    {
        get;
    }

    private void Delete(int param) 
    {
        DeleteCommand_Executed(param);
    }

    private void DeleteCommand_Executed(int parm) {
        if (parm > 0) {
            var toBeDeleted = Items.First(x => DeleteSelector(x, parm));        }
    }

}

public partial class HomePageViewModel : MasterDetailViewModel<Tenant> {
    protected override Func<Tenant, int, bool> DeleteSelector
        => (t, i) => t.Id == i;

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.