Holding classes in Dictionary - botframework

I have a question about holding a class in dictionary.So I am working on a project about a university.There are more than one faculty names.When user types a faculty name,I am directing user to appropriate faculty class with using context.call
So in here,if user enters show me computer engineering,user directed to the ShowComp class.
But using if-else makes code really unreadable.I thought that I can put these keywords to dictionary
But this time context.Call gives an error about the value type.What should I put dictionary value type.I couldn't figure it out.Can anyone help me please?

Since Dialogs inherit from IDialog<object>, you can put that in the dictionary:
private readonly Dictionary<string, IDialog<object>> options
= new Dictionary<string, IDialog<object>>
{ { "computer", new ShowComp() }, { "law", new ShowLaw() } };
public async Task GetFacilities(IDialogContext context, LuisResult result)
{
var entity = result.Entities.FirstOrDefault(e => options.ContainsKey(e.Entity));
if (entity != null)
{
IDialog<object> dialog = null;
if (options.TryGetValue(entity.Entity, out dialog))
{
context.Call(dialog, this.AfterResume);
}
}
}

Do it like this:
static Dictionary<String, Type> FACULTY_CLASS_MAP;
/**
* faculty class mapping.
*/
FACULTY_CLASS_MAP= new Dictionary<String, Type>
{
{ "Text", typeof(FacultyClass) }
}

Related

Why does Resolver behave differently in Query vs Mutation?

I've been struggling with the issue/behaviour described below for while now and can't seem to figure out what's going on. This is all based on inherited/adapted code, so it's completely possible that I'm missing something fundamental. It's been literally years since I've asked a question on SO, so please bear that in mind if I'm not complying with the current expectations on formatting and content/detail.
I've got a number of domain classes involved in this problem, but at a high level, what I'm trying to do is to count the number of users with a specific role in relation to a group.
The behaviour that I'm seeing when I execute a GraphQL query (See below), is that the attribute in question (capacityUsed) is populated as expected, which makes me believe that the wiring is correct.
However, when I try to access the attribute within a GraphQL mutation, the underlying data to populate the value isn't retrieved, so the default value is returned, which isn't accurate or what I want.
Ultimately what I'm trying to do is to access (and make business decisions based on) the GroupRoleCapacity.CapacityUsed attribute. I'd like to sort out what's wrong with the config/setup for the mutation so that my resolver works as expected in both situations.
I'd appreciate any help or insight into what might be causing this. I've gone through the related HotCholocate and GraphL documentation a number of times and tried just about every iteration of related keywords that I can think of searching for an explanation of what's going on, and haven't been able to find a solution.
Here's how things are currently wired up (pulling out extraneous info/attributes to keep it short):
//GraphQL Entity Configuration
public class GroupRoleCapacityType : ObjectType<GroupRoleCapacity>
{
protected override void Configure(IObjectTypeDescriptor<GroupRoleCapacity> descriptor)
{
descriptor
.Field(grc => grc.Group)
.ResolveWith<Resolvers>(r => r.GetGroup(default!, default!))
.UseDbContext<AppDbContext>()
.Description("This is the Group for this GroupRoleCapacity.");
descriptor
.Field(grc => grc.Role)
.ResolveWith<Resolvers>(r => r.GetRole(default!, default!))
.UseDbContext<AppDbContext>()
.Description("This is the Role for this GroupRoleCapacity.");
descriptor
.Field(grc => grc.CapacityUsed)
.ResolveWith<Resolvers>(r => r.GetCapacityUsed(default!, default!))
.UseDbContext<AppDbContext>()
.Description("This is the number of Users with the indicated Role for this GroupRoleCapacity.");
}
protected class Resolvers
{
public Group GetGroup([Parent] GroupRoleCapacity GroupRoleCapacity, [ScopedService] AppDbContext context)
{
return context.Groups.FirstOrDefault(p => p.Id == GroupRoleCapacity.GroupId);
}
public Role GetRole([Parent] GroupRoleCapacity GroupRoleCapacity, [ScopedService] AppDbContext context)
{
return context.Roles.FirstOrDefault(p => p.Id == GroupRoleCapacity.RoleId);
}
public int GetCapacityUsed([Parent] GroupRoleCapacity GroupRoleCapacity, [ScopedService] AppDbContext context)
{
return context.Users.Count(u => u.GroupId == GroupRoleCapacity.GroupId &&
u.UserRoles.Any(ur => ur.RoleId == GroupRoleCapacity.RoleId));
}
}
}
//GraphQL Service Startup
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration) { Configuration = configuration; }
public void ConfigureServices(IServiceCollection services) {
services.AddScoped(p => p.GetRequiredService<IDbContextFactory<AppDbContext>>().CreateDbContext());
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddType<GroupType>()
.AddType<RoleType>()
.AddType<UserType>()
.AddType<UserRoleType>()
.AddType<GroupRoleCapacityType>()
}
}
Using the query below seems to work as expected and the GroupRoleCapacity.CapacityUsed attribute is populated appropriately when accessing the data via a query
{ groupRoleCapacity { groupId roleId capacityUsed }}
//GraphQL Query
public class Query
{
[UseDbContext(typeof(AppDbContext))]
[GraphQLType(typeof(NonNullType<ListType<NonNullType<GroupRoleCapacities.GroupRoleCapacityType>>>))]
public IQueryable<GroupRoleCapacity> GetGroupRoleCapacity([ScopedService] AppDbContext context)
{
return context.GroupRoleCapacities;
}
}
However, when I use a mutation similar to below, GroupRoleCapacity.CapacityUsed isn't being set correctly, which is what's making me suspect a problem with either the Resolver implementation or the configuration.
mutation { addUserToGroup(input: { userId: 1, groupId: 1, roleId: 1}) { result { userId, groupId, roleId }}}
//GraphQL Mutation
public class Mutation
{
public Mutation(IConfiguration configuration) { }
[UseDbContext(typeof(AppDbContext))]
public async Task<AddUserToGroupPayload> AddUserToGroupAsync(AddUserToGroupInput input,
[ScopedService] AppDbContext context
)
{
int someLimit = 5;
var roleCapacity = context.GroupRoleCapacities.First(grc =>
grc.GroupId == input.GroupId &&
grc.RoleId == input.RoleId);
if(roleCapacity.CapacityUsed > someLimit) {
throw ApplicationException("limit exceeded");
}
//add user to group and save etc
return new AddUserToGroupPayload(result);
}
}
Difference here that Hot Chocolate is used only for GraphQL querying, but your code calls EF Core context explicitly. You need some common part.
What you can do:
Better expose DTO instead of database entity
Write common query which returns this DTO
Feed Hot Chocolate with this query. In this case you do not need resolvers at all (if I understand this library correctly)
Sample query (without DTO, never used Hot Chocolate and not familiar with it's attributes)
public static class MyQueries
{
public static IQueryable<GroupRoleCapacity> GetGroupRoleCapacity(AppDbContext context)
{
return context.GroupRoleCapacities.Select(c => new GroupRoleCapacity
{
GroupId = c.GroupId,
Group = c.Group, // I assume you have defined navigation properties
Role = c.Role,
RoleId = c.RoleId,
CapacityUsed = await context.Users.Count(u => u.GroupId == c.GroupId &&
u.UserRoles.Any(ur => ur.RoleId == c.RoleId))
);
}
}
Query:
//GraphQL Query
public class Query
{
[UseDbContext(typeof(AppDbContext))]
[GraphQLType(typeof(NonNullType<ListType<NonNullType<GroupRoleCapacities.GroupRoleCapacityType>>>))]
public IQueryable<GroupRoleCapacity> GetGroupRoleCapacity([ScopedService] AppDbContext context)
{
return MyQueries.GetGroupRoleCapacity(context);
}
}
Mutation:
//GraphQL Mutation
public class Mutation
{
public Mutation(IConfiguration configuration) { }
[UseDbContext(typeof(AppDbContext))]
public async Task<AddUserToGroupPayload> AddUserToGroupAsync(AddUserToGroupInput input,
[ScopedService] AppDbContext context
)
{
int someLimit = 5;
var roleCapacity = MyQueries.GetGroupRoleCapacity(context)
.FirstAsync(grc =>
grc.GroupId == input.GroupId &&
grc.RoleId == input.RoleId);
if (roleCapacity.CapacityUsed > someLimit)
{
throw ApplicationException("limit exceeded");
}
//add user to group and save etc
return new AddUserToGroupPayload(result);
}
}

How to create dynamic, multiple partial views using repository pattern in MVC

I am trying to have a general home page that depending on the parameter passed to the control, different content (modules) will be displayed.
For example, a user may select Kentucky from the menu and the id for Kentucky is 1. The home controller gets the id (1) and determines the possible modules for that
state (a simple db call.) Perhaps there is an announcements module and a contacts module for the state. An announcements module could have several items but it's only one module. There would be a partial view for each type of module.
Here is the basic setup I have.
public interface IModuleRepository
{
IList<MenuItemModule> GetMenuItemModules(int menuItem);
IList<Announcements> GetAnnouncements(int modID);
IList<News> GetNews(int modID);
IList<Contacts> GetContacts(int modID);
}
//business object
public class MenuItemModule
{
private int _MenuItemID;
private int _ModuleID;
private int _ModuleDefID;
private string _Src;
private int _ModuleOrder;
//get, set properties for these...
}
//announcements entity
public class Announcements
{
private int _ID = -1;
private int _MenuItemID = -1;
private int _ModuleID = -1;
private string _Description = string.Empty;
//get set props ...
}
In my home controller...
public class HomeController : Controller
{
private IModuleRepository modRepository;
public HomeController(IModuleRepository modRepository)
{
this.modRepository = modRepository;
}
public ViewResult Item(string ItemID)
{
//returns a list of menuitemmodules for the page. This gives me the Src or name of each
//module on the page, i.e. Announcements, news, contacts, etc.
var modules = modRepository.GetMenuItemModules(Convert.ToInt32(ItemID));
return View(modules);
}
}
I have tried several different models to return but I always run up against some contstraint. If I pass the menuitemmodules to my Item.aspx, then I can do something like this:
foreach (var mod in Model)
{
Html.RenderPartial(mod.Src, a); //needs an announcement object though
}
That makes it somewhat dynamic because I have the Src which would basically be something like "Announcements" and I can just create an announcements.ascx partial to process the module. But I have found it difficult to pass my menuitemmodule and an announcements entity as well.
I have also messed around with passing a more complex object and then testing every Src that comes through with an If statement. This would make scaling difficult in the future as I increase the number of possible modules in the app.
How can I solve my problem? I hope I have provided enough info. I like the basic idea here - http://www.mikesdotnetting.com/Article/105/ASP.NET-MVC-Partial-Views-and-Strongly-Typed-Custom-ViewModels but that seems to only work for static modules on a page.
I did try a composite view model called ModuleViewModel. Here is that attempt:
public class ModuleViewModel
{
public IList<Announcements> announcements { get; set; }
public IList<MenuItemModule> mods { get; set; }
}
If I pass that model to the Item.aspx I can do something like this (but I must be doing something wrong because something doesn't look right.)
foreach (var mod in Model)
{
if (mod.announcements.Count > 0)
{
Html.RenderPartial("Announcements", mod.announcements);
}
}
Once again, scalability is going to haunt me. I would like to have something like this on item page:
foreach (var mod in Model)
{
Html.RenderPartial(mod.Src, mod);
}
That would the correct partial view and pass it the correct model.
Create Module classes that derive from a common Module base class:
public class AnnouncementsModule : Module
{
}
public class ContactsModule : Module
{
}
In controller:
Create your various modules and put them into your overall view module (here it has a property called Modules that is an array of Module:
var viewModel = new ComplexViewModel
{
Modules = new []
{
new ContactsModule(),
new AnnouncementsModule()
}
};
return View(viewModule);
In view:
#Html.DisplayFor(x => x.Modules);
Create the partial views for each Type of Module in the appropriate 'Shared` folder. (Run it without creating them and it will show you an exception with the locations where it's looking for them).
After messing around with this for over a week, I finally managed to figure out how MVC can do what I want dynamically. I decided to post my solution for others that are new to MVC. Hopefully, the following will clear up the misunderstandings I had (although, at this point in my understanding of MVC, I cannot say this is the best approach.)
I will include the previous code snips and modifications for clarity:
public interface IModuleRepository
{
IList<MenuItemModule> GetMenuItemModules(int menuItem);
IList<Announcements> GetAnnouncements(int modID);
IList<News> GetNews(int modID);
IList<Contacts> GetContacts(int modID);
}
//business object
public class MenuItemModule
{
private int _MenuItemID;
private int _ModuleID;
private int _ModuleDefID;
private string _Src;
private int _ModuleOrder;
//get, set properties for these...
}
//announcements entity
public class Announcements : MenuItemModule
{
private int _ID = -1;
private string _Description = string.Empty;
//get set props ...
}
I also added another class:
public class AnnouncementModule : MenuItemModule
{
private IList<Announcements> _Announcements;
//get set prop
}
...and I created a model for the view
public class HomeItemViewModel
{
public MenuItemModule[] MenuItemModules { get; set; } //collection of menuitemmodules
}
In my home controller...
var menuItemModules = modRepository.GetMenuItemModules(ItemID);
if (menuItemModules.Count > 0)
{
AnnouncementModule aMod;
MenuItemModule[] mods = new MenuItemModule[menuItemModules.Count()];
int i = 0;
//loop through each MenuItemModule assign to the appropriate model
foreach (MenuItemModule mod in menuItemModules)
{
if (mod.Src == "Announcements")
{
aMod = new AnnouncementModule();
aMod.Announcements = modRepository.GetAnnouncements(mod.ModuleID);
//now add this to the menuitemmodule collection
mods[i] = aMod;
}
if (mod.Src == "Contacts")
{
//...
}
i++;
}
}
var viewModel = new HomeItemViewModel
{
MenuItemModules = mods
};
return View(viewModel);
Then I used the suggestion to use DisplayFor in the view. The view is strongly typed to HomeItemViewModel.
<%: Html.DisplayFor(m => m.MenuItemModules) %>
This iterates through the collection and based on the type, it will call that template. In this example, it calls AnnouncementModule.ascx which is strongly typed to AnnouncementModule.
foreach (var a in Model.Announcements)
{
//a.Description will give us the description of the announcement
}
I realize there are slicker ways to code the controller, and I plan on refactoring, but this skeleton should provide the basics to solve the question I posted.

Is there a way to invalidate the ASP.NET MVC 3 ActionCache

I have an ASP.NET MVC 3 application that uses custom attributes to create select controls for model properties that can be populated from external data sources at runtime. The issue is that my EditorTemplate output appear to be cached at the application level, so my drop down lists are not updated when their data source changes until the Application Pool is recycled.
I also have output the contents of the MVC 3 ActionCache that is bound to the ViewContext.HttpContext object as shown in the MVC 3 source code in System.Web.Mvc.Html.TemplateHelpers.cs:95.
Action Cache GUID: adf284af-01f1-46c8-ba15-ca2387aaa8c4:
Action Cache Collection Type: System.Collections.Generic.Dictionary``2[System.String,System.Web.Mvc.Html.TemplateHelpers+ActionCacheItem]
Action Cache Dictionary Keys: EditorTemplates/Select
So it appears that the Select editor template is definitely being cached, which would result in the TemplateHelper.ExecuteTemplate method to always return the cached value instead of calling ViewEngineResult.View.Render a second time.
Is there any way to clear the MVC ActionCache or otherwise force the Razor view engine to always re-render certain templates?
For reference, Here are the relevant framework components:
public interface ISelectProvider
{
IEnumerable<SelectListItem> GetSelectList();
}
public class SelectAttribute : Attribute, IMetadataAware
{
private readonly ISelectProvider _provider;
public SelectAttribute(Type type)
{
_provider = DependencyResolver.Current.GetService(type) as ISelectProvider;
}
public void OnMetadataCreated(ModelMetadata modelMetadata)
{
modelMetadata.TemplateHint = "Select";
modelMetadata.AdditionalValues.Add("SelectListItems", SelectList);
}
public IEnumerable<SelectListItem> SelectList
{
get
{
return _provider.GetSelectList();
}
}
}
Next, there is a custom editor template in ~\Views\Shared\EditorTemplates\Select.cshtml.
#model object
#{
var selectList = (IEnumerable<SelectListItem>)ViewData.ModelMetadata.AdditionalValues["SelectListItems"];
foreach (var item in selectList)
{
item.Selected = (item != null && Model != null && item.Value.ToString() == Model.ToString());
}
}
#Html.DropDownListFor(s => s, selectList)
Finally, I have a view model, select provider class and a simple view.
/** Providers/MySelectProvider.cs **/
public class MySelectProvider : ISelectProvider
{
public IEnumerable<SelectListItem> GetSelectList()
{
foreach (var item in System.IO.File.ReadAllLines(#"C:\Test.txt"))
{
yield return new SelectListItem() { Text = item, Value = item };
}
}
}
/** Models/ViewModel.cs **/
public class ViewModel
{
[Select(typeof(MySelectProvider))]
public string MyProperty { get; set; }
}
/** Views/Controller/MyView.cshtml **/
#model ViewModel
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="Submit" />
}
** EDIT **
Based on suggestions in the comment, I started to look more closely at the ObjectContext lifecycle. While there were some minor issues, the issue appears to be isolated to an odd behavior involving a callback within a LINQ expression in the SelectProvider implementation.
Here is the relevant code.
public abstract class SelectProvider<R, T> : ISelectProvider
where R : class, IQueryableRepository<T>
{
protected readonly R repository;
public SelectProvider(R repository)
{
this.repository = repository;
}
public virtual IEnumerable<SelectListItem> GetSelectList(Func<T, SelectListItem> func, Func<T, bool> predicate)
{
var ret = new List<SelectListItem>();
foreach (T entity in repository.Table.Where(predicate).ToList())
{
ret.Add(func(entity));
}
return ret;
}
public abstract IEnumerable<SelectListItem> GetSelectList();
}
public class PrinterSelectProvider : SelectProvider<IMyRepository, MyEntityItem>
{
public PrinterSelectProvider()
: base(DependencyResolver.Current.GetService<IMyRepository>())
{
}
public override IEnumerable<SelectListItem> GetSelectList()
{
// Create a sorted list of items (this returns stale data)
var allItems = GetSelectList(
x => new SelectListItem()
{
Text = x.DisplayName,
Value = x.Id.ToString()
},
x => x.Enabled
).OrderBy(x => x.Text);
// Do the same query, but without the callback
var otherItems = repository.Table.Where(x => x.Enabled).ToList().Select(x => new SelectListItem()
{
Text = x.DisplayName,
Value = x.Id.ToString()
}).OrderBy(x => x.Text);
System.Diagnostics.Trace.WriteLine(string.Format("Query 1: {0} items", allItems.Count()));
System.Diagnostics.Trace.WriteLine(string.Format("Query 2: {0} items", otherItems.Count()));
return allItems;
}
}
And, the captured output from the System.Diagnostics.Trace is
Query 1: 2 items
Query 2: 3 items
I'm not sure what could be going wrong here. I considered that the Select may need an Expressions, but I just double-checked and the LINQ Select method only takes Func objects.
Any additional suggetions?
Problem Solved!
I finally had a chance to re-visit this issue. The root cause had nothing to do with LINQ, the ActionCache, or the ObjectContext, rather it was related to when attribute constructors are called.
As shown, my custom SelectAttribute class calls DependencyResolver.Current.GetService in its constructor to create an instance of the ISelectProvider class. However, the ASP.NET MVC framework scans the assemblies for custom metadata attributes once and keeps a reference to them in the application scope. As explained in the linked question, accessing a Attribute triggers its constructor.
So, the constructor was run only once, rather than on each request, as I had assumed. This meant that there was actually only one, cached instance of the PrinterSelectProvider class instantiated that was shared across all requests.
I solved the problem by changing the SelectAttribute class like this:
public class SelectAttribute : Attribute, IMetadataAware
{
private readonly Type type;
public SelectAttribute(Type type)
{
this.type = type;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
// Instantiate the select provider on-demand
ISelectProvider provider = DependencyResolver.Current.GetService(type) as ISelectProvider;
modelMetadata.TemplateHint = "Select";
modelMetadata.AdditionalValues.Add("SelectListItems", provider.GetSelectList());
}
}
Tricky problem indeed!

LINQ-To-Sharepoint Multiple content types for a single list

I'm using SPMetal in order to generate entity classes for my sharepoint site and I'm not exactly sure what the best practice is to use when there are multiple content types for a single list. For instance I have a task list that contains 2 content types and I'm defining them via the config file for SPMetal. Here is my definition...
<List Member="Tasks" Name="Tasks">
<ContentType Class="LegalReview" Name="LegalReviewContent"/>
<ContentType Class="Approval" Name="ApprovalContent"/>
</List>
This seems to work pretty well in that the generated objects do inherit from WorkflowTask but the generated type for the data context is a List of WorkflowTask. So when I do a query I get back a WorkflowTask object instead of a LegalReview or Approval object. How do I make it return an object of the correct type?
[Microsoft.SharePoint.Linq.ListAttribute(Name="Tasks")]
public Microsoft.SharePoint.Linq.EntityList<WorkflowTask> Tasks {
get {
return this.GetList<WorkflowTask>("Tasks");
}
}
UPDATE
Thanks for getting back to me. I'm not sure how I recreate the type based on the SPListItem and would appreciate any feedback.
ContractManagementDataContext context = new ContractManagementDataContext(_url);
WorkflowTask task = context.Tasks.FirstOrDefault(t => t.Id ==5);
Approval a = new Approval(task.item);
public partial class Approval{
public Approval(SPListItem item){
//Set all properties here for workflowtask and approval type?
//Wouldn't there be issues since it isn't attached to the datacontext?
}
public String SomeProperty{
get{ //get from list item};
set{ //set to list item};
}
Linq2SharePoint will always return an object of the first common base ContentType for all the ContentTypes in the list. This is not only because a base type of some description must be used to combine the different ContentTypes in code but also it will then only map the fields that should definitely exist on all ContentTypes in the list. It is however possible to get access to the underlying SPListItem returned by L2SP and thus from that determine the ContentType and down cast the item.
As part of a custom repository layer that is generated from T4 templates we have a partial addition to the Item class generated by SPMetal which implements ICustomMapping to get the data not usually available on the L2SP entities. A simplified version is below which just gets the ContentType and ModifiedDate to show the methodology; though the full class we use also maps Modified By, Created Date/By, Attachments, Version, Path etc, the principle is the same for all.
public partial class Item : ICustomMapping
{
private SPListItem _SPListItem;
public SPListItem SPListItem
{
get { return _SPListItem; }
set { _SPListItem = value; }
}
public string ContentTypeId { get; internal set; }
public DateTime Modified { get; internal set; }
public virtual void MapFrom(object listItem)
{
SPListItem item = (SPListItem)listItem;
this.SPListItem = item;
this.ContentTypeId = item.ContentTypeId.ToString();
this.Modified = (DateTime)item["Modified"];
}
public virtual void MapTo(object listItem)
{
SPListItem item = (SPListItem)listItem;
item["Modified"] = this.Modified == DateTime.MinValue ? this.Modified = DateTime.Now : this.Modified;
}
public virtual void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
{
SPListItem originalItem = (SPListItem)originalListItem;
SPListItem databaseItem = (SPListItem)databaseObject;
DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];
string originalContentTypeIdValue = originalItem.ContentTypeId.ToString();
string dbContentTypeIdValue = databaseItem.ContentTypeId.ToString();
switch(mode)
{
case RefreshMode.OverwriteCurrentValues:
this.Modified = dbModifiedValue;
this.ContentTypeId = dbContentTypeIdValue;
break;
case RefreshMode.KeepCurrentValues:
databaseItem["Modified"] = this.Modified;
break;
case RefreshMode.KeepChanges:
if (this.Modified != originalModifiedValue)
{
databaseItem["Modified"] = this.Modified;
}
else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
{
this.Modified = dbModifiedValue;
}
if (this.ContentTypeId != originalContentTypeIdValue)
{
throw new InvalidOperationException("You cannot change the ContentTypeId directly");
}
else if (this.ContentTypeId == originalContentTypeIdValue && this.ContentTypeId != dbContentTypeIdValue)
{
this.ContentTypeId = dbContentTypeIdValue;
}
break;
}
}
}
Once you have the ContentType and the underlying SPListItem available on your L2SP entity it is simply a matter of writing a method which returns an instance of the derived ContentType entity from a combination of the values of the base type and the extra data for the missing fields from the SPListItem.
UPDATE: I don't actually have an example converter class as we don't use the above mapping extension to Item in this way. However I could imagine something like this would work:
public static class EntityConverter
{
public static Approval ToApproval(WorkflowTask wft)
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = wft.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = wft.SPListItem["field-name"];
return a;
}
}
Or you could put a method on a partial instance of WorkflowTask to return an Approval object.
public partial class WorkflowTask
{
public Approval ToApproval()
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = this.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = this.SPListItem["field-name"];
return a;
}
public LegalReview ToLegalReview()
{
// Create and return LegalReview as for Approval
}
}
In either situation you would need to determine the method to call to get the derived type from the ContentTypeId property of the WorkflowTask. This is the sort of code I would normally want to generate in one form or another as it will be pretty repetitive but that is a bit off-topic.

How to update a List<T> in C#?

I'm taking a basic course in C# programming, have never programmed anything before. One of our exercises is to create a program that can rent out movies (i.e. a Videostore) from scratch.
One of my classes contains customers. I need a method where the user of the program can add customers to the first list and a separate method in which to display all customers, containing the newly added customers; or if no customers are added then the original ones.
This is what I´ve done so far:
I've created a List<T> for the original customers.
I have made a method that can add customers to the first list and display them in ONE method.
The problem is that I don´t know how to update the original list of customers with the ones the user adds. If I call the entire method it will obviously (even to me..) return the entire method and make the user add the customers over again. I´ve tried creating two List<T>s, but how can I make the original list update to include the customers the user adds?? I managed to call the first list from the second but the reverse doesn't work.
I have tried and tried and tried but I´ve simply run out of ideas! For me even getting this far has been quite the challenge. I thought about giving the whole thing up. Programming is not easy.
If anyone has any suggestions I would be very happy!
namespace MyNameSpace
{
public class Customers
{
public Customers()
{
}
public string Name
{
get;
set;
}
public string Tel
{
get;
set;
}
public List<Customers> CustomerList1() //Original customers
{
List<Customers> newCustomer = new List<Customers>
{
new Customers
{
Name="A",
Tel="1"
},
new Customers
{
Name="H",
Tel="2"
},
};
return newCustomer;
}
public List<Customers> CustomerList2() //User adds new customers
{
List<Customers> custList = CustomerList1();
Console.WriteLine("---------------------------");
Console.WriteLine("New Customer");
Console.WriteLine("---------------------------");
Console.WriteLine("Name:");
Console.WriteLine("Tel:");
List<Customers> addedCustomer = new List<Customers>
{
new Customers //There is most likely a better way...
{
Name=Console.ReadLine(),
Telephone=Console.ReadLine()
}
};
custList.AddRange(addedCustomer);
Console.WriteLine("***************List******************");
foreach (Customers c in custList)
{
Console.WriteLine();
Console.WriteLine(c.Name);
Console.WriteLine(c.Tel);
Console.WriteLine();
}
Console.WriteLine("******************************************");
return addedCustomer;
}
public void CustomerView() //This method only returns original list
{
List<Customers> customers = CustomerList1();
foreach (Customers c in customers)
{
Console.WriteLine();
Console.WriteLine(c.Name);
Console.WriteLine(c.Tel);
Console.WriteLine();
}
Console.WriteLine("*******************");
}
public void CustomerListAdd() //This is another method I´ve tried to add
{ customers..
List<Customers> customers = CustomerList1();
Console.WriteLine("");
Console.WriteLine("---------------------------");
Console.WriteLine("New Customer");
Console.WriteLine("---------------------------");
Customers customerAdd = new Customers();
Console.WriteLine("Name:");
customerAdd.Name = Console.ReadLine();
Console.WriteLine("Tel:");
customerAdd.Telephone = Console.ReadLine();
customers.Add(customerAdd);
Console.WriteLine();
foreach (Customers c in customers)
{
Console.WriteLine();
Console.WriteLine(c.Name);
Console.WriteLine(c.Tel);
Console.WriteLine();
}
Console.WriteLine("*******************");
}
}
}
Thank you in advance
Make your Customer list class level:
public class MyClass
{
// this is outside of a method, but inside the class
private List<Customer> customers;
public MyClass()
{
// instantiate the customer list inside the constructor
customers = new List<Customer>();
// add a default customer to the list by calling the AddCustomer
// method in the constructor.
AddCustomer(new Customer() { Name = "A", Tel="1" });
// You can also bypass the AddCustomer method below and just call
// customers.Add() here instead. If you have other things you want
// to do (like insert the customer into a database, for example)
// you might want to keep the method like I have below.
}
public void AddCustomer(Customer cust)
{
// add the customer to the existing list.
customers.Add(cust);
}
}

Resources