I am trying to get following routes so, i have the GlobalTemplatePageRouteModelConvention.
I used int? constraint to distinguish /1 from /ListJson handler but I am not happy with it, it is needlessly checking integer for each route.
Is it the ideal solution? How could i get it better?
Pages;
/Index.cshtml
/MainTestPage.cshtml
/TestDir/Index.cshtml
/TestDir/TestPage.cshtml
Handlers;
public void OnGet(int? id)
public void OnGetListJson(int? id)
Routes;
"/",
"/1",
"/ListJson",
"/ListJson/1",
"/MainTestPage",
"/MainTestPage/1",
"/MainTestPage/ListJson",
"/MainTestPage/ListJson/1",
"/TestDir",
"/TestDir/1",
"/TestDir/ListJson",
"/TestDir/ListJson/1",
"/TestDir/TestPage",
"/TestDir/TestPage/1",
"/TestDir/TestPage/ListJson",
"/TestDir/TestPage/ListJson/1",
Code;
public class GlobalTemplatePageRouteModelConvention : IPageRouteModelConvention
{
public void Apply(PageRouteModel model)
{
var selectorCount = model.Selectors.Count;
var list = new List<SelectorModel>();
var isIndexPage = model.ViewEnginePath.EndsWith("/Index", StringComparison.OrdinalIgnoreCase);
for (var i = 0; i < selectorCount; i++)
{
var selector = model.Selectors[i];
var template = selector.AttributeRouteModel.Template;
var isIndexRoute = template.EndsWith("Index", StringComparison.OrdinalIgnoreCase);
if (isIndexPage)
{
if (isIndexRoute)
{
list.Add(selector);
}
else
{
model.Selectors.Add(new SelectorModel
{
AttributeRouteModel = new AttributeRouteModel
{
Order = -2,
Template = AttributeRouteModel.CombineTemplates(selector.AttributeRouteModel.Template, "{id:int?}")
}
});
selector.AttributeRouteModel.Order = -1;
selector.AttributeRouteModel.Template = AttributeRouteModel.CombineTemplates(selector.AttributeRouteModel.Template, "{handler?}/{id?}");
}
}
else
{
model.Selectors.Add(new SelectorModel
{
AttributeRouteModel = new AttributeRouteModel
{
Order = -4,
Template = AttributeRouteModel.CombineTemplates(selector.AttributeRouteModel.Template, "{id:int?}")
}
});
selector.AttributeRouteModel.Order = -3;
selector.AttributeRouteModel.Template = AttributeRouteModel.CombineTemplates(selector.AttributeRouteModel.Template, "{handler?}/{id?}");
}
}
foreach (var selector in list)
model.Selectors.Remove(selector);
}
}
builder.AddRazorPagesOptions(o => {
o.Conventions.Add(new GlobalTemplatePageRouteModelConvention());
}
Finally, i prefer following route template convention so int constraints are eliminated and performance is not lost because of them.
Templates are specified globally, not specified within #page directive of each page.
Index routes(/Index/1) are intended for unnamed handlers, OnGet etc.
It works for following routes;
"/",
"/Index/1",
"/ListJson",
"/ListJson/1",
"/MainTestPage",
"/MainTestPage/Index/1",
"/MainTestPage/ListJson",
"/MainTestPage/ListJson/1",
"/TestDir",
"/TestDir/Index/1",
"/TestDir/ListJson",
"/TestDir/ListJson/1",
"/TestDir/TestPage",
"/TestDir/TestPage/Index/1",
"/TestDir/TestPage/ListJson",
"/TestDir/TestPage/ListJson/1",
GlobalTemplatePageRouteModelConvention;
Index route causes mismatches so it is removed.
builder.AddRazorPagesOptions(o =>
{
o.Conventions.Add(new GlobalTemplatePageRouteModelConvention());
});
public class GlobalTemplatePageRouteModelConvention : IPageRouteModelConvention
{
public void Apply(PageRouteModel model)
{
var isIndexPage = model.ViewEnginePath.EndsWith("/Index", StringComparison.OrdinalIgnoreCase);
foreach (var selector in model.Selectors.ToList())
{
var template = selector.AttributeRouteModel.Template;
if (isIndexPage)
{
var isIndexRoute = template.EndsWith("Index", StringComparison.OrdinalIgnoreCase);
if (isIndexRoute)
{
model.Selectors.Remove(selector);
continue;
}
}
selector.AttributeRouteModel.Template =
AttributeRouteModel.CombineTemplates(template,
"{handler?}/{id?}");
}
}
}
Related
I want to override route in ASP.NET Core 5
I tried this one but it is not working
var lastExistingRoute= routeBuilder.Routes.FirstOrDefault(x => ((Route)x).Name == "HomePage");
routeBuilder.Routes.Remove(lastExistingRoute);
routeBuilder.MapRoute("HomePage", "",
new { controller = "CustomPage", action = "Homepage", });
var lastDownloadRoute=routeBuilder.Routes.FirstOrDefault(x => ((Route)x).Name == "GetDownload");
routeBuilder.Routes.Remove(lastDownloadRoute);
routeBuilder.MapRoute("GetDownload", "download/getdownload/{guid}/{agree?}",
new { controller = "AzTechProduct", action = "GetPayed", });
Created same route with high different display order is worked for me
And good this is it not throwing any exception on inserting new route with the same name
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
{
endpointRouteBuilder.MapControllerRoute("NewCheckout", "onepagecheckout",
new { controller = "NewCheckout", action = "OnePageCheckout" });
}
return string.Empty;
}
public int Priority
{
get
{
return 100;
}
}
}
I am using cosmos db to store and fetch data. Previously I was using DocumentClient like:
public class ProductRepository : IProductRepository
{
private DocumentClient _documentClient;
private DocumentCollection _graphCollection;
public ProductRepository(DocumentClient documentClient, DocumentCollection graphCollection)
{
_documentClient = documentClient;
_graphCollection = graphCollection;
}
public async Task Create(Product product)
{
var createQuery = CreateQuery(product);
IDocumentQuery<dynamic> query = _documentClient.CreateGremlinQuery<dynamic>(_graphCollection, createQuery);
if(query.HasMoreResults)
{
await query.ExecuteNextAsync();
}
}
public async Task<Product> Get(string id)
{
Product product = null;
var getQuery = #"g.V('" + id + "')";
var query = _documentClient.CreateGremlinQuery<dynamic>(_graphCollection, getQuery);
if (query.HasMoreResults)
{
var result = await query.ExecuteNextAsync();
if (result.Count == 0)
return product;
var productData = (JObject)result.FirstOrDefault();
product = new Product
{
name = productData["name"].ToString()
};
}
return product;
}
}
}
But it is not unit testable so I want to convert it to IDocumentClient but IDocumentClient doesn't contain definition for CreateGremlinQuery. So what is the best possible way to convert my methods so that they will be using IDocumentClient? Do I need to use CreateDocumentQuery? if yes, how can I convert CreateGremlimQuery to CreateDocumentQuery?
There are several ways to get around that. The simplest one would be to simply hard cast your IDocumentClient to DocumentClient.
If you go with that approach your code becomes:
public class ProductRepository : IProductRepository
{
private IDocumentClient _documentClient;
private DocumentCollection _graphCollection;
public ProductRepository(IDocumentClient documentClient, DocumentCollection graphCollection)
{
_documentClient = documentClient;
_graphCollection = graphCollection;
}
public async Task Create(Product product)
{
var createQuery = CreateQuery(product);
IDocumentQuery<dynamic> query = ((DocumentClient)_documentClient).CreateGremlinQuery<dynamic>(_graphCollection, createQuery);
if(query.HasMoreResults)
{
await query.ExecuteNextAsync();
}
}
public async Task<Product> Get(string id)
{
Product product = null;
var getQuery = #"g.V('" + id + "')";
var query = ((DocumentClient)_documentClient).CreateGremlinQuery<dynamic>(_graphCollection, getQuery);
if (query.HasMoreResults)
{
var result = await query.ExecuteNextAsync();
if (result.Count == 0)
return product;
var productData = (JObject)result.FirstOrDefault();
product = new Product
{
name = productData["name"].ToString()
};
}
return product;
}
}
You could also create your own extensions for IDocumentClient.
public static class MoreGraphExtensions
{
public static IDocumentQuery<T> CreateGremlinQuery<T>(this IDocumentClient documentClient, DocumentCollection collection, string gremlinExpression, FeedOptions feedOptions = null, GraphSONMode graphSONMode = GraphSONMode.Compact)
{
return GraphExtensions.CreateGremlinQuery<T>((DocumentClient)documentClient, collection, gremlinExpression, feedOptions, graphSONMode);
}
public static IDocumentQuery<object> CreateGremlinQuery(this IDocumentClient documentClient, DocumentCollection collection, string gremlinExpression, FeedOptions feedOptions = null, GraphSONMode graphSONMode = GraphSONMode.Compact)
{
return GraphExtensions.CreateGremlinQuery<object>((DocumentClient)documentClient, collection, gremlinExpression, feedOptions, graphSONMode);
}
}
It is a pre-release however, so I do think that Microsoft will get around moving the extension methods at the interface level.
I've created a custom element that generates tabular data. For good reasons, this generates the actual HTML and inserts into the DOM without using a template.
I need to attach click observers to specific elements to I can run a function in the custom element in response to a click. If using a template, I'd use click.delegate, but I can't use that with generated HTML.
How do you attach an event handler with Aurelia other than by using jQuery?
I know this answer is late, but in case this hasn't been (properly) solved yet and/or someone else finds this in the future:
In order to make any aurelia behavior work in dynamically generated HTML, you need to compile that HTML.
I have worked on a custom element (based on how aurelia's enhance and compose work) that allows you to pass in a string of HTML and it will then be compiled, so that any behaviors like bindables, custom elements / attributes will just work. It will also re-compile when the html changes.
Here's an example: https://gist.run?id=1960218b52ba628f73774822aef55ad7
src/app.html
<template>
<dynamic-html html.bind="dynamicHtml"></dynamic-html>
</template>
src/app.ts
export class App {
public dynamicHtml: string = `
<button click.delegate="handleClick()">Click me</button>
`;
public handleClick(): void {
alert("Hello!")
}
}
src/dynamic-html.ts
import {
customElement,
TaskQueue,
bindable,
ViewCompiler,
ViewSlot,
View,
ViewResources,
Container,
ViewFactory,
inlineView,
inject,
DOM
} from "aurelia-framework";
#customElement("dynamic-html")
#inlineView("<template><div></div></template>")
#inject(DOM.Element, TaskQueue, Container, ViewCompiler)
export class DynamicHtml {
#bindable()
public html: string;
public element: HTMLElement;
private tq: TaskQueue;
private container: Container;
private viewCompiler: ViewCompile;
private runtimeView: View;
private runtimeViewSlot: ViewSlot;
private runtimeViewFactory: ViewFactory;
private runtimeViewAnchor: HTMLDivElement;
constructor(element, tq, container, viewCompiler) {
this.element = <HTMLElement>element;
this.tq = tq;
this.container = container;
this.viewCompiler = viewCompiler;
}
public bindingContext: any;
public overrideContext: any;
public bind(bindingContext: any, overrideContext: any): void {
this.bindingContext = bindingContext;
this.overrideContext = overrideContext;
if (this.html) {
this.htmlChanged(this.html, undefined);
}
}
public unbind(): void {
this.disposeView();
this.bindingContext = null;
this.overrideContext = null;
}
public needsApply: boolean = false;
public isAttached: boolean = false;
public attached(): void {
this.runtimeViewAnchor = this.element.firstElementChild;
this.isAttached = true;
if (this.needsApply) {
this.needsApply = false;
this.apply();
}
}
public detached(): void {
this.isAttached = false;
this.runtimeViewAnchor = null;
}
private htmlChanged(newValue: string, oldValue: void): void {
if (newValue) {
if (this.isAttached) {
this.tq.queueMicroTask(() => {
this.apply();
});
} else {
this.needsApply = true;
}
} else {
if (this.isApplied) {
this.disposeView();
}
}
}
private isApplied: boolean = false;
private apply(): void {
if (this.isApplied) {
this.disposeView();
}
this.compileView();
}
private disposeView(): void {
if (this.runtimeViewSlot) {
this.runtimeViewSlot.unbind();
this.runtimeViewSlot.detached();
this.runtimeViewSlot.removeAll();
this.runtimeViewSlot = null;
}
if (this.runtimeViewFactory) {
this.runtimeViewFactory = null;
}
if (this.runtimeView) {
this.runtimeView = null;
}
this.isApplied = false;
}
private compileView(): void {
this.runtimeViewFactory = createViewFactory(this.viewCompiler, this.container, this.html);
this.runtimeView = createView(this.runtimeViewFactory, this.container);
this.runtimeViewSlot = createViewSlot(this.runtimeViewAnchor);
this.runtimeViewSlot.add(this.runtimeView);
this.runtimeViewSlot.bind(this.bindingContext, this.overrideContext);
this.runtimeViewSlot.attached();
this.isApplied = true;
}
}
function createViewFactory(viewCompiler: ViewCompiler, container: Container, html: string): ViewFactory {
if (!html.startsWith("<template>")) {
html = `<template>${html}</template>`;
}
let viewResources: ViewResources = container.get(ViewResources);
let viewFactory = viewCompiler.compile(html, viewResources);
return viewFactory;
}
function createView(viewFactory: ViewFactory, container: Container): View {
let childContainer = container.createChild();
let view = viewFactory.create(childContainer);
return view;
}
function createViewSlot(containerElement: Element): ViewSlot {
let viewSlot = new ViewSlot(containerElement, true);
return viewSlot;
}
I am creating an application with MVC4 and entity framework 5. How do can I implement this?
I have looked around and found that I need to override SaveChanges .
Does anyone have any sample code on this? I am using code first approach.
As an example, the way I am saving data is as follows,
public class AuditZoneRepository : IAuditZoneRepository
{
private AISDbContext context = new AISDbContext();
public int Save(AuditZone model, ModelStateDictionary modelState)
{
if (model.Id == 0)
{
context.AuditZones.Add(model);
}
else
{
var recordToUpdate = context.AuditZones.FirstOrDefault(x => x.Id == model.Id);
if (recordToUpdate != null)
{
recordToUpdate.Description = model.Description;
recordToUpdate.Valid = model.Valid;
recordToUpdate.ModifiedDate = DateTime.Now;
}
}
try
{
context.SaveChanges();
return 1;
}
catch (Exception ex)
{
modelState.AddModelError("", "Database error has occured. Please try again later");
return -1;
}
}
}
There is no need to override SaveChanges.
You can
Trigger Context.ChangeTracker.DetectChanges(); // may be necessary depending on your Proxy approach
Then analyze the context BEFORE save.
you can then... add the Change Log to the CURRENT Unit of work.
So the log gets saved in one COMMIT transaction.
Or process it as you see fit.
But saving your change log at same time. makes sure it is ONE Transaction.
Analyzing the context sample:
I have a simple tool, to Dump context content to debug output so when in debugger I can use immediate window to check content. eg
You can use this as a starter to prepare your CHANGE Log.
Try it in debugger immediate window. I have FULL dump on my Context class.
Sample Immediate window call. UoW.Context.FullDump();
public void FullDump()
{
Debug.WriteLine("=====Begin of Context Dump=======");
var dbsetList = this.ChangeTracker.Entries();
foreach (var dbEntityEntry in dbsetList)
{
Debug.WriteLine(dbEntityEntry.Entity.GetType().Name + " => " + dbEntityEntry.State);
switch (dbEntityEntry.State)
{
case EntityState.Detached:
case EntityState.Unchanged:
case EntityState.Added:
case EntityState.Modified:
WriteCurrentValues(dbEntityEntry);
break;
case EntityState.Deleted:
WriteOriginalValues(dbEntityEntry);
break;
default:
throw new ArgumentOutOfRangeException();
}
Debug.WriteLine("==========End of Entity======");
}
Debug.WriteLine("==========End of Context======");
}
private static void WriteCurrentValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.CurrentValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.CurrentValues[cv]);
}
}
private static void WriteOriginalValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.OriginalValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.OriginalValues[cv]);
}
}
}
EDIT: Get the changes
I use this routine to get chnages...
public class ObjectPair {
public string Key { get; set; }
public object Original { get; set; }
public object Current { get; set; }
}
public virtual IList<ObjectPair> GetChanges(object poco) {
var changes = new List<ObjectPair>();
var thePoco = (TPoco) poco;
foreach (var propName in Entry(thePoco).CurrentValues.PropertyNames) {
var curr = Entry(thePoco).CurrentValues[propName];
var orig = Entry(thePoco).OriginalValues[propName];
if (curr != null && orig != null) {
if (curr.Equals(orig)) {
continue;
}
}
if (curr == null && orig == null) {
continue;
}
var aChangePair = new ObjectPair {Key = propName, Current = curr, Original = orig};
changes.Add(aChangePair);
}
return changes;
}
edit 2 If you must use the Internal Object tracking.
var context = ???// YOUR DBCONTEXT class
// get objectcontext from dbcontext...
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
// for each tracked entry
foreach (var dbEntityEntry in context.ChangeTracker.Entries()) {
//get the state entry from the statemanager per changed object
var stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(dbEntityEntry.Entity);
var modProps = stateEntry.GetModifiedProperties();
Debug.WriteLine(modProps.ToString());
}
I decompiled EF6 . Get modified is indeed using private bit array to track fields that have
been changed.
// EF decompiled source..... _modifiedFields is a bitarray
public override IEnumerable<string> GetModifiedProperties()
{
this.ValidateState();
if (EntityState.Modified == this.State && this._modifiedFields != null)
{
for (int i = 0; i < this._modifiedFields.Length; ++i)
{
if (this._modifiedFields[i])
yield return this.GetCLayerName(i, this._cacheTypeMetadata);
}
}
}
I've got a problem populating nested List<>
The object graph looks like this:
Route ⇒ Section ⇒ Co-ordinates
Whenever I try to populate Сoordinates list it just overwrites previous record and at the end gives me only the last Coordinate record. But I want all the Co-ordinates.
Here is my controller code:
List<RequestRouteDataClass> result = new List<RequestRouteDataClass> {
new RequestRouteDataClass() {
RouteRequestId = objRouteManagement.RouteRequestId,
RouteName = objRouteManagement.RouteName,
RouteDescription = objRouteManagement.RouteDescription,
RouteSections = new List<RouteSections> {
new RouteSections() {
Route_Sections_Id = objSections.Route_Sections_Id,
Section_Speed = objSections.Section_Speed,
Section_Description = objSections.Section_Description,
RouteCordinatesSections = new List<SectionCoordinatesRelationData> {
new SectionCoordinatesRelationData() {
SectionCoordinate_Relat_Id = objSectionsCordinates.SectionCoordinate_Relat_Id,
CoordinateLat = objSectionsCordinates.CoordinateLat,
CoordinateLag = objSectionsCordinates.CoordinateLag
}
}
}
}
}
If you want to use Nested List.
Your Model Contains =>
public class MainModelToUse
{
public MainModelToUse()
{
FirstListObject = new List<FirstListClass>();
}
public List<FirstListClass> FirstListObject { get; set; }
}
public class FirstListClass
{
public FirstListClass()
{
SecondListObject = new List<SecondListClass>();
}
public List<SecondListClass> SecondListObject { get; set; }
}
public class SecondListClass
{
public SecondListClass()
{
ThirdListObject = new List<ThirdListClass>();
}
public List<ThirdListClass> ThirdListObject { get; set; }
}
public class ThirdListClass
{
}
Your Code to Nested List =>
FirstListClass vmFirstClassMenu = new FirstListClass();
vmFirstClassMenu.SecondListClass = new List<SecondListClass>();
FirstListClass vmFirstClassCategory = new FirstListClass();
var dataObject1 = //Get Data By Query In Object;
foreach (Model objModel in dataObject1)
{
vmFirstClassCategory = new FirstListClass
{
//Your Items
};
var DataObject2 = //Get Data By Query In Object;
vmFirstClassCategory.SecondListClass = new List<SecondListClass>();
foreach (SecondListClass menuItem in DataObject2)
{
SecondListClass vmFirstClassMenuItem = new SecondListClass
{
//Your Items
};
var DataObject3 = //Get Data By Query In Object;
vmFirstClassMenuItem.ThirdListClass = new List<ThirdListClass>();
foreach (ThirdListClass price in DataObject3)
{
ThirdListClass vmThirdClassobj = new ThirdListClass
{
//Your Items
};
vmFirstClassMenuItem.ThirdListClass.Add(vmThirdClassobj);
}
vmFirstClassCategory.SecondListClass.Add(vmFirstClassMenuItem);
}
}
Hope this is what you are looking for.
First off: spacing helps with readability (edit: but I see you fixed that in your question already):
List<RequestRouteDataClass> result = new List<RequestRouteDataClass>
{
new RequestRouteDataClass()
{
RouteRequestId = objRouteManagement.RouteRequestId,
RouteName = objRouteManagement.RouteName,
RouteDescription = objRouteManagement.RouteDescription,
RouteSections = new List<RouteSections>
{
new RouteSections()
{
Route_Sections_Id = objSections.Route_Sections_Id,
Section_Speed = objSections.Section_Speed,
Section_Description = objSections.Section_Description,
RouteCordinatesSections = new List<SectionCoordinatesRelationData>
{
new SectionCoordinatesRelationData()
{
SectionCoordinate_Relat_Id = objSectionsCordinates.SectionCoordinate_Relat_Id,
CoordinateLat = objSectionsCordinates.CoordinateLat,
CoordinateLag =objSectionsCordinates.CoordinateLag
}
}
}
}
}
};
Next: what you are doing with the above is initiating your lists with a single element in each list. If you want more elements, you have to add them. I recommend using a foreach and the Add() functionality to fill your lists.
From your example it is not clear how your source data is stored, but if you have multiples of something I would expect those too to be in a list or an array of some kind.