I would like to know if it is possible that, depending on the user's choice, all the questions may be skipped and the dialogue terminated.
Example, I have the next code:
public ContentClassification ContentClassification {get;set;}
public StatusOfContent StatusContent {get; set;}
public Accessibility ExternalSharing {get; set;}
Depending on the choice of "ContentClassification", skip the other questions.
Thanks in advance.
Depending on the choice of "ContentClassification", skip the other questions.
You can use FieldReflector to implement your own IField, for example:
public enum ContentClassification
{
Confidential_Restricted = 1,
Confidential_Secret = 2,
Public = 3,
Strictly_Confidential = 4,
help = 5
};
public enum StatusContent
{
Status1,
Status2
}
public enum Accessibility
{
Accessibility1,
Accessibility2
}
[Serializable]
public class Classification
{
public ContentClassification? Choice { get; set; }
public StatusContent? StatusOfContent { get; set; }
public Accessibility? Accessibility { get; set; }
public static bool Confirmation = true;
public static IForm<Classification> BuildForm()
{
return new FormBuilder<Classification>()
.Message("You want to")
.Field(new FieldReflector<Classification>(nameof(Choice))
.SetNext((value, state) =>
{
var selection = (ContentClassification)value;
if (selection == ContentClassification.help)
{
Confirmation = false;
state.Accessibility = null;
state.StatusOfContent = null;
}
else
{
Confirmation = true;
}
return new NextStep();
}))
.Field(new FieldReflector<Classification>(nameof(StatusOfContent))
.SetActive(state => Confirmation))
.Field(new FieldReflector<Classification>(nameof(Accessibility))
.SetActive(state => Confirmation))
.Build();
}
}
And in the RootDialog:
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
var form = new FormDialog<Classification>(new Classification(), Classification.BuildForm, FormOptions.PromptInStart, null);
context.Call(form, this.GetResultAsync);
return Task.CompletedTask;
}
private async Task GetResultAsync(IDialogContext context, IAwaitable<Classification> result)
{
var state = await result;
//TODO:
}
}
Using this code, when user select Help in the first dialog Choice, it will skip the following two questions and you will get the result in GetResultAsync with Choice = Help, StatusOfContent = null, Accessibility = null and so on.
Related
Working on a project thats Stores items to my sqlDb created it following this video by James Montemagno https://www.youtube.com/watch?v=XFP8Np-uRWc&ab_channel=JamesMontemagno my issue now comes when I'm trying to save a list to the sqlDb it shows that it was added however when i retrieve my data my List prop is null.
public class UserTask{
[PrimaryKey]
public string ID { get; set; }
public string Title { get; set; }
[TextBlob("TagBlobbed")]
public List<string> TagsList { get; set; }
public string TagBlobbed { get; set; }
public string Details { get; set; }
[Ignore]
public string Comment { get; set; }
public UserTask()
{
TagsList = new();
}
}
public static class PlannerDataService
{
static SQLiteAsyncConnection db;
static async Task Init()
{
if (db != null) return;
var databasePath = Path.Combine(FileSystem.AppDataDirectory, "DbTasks.db");
db = new SQLiteAsyncConnection(databasePath);
await db.CreateTableAsync<UserTask>();
}
public static async Task AddNewTask(UserTask t)
{
await Init();
var task = new UserTask()
{
ID = t.ID,
TagsList = t.TagsList,
Details = t.Details,
Title = t.Title
};
await db.InsertAsync(task);
}
public static async Task<List<UserTask>> GetUserTasks()
{
await Init();
var tasks = await db.Table<UserTask>().ToListAsync();
var t = tasks.OrderByDescending(a => a.ID).ToList();
return t;
}
public static async Task RemoveTask(string id)
{
await Init();
await db.DeleteAsync<UserTask>(id);
}
public static async Task UpdateTask(UserTask t)
{
await Init();
var task = new UserTask()
{
ID = t.ID,
TagsList = t.TagsList,
Details = t.Details,
Title = t.Title
};
await db.UpdateAsync(task);
}
}
I've seen + read questions similar to this and I've tried following their advice to no luck which is why I'm posting for a better solution without changing much of my code.
I've been playing around with the bot framework and creating a chat bot for fun that lets you detail the members of your family/pets.
Is there a way to recur over the same set of questions until the user is satisfied? Example code below:
[Prompt("What is your family name?")]
public string familyName{ get; set; }
[Prompt("What is your postcode?")]
public string postcode { get; set; }
[Prompt("Would you like to add a family member? {||}")]
public bool AddPerson { get; set; }
[Prompt("What is their name?")]
public string PersonName { get; set; }
[Prompt("How old are they?")]
public string PersonAge{ get; set; }
[Prompt("How are they related to you?")]
public string PersonRelation{ get; set; }
[Prompt("Would you like to add another family member? {||}")]
public bool addAnotherPerson { get; set; }
public IForm<Family> BuildForm()
{
return new FormBuilder<GetQuoteDialog>()
.Field(nameof(familyName))
.Field(nameof(postcode))
//Choose to add a person to the family
.Field(nameof(AddPerson))
//Details of that person.
.Field(new FieldReflector<Family>(nameof(PersonName))
.SetActive((state) => state.AddPerson== true))
.Field(new FieldReflector<Family>(nameof({PersonAge))
.SetActive((state) => state.AddPerson== true))
.Field(new FieldReflector<Family>(nameof({PersonRelation))
.SetActive((state) => state.AddPerson== true))
//Prompts the user to add another if they wish
//Recurs to the PersonName field and lets them go through the
//process of adding another member
.Field(new FieldReflector<Family>(nameof({AddAnotherMember))
.SetActive((state) => state.AddPerson== true))
.Confirm("Is this your family? {*}")
.Build();
}
}
Does anyone have an idea on how to accomplish this?
I call the formflow like this:
public async Task confirmAdd(IDialogContext context, IAwaitable<bool> result)
{
if (await result)
{
// builds and calls the form from here
var myform = new FormDialog<BuildFamily>(new BuildFamily(), BuildForm, FormOptions.PromptInStart, null);
context.Call<BuildFamily>(myform, End);
}
}
private async Task End(IDialogContext context, IAwaitable<BuildFamily> result)
{
BuildFamily data = null;
try
{
data = await result;
await context.PostAsync("Nice family you got there :)");
}
catch (OperationCanceledException)
{
await context.PostAsync("You canceled the form!");
return;
}
}
I'm not sure how to "recur over the same set of questions until the user is satisfied" within a FormFlow dialog. However, you can ask the user the question "Would you like to add more family members?" in the calling dialog, and achieve the same type of conversation flow. Remove the PostalCode and FamilyName type questions, and put them in a separate dialog. Then, in the add family members dialog do something like this:
[Serializable]
public class AddFamilyMembersDialog : IDialog<object>
{
List<Family> _familyMembers = new List<Family>();
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
PromptAddMembers(context);
}
private void PromptAddMembers(IDialogContext context)
{
PromptDialog.Text(context, AfterPromptAdd, "Would you like to add more family members?", null, 1);
}
private async Task AfterPromptAdd(IDialogContext context, IAwaitable<string> result)
{
var yesno = await result;
if (yesno.ToLower() == "yes")
{
await context.Forward(FormDialog.FromForm(Family.BuildForm), AfterAdded, null, CancellationToken.None);
}
else
{
//_familyMembers contains everyone the user wanted to add
context.Done(true);
}
}
private async Task AfterAdded(IDialogContext context, IAwaitable<Family> result)
{
var member = await result;
if (member != null)
_familyMembers.Add(member);
PromptAddMembers(context);
}
[Serializable]
public class Family
{
[Prompt("What is their name?")]
public string PersonName { get; set; }
[Prompt("How old are they?")]
public string PersonAge { get; set; }
[Prompt("How are they related to you?")]
public string PersonRelation { get; set; }
public static IForm<Family> BuildForm()
{
return new FormBuilder<Family>()
.AddRemainingFields()
.Build();
}
}
}
Environment:
I am working in Webapi. There is 2 entity classes which are follows;
public class Class1
{
public Class1()
{
this.items = new HashSet<Class2>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Class2> items { get; set; }
}
public class Class2
{
public int Id { get; set; }
public string Name { get; set; }
public int Class1Id { get; set; }
public virtual Class1 class1 { get; set; }
}
Business Layer:
The buniess layer have the following codes;
public class Class1Logic : IClass1Logic
{
private readonly IClass1Repository _repo;
public Class1Logic(IClass1Repository repository)
{
_repo = repository;
}
public async Task<bool> AddClass1ItemAsync(Class1 item)
{
_repo.Add(item);
bool status = await _repo.SaveAsync();
return status;
}
public async Task<Class1> GetClass1ItemAsync(int id)
{
return await _repo.GetAsync(id);
}
}
public class Class2Logic : IClass1Logic
{
private readonly IClass2Repository _repo;
public Class2Logic(IClass2Repository repository)
{
_repo = repository;
}
public async Task<bool> AddClass2ItemAsync(Class2 item)
{
_repo.Add(item);
bool status = await _repo.SaveAsync();
return status;
}
public async Task<Class2> GetClass2ItemAsync(int id)
{
return await _repo.GetAsync(id);
}
}
ViewModels:
public class Class1Model
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Class2Model
{
public int Id { get; internal set; }
public string Name { get; set; }
public int Class1Id { get; set; }
public string Class1Name { get; internal set; }
}
Controllers:
There are 2 contrtollers like Class1Controller and Class2Controller. Both have all CRUD operations.
[RoutePrefix("api/class1items")]
public class Class1Controller : ApiController
{
private readonly IClass1Logic _class1Logic;
private ModelFactory TheFactory;
public Class1Controller(IClass1Logic class1Logic)
{
_class1Logic = class1Logic;
TheFactory = new ModelFactory();
}
[Route("")]
public async Task<IHttpActionResult> Post(Class1Model class1Model)
{
var item = TheFactory.Parse(class1Model);
bool result = await _class1Logic.AddClassItemAsync(item);
if (!result)
{
return BadRequest("Error");
}
string uri = Url.Link("GetLabById", new { id = item.Id });
return Created(uri, TheFactory.Create(item));
}
[Route("{id:int}", Name = "GetClass1ItemById")]
public async Task<IHttpActionResult> GetClass1Item(int id)
{
Class1 item = await _class1Logic.GetClassItemAsync(id);
if (item == null)
{
return NotFound();
}
return Ok(TheFactory.Create(item));
}
}
[RoutePrefix("api/class2items")]
public class Class2Controller : ApiController
{
private readonly IClass2Logic _class2Logic;
private ModelFactory TheFactory;
public Class2Controller(IClass2Logic class2Logic)
{
_class2Logic = class2Logic;
TheFactory = new ModelFactory();
}
[Route("")]
public async Task<IHttpActionResult> Post(Class2Model class2Model)
{
var item = TheFactory.Parse(class2Model);
***//Here item should include Class1 object even if user give ClassId in class2Model***
bool result = await _class2Logic.AddClassItemAsync(item);
if (!result)
{
return BadRequest("Error");
}
string uri = Url.Link("GetClass2ItemById", new { id = item.Id });
return Created(uri, TheFactory.Create(item));
}
}
There is not dependecies in Class1. So all operations are fine. In Class2Controller post method, I got the model object as following to create Class2.
{
"id": 0,
"name": "string",
"class1Id": 1
}
Understanding:
I need to return this viewmodel to user after the create the record. The record created successfully but when mapping to viewmodel i got null exception as Class1 object not in the Class2 object.
In order to get the Class2 object including class1 object, I need to give the class1Object in the request object.
For this i need to find the Class1 object with Class1Id in the request object.
ViewMapper Code:
public class ModelFactory
{
public Class1Model Create(Class1 item)
{
return new Class1Model
{
Id = item.Id,
Name = item.Name
};
}
public Class2Model Create(Class2 item)
{
return new Class2Model
{
Id = item.Id,
Name = item.Name,
Class1Id = item.class1.Id,
Class1Name = item.class1.Name
};
}
public Class1 Parse(Class1Model modelItem)
{
return new Class1
{
Id = modelItem.Id,
Name = modelItem.Name
};
}
public Class2 Parse(Class2Model modelItem)
{
return new Class2
{
Id = modelItem.Id,
Name = modelItem.Name,
Class1Id = modelItem.Class1Id,
***/*Issue Place*/
//class1 = Need to set property by getting object using modelItem.Class1Id***
};
}
}
Issue:
Now i need to call get method of Class1Controller by passing Class1Id.
How to call and is this correct? or my design is bad?
This is initial case. If my Class3 have both Class1 and Class2 again i need to call methods of Class1 and Class2.
Please help to find the correct solution in this case
Note: I added comments the issue area to understand
Well, just to fix this issue you need to manually call _class1Logic.GetClass1ItemAsync after saving. However this doesn't look good.
More elegant ways to fix it:
1) If you always need Class2.Class1 field to be filled use Include when you fetch data (in repository): dbContext.Set<Class2>().Include(c => c.class1).
2) Also you can turn on LazyLoading for EF - I assume it should work in your case.
3) Inject class1Repo to class2Logic and fix up class1 reference after saving - in case if you don't want to enable lazy loading or item was detached from context after save method
Thoughts about design:
I suggest you to look at Automapper or simular libraries instead of ModelFactory where you going to have all mapping logic
Edit: About generic repository: you can modify you GetAsync method
public async Task<T> GetAsync<T>(int id, params Expression<Func<T, object>>[] includes)
where T: class, IEntity
{
var query = context.Set<T>().AsQueryable();
if (includes.Length > 0)
{
query = includes.Aggregate(query,
(current, include) => current.Include(include));
}
return await query.FirstOrDefaultAsync(x => x.Id == id);
}
IEntity interface:
interface IEntity
{
int Id { get; }
}
With this implementation you can use
await _repo.GetAsync<Class2>(id, x => x.class1);
I have this class:
public class CompetitionBindingViewModel
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public class CompetitionViewModel : CompetitionBindingViewModel
{
public int Id { get; set; }
public string Logo { get; set; }
public string BackgroundImage { get; set; }
public string ColourScheme { get; set; }
public bool Current { get; set; }
public CompetitionViewModel(Competition model)
{
this.Id = model.Id;
this.Name = model.Name;
this.Description = model.Description;
this.Logo = model.Logo;
this.BackgroundImage = model.BackgroundImage;
this.ColourScheme = model.ColourScheme;
this.Current = model.Current;
}
}
As you can see there are 2 ViewModels here. One is for when a competition is first created (only the Name and Description need to be supplied. Later in the project I want to assign a Logo, BackgroundImage and ColourScheme, but these are not required.
I also have a Current value which by default is set to False.
I have this function which handles all that:
[Route("")]
public async Task<IHttpActionResult> Put(CompetitionViewModel model)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
var competition = await this.service.GetAsync(model.Id);
competition.Name = (string.IsNullOrEmpty(model.Name)) ? competition.Name : model.Name;
competition.Description = (string.IsNullOrEmpty(model.Description)) ? competition.Description : model.Description;
competition.Logo = (string.IsNullOrEmpty(model.Logo)) ? competition.Logo : model.Logo;
competition.BackgroundImage = (string.IsNullOrEmpty(model.BackgroundImage)) ? competition.BackgroundImage : model.BackgroundImage;
competition.ColourScheme = (string.IsNullOrEmpty(model.ColourScheme)) ? competition.ColourScheme : model.ColourScheme;
competition.Current = (model.Current == competition.Current) ? competition.Current : model.Current;
this.service.Update(competition);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
return await base.SaveChanges();
}
Now, I like neat code and this just not do it for me. The lines that are an issue are these ones here:
competition.Name = (string.IsNullOrEmpty(model.Name)) ? competition.Name : model.Name;
competition.Description = (string.IsNullOrEmpty(model.Description)) ? competition.Description : model.Description;
competition.Logo = (string.IsNullOrEmpty(model.Logo)) ? competition.Logo : model.Logo;
competition.BackgroundImage = (string.IsNullOrEmpty(model.BackgroundImage)) ? competition.BackgroundImage : model.BackgroundImage;
competition.ColourScheme = (string.IsNullOrEmpty(model.ColourScheme)) ? competition.ColourScheme : model.ColourScheme;
competition.Current = (model.Current == competition.Current) ? competition.Current : model.Current;
What I am trying to do is check to see if a Property has changed. If it has, then take the new value otherwise leave it alone.
Can someone help me provide a better solution because this happens a lot (I have other classes and functions that do the same thing but with different properties).
I would prefer not to use a library as I don't like have libraries and using a tiny bit of it when it is there to address a multitude of situations.
Couldn't you just create a helper method or something? Not that it's pretty or has any error checking, but this works:
public static void UpdateValueIfChanged<TData,TValue>(TData data, Expression<Func<TData,TValue>> propFunc, TValue newValue)
{
var prop = (MemberExpression)propFunc.Body;
var propInfo = (PropertyInfo)prop.Member;
Func<TData, TValue> getFunc = propFunc.Compile();
TValue originalValue = getFunc(data);
if (!EqualityComparer<TValue>.Default.Equals(originalValue,newValue))
{
propInfo.SetMethod.Invoke(data, new object[] { newValue });
}
}
You can call it like UpdateIfChanged(competition c => c.Name, model.Name).
I want to use AbstractValidator<T> inside base entity class.
[Serializable]
public abstract class Entity<T> where T : Entity<T>
{
public virtual Boolean Validate(AbstractValidator<T> validator)
{
return validator.Validate(this as ValidationContext<T>).IsValid;
}
// other stuff..
}
But one of my tests fails saying that Validate() method couldn't accept null as a paramter.
[Test]
public void CategoryDescriptionIsEmpty()
{
var category = new Category
{
Title = "some title",
Description = String.Empty
};
Assert.False(category.Validate(this.validator) == true);
}
[SetUp]
public void Setup()
{
this.validator = new CategoryValidator();
}
I'm using Visual Web Developer and at the moment can't install C# Developer Express to create console application to debug the error. Since that I don't know how do I debug inside the unit test. Alternatively it would be great if some explanation could be given!
Thanks!
This topic is old, but I found useful and made a little diferent:
public abstract class WithValidation<V> where V : IValidator
{
private IValidator v = Activator.CreateInstance<V>();
public bool IsValid => !(Errors.Count() > 0);
public IEnumerable<string> Errors
{
get
{
var results = v.Validate(this);
List<string> err = new List<string>();
if (!results.IsValid)
foreach (var item in results.Errors)
err.Add(item.ErrorMessage);
return err;
}
}
}
public class Client : WithValidation<ClientValidator>
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class ClientValidator : AbstractValidator<Client>
{
public ClientValidator()
{
RuleFor(c => c.Name).NotNull();
RuleFor(c => c.Age).GreaterThan(10);
}
}
So you can use easier now like:
Client c = new Client();
var isvalid = c.IsValid;
IList<string> errors = c.Errors;
Ok!
So solution to my problem is next (at least this works as expected):
public abstract class Entity<T> where T : Entity<T>
{
public Boolean IsValid(IValidator<T> validator)
{
// var context = new ValidationContext(this);
// var instance = context.InstanceToValidate as T;
// return validator.Validate(instance).IsValid;
return validator.Validate(this as T).IsValid;
}
}
public class Rambo : Entity<Rambo>
{
public Int32 MadnessRatio { get; set; }
public Boolean CanHarmEverything { get; set; }
}
public class RamboValidator : AbstractValidator<Rambo>
{
public RamboValidator()
{
RuleFor(r => r.MadnessRatio).GreaterThan(100);
}
}
class Program
{
public static void Main(String[] args)
{
var ramboInstance = new Rambo {
MadnessRatio = 90
};
Console.WriteLine("Is Rembo still mad?");
Console.WriteLine(ramboInstance.IsValid(new RamboValidator()));
Console.ReadKey();
}
}