Adding MVC4 Display Modes Dynamically - asp.net-mvc-4

In Global.asax, I want to add a number of display modes dynamically, based on a string array I have, thus:
foreach (string displayModeId in myDisplayModeIds)
{
modes.Insert(0, new DefaultDisplayMode(displayModeId)
{
ContextCondition = context =>
context.Request["DisplayMode"] == displayModeId
}
);
}
Of course this doesn't work, because displayModeId needs to be a constant within the scope of the ContextCondition.
Any help appreciated.

You could capture it in a closure so that it doesn't run away from you in the loop:
foreach (string displayModeId in myDisplayModeIds)
{
string dmId = displayModeId;
modes.Insert(0, new DefaultDisplayMode(displayModeId)
{
ContextCondition = context => context.Request["DisplayMode"] == dmId
});
}

Related

ASP.NET Core + Entity Framework changed data

I'm trying to save only changed entities.
If I remove this if:
if (!period.IsSame(_context.Periods.First(p => p.ID == period.ID)))
everything is fine.
But if I keep it, on the statement _context.Attach(period); or same if I use Update, I get an error:
InvalidOperationException: The instance of entity type 'Period' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked.
I don't know how test that it's really modified.
public async Task<IActionResult> OnPostAsync(List<Period> periods)
{
if (!ModelState.IsValid)
{
return Page();
}
// _context.Periods.Add(Period);
int i = 0;
foreach (var period in periods)
{
TimeOnly startTime = TimeOnly.Parse(Request.Form["StartTime" + i].ToString());
TimeOnly endTime = TimeOnly.Parse(Request.Form["EndTime" + i].ToString());
period.StartHour = startTime.Hour;
period.StartMinute = startTime.Minute;
period.EndHour = endTime.Hour;
period.EndMinute = endTime.Minute;
period.StatusDate = DateTime.Now;
// if it already exists
if (period.ID > 0)
{
// if modified
if (!period.IsSame(_context.Periods.First(p => p.ID == period.ID)))
{
_context.Attach(period);
if (period.Delete)
{
period.Status = (int)Status.deleted;
}
else
{
period.Status = (int)Status.modified;
}
}
}
// if new
else
{
period.Status = (int)Status.created;
_context.Attach(period);
}
i++;
}
await _context.SaveChangesAsync();
return RedirectToPage("./Periods");
}
I have tried both update and attach. I have search for entity tracking but it seems to be detached as soon as it's on a webpage
EF uses concept of change tracking to determine what should be done with entities. By default querying data will lead to context starting to track it hence the exception. You can mitigate it by disabling tracking by default, for example using .AsNoTracking():
if (!period.IsSame(_context.Periods.AsNoTracking().First(p => p.ID == period.ID)))
{
// ...
}
But this is not very advisable approach, due to multiple reasons - possibly change tracker will not detect any changes and you will need to handle that manually, and bigger reason - you will be querying database in a loop which is bad for application performance. Just fetch everything from the database and update it accordingly:
var existingPeriods = await _context.Periods
.Where(p => periods.Select(p => p.ID).Contains(p.ID))
.ToListAsync(); // or ToDictionaryAsync if there a lot of the periods
foreach (var period in periods)
{
var existing = existingPeriods.FirstOrDefault(p => p.ID == period.ID);
if (existing != null)
{
// maybe throw if period.ID != 0
// update data in existing
existing. ... = ...;
}
else
{
// if new ...
_context.Periods.Add(period);
}
await _context.SaveChangesAsync();
}

Localization With Database MVC

I am working on a multilingual ASP.NET MVC application (MVC4).
I want to make my resource file strings to be editable at runtime without recompiling the application and without app pool recycling And it doesn't look possible with .resx file, so I migrate to store string resources in Database.
I have to Get Each Label/String Resource From Database, so there will be more hits to database for each request. How to fix that?
I have googled around and someone suggests to load the resource in a dictionary and store it as application variable, at login/Sign In page and use that dictionary as resource instead of database hit.
I am confused, what will be effective approach.Can someone guide me in right direction to avoid more database hits?
Any help/suggestions will be highly appreciated.
Thanks
I ran into the same concerns using .resx files for localization. They just did not work well when the persons doing the translation were not programmers. Now, we have a translation page in our admin area. Works great.
One area which we still don't have a good solution for are the data annotations, which still use the .resx files. I have trimmed the source below to remove any references to our actual database structures or tables.
There is a fallback to using the underlying .resx file, if an entry does not exist in the database. If there is not an entry in the .resx file, I split the word whenever a capitol letter is found ( CamelSpace is a string extension method ).
Lastly, depending on your implementation, you may need to remove the context caching, especially if you are caching out of process.
A few examples of usage:
#LanguageDb.Translate("Enter your first name below","FirstNamePrompt")
#LanguageDb.Me.FirstName
#String
.Format(LanguageDb
.Translate(#"You can be insured for
{0} down and {1} payments of {2}"),
Model.Down,Model.NumPayments,
Model.InstallmentAmount)
public class LanguageDb : DynamicObject
{
public static dynamic Me = new LanguageDb();
private LanguageDb() { }
public static string Translate(string englishPhrase, string resourceCode = null)
{
return GetTranslation(englishPhrase, resourceCode) ?? englishPhrase;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = GetTranslation(binder.Name);
return true;
}
private static string GetTranslation(string resourceName, string resourceCode = null)
{
resourceCode = resourceCode ?? resourceName;
if (resourceCode.Contains(" ") || resourceCode.Length > 50)
{
resourceCode = resourceName.GetHashCode().ToString(CultureInfo.InvariantCulture);
}
var lang = (string)HttpContext.Current.Request.RequestContext.RouteData.Values["lang"] ?? "en";
// cache entries for an hour
var result = Get(subagent + "_" + lang + "_" + resourceCode, 3600, () =>
{
// cache a datacontext object for 30 seconds.
var context = Get("_context", 30, () => new YourDataContext()) as YourDataContext;
var translation = context.Translations.FirstOrDefault(row => row.lang == lang && row.Code == resourceCode);
if (translation == null)
{
translation = new Lookup {
Code = resourceCode,
lang = lang,
Value = Language.ResourceManager.GetString(resourceName, Language.Culture)
?? resourceName.CamelSpace()
};
context.Translations.Add(translation);
context.SaveChanges();
}
return translation.Value;
});
return result;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
// ignore this
return true;
}
public static T Get<T>(string cacheId, int secondsToCache, Func<T> getItemCallback) where T : class
{
T item = HttpRuntime.Cache.Get(cacheId) as T;
if (item == null)
{
item = getItemCallback();
if (secondsToCache > 0)
{
HttpRuntime.Cache.Insert(
cacheId, item, null, Cache.NoAbsoluteExpiration,
new TimeSpan(0, 0, secondsToCache), CacheItemPriority.Normal, null);
}
else
{
HttpRuntime.Cache.Insert(cacheId, item);
}
}
return item;
}
}

What's the fastest way to find Tags pointing to Commits?

With libgit2sharp I would like to do the following:
foreach( Commit commit in repo.Commits )
{
// How to implement assignedTags?
foreach( Tag tag in commit.assignedTags ) {}
}
I want to get all tags assigned to the current commit. Whats the best way to do that? Iterate through all Tags and see if tag.Target.Sha == commit.Sha? Thats not very performant. Is there another way?
So I want to get all tags assigned to the current commit. Whats the best way to do that? Iterate through all Tags and see if tag.Target.Sha == commit.Sha? Thats not very performant. Is there another way?
There are two things to take into account when it comes to Tags.
A Tag can point to something else than a Commit (A Tree or a Blob, for instance)
A Tag can point to another Tag (chained annotated tags)
The code below should fit your need by taking these points above into account.
Note: repo.Commits will only enumerate the commits reachable from the current branch (HEAD). The code below
extends this to easily browse all the reachable commits.
...
using (var repo = new Repository("Path/to/your/repo"))
{
// Build up a cached dictionary of all the tags that point to a commit
var dic = TagsPerPeeledCommitId(repo);
// Let's enumerate all the reachable commits (similarly to `git log --all`)
foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter {Since = repo.Refs}))
{
foreach (var tags in AssignedTags(commit, dic))
{
Console.WriteLine("Tag {0} points at {1}", tags.Name, commit.Id);
}
}
}
....
private static IEnumerable<Tag> AssignedTags(Commit commit, Dictionary<ObjectId, List<Tag>> tags)
{
if (!tags.ContainsKey(commit.Id))
{
return Enumerable.Empty<Tag>();
}
return tags[commit.Id];
}
private static Dictionary<ObjectId, List<Tag>> TagsPerPeeledCommitId(Repository repo)
{
var tagsPerPeeledCommitId = new Dictionary<ObjectId, List<Tag>>();
foreach (Tag tag in repo.Tags)
{
GitObject peeledTarget = tag.PeeledTarget;
if (!(peeledTarget is Commit))
{
// We're not interested by Tags pointing at Blobs or Trees
continue;
}
ObjectId commitId = peeledTarget.Id;
if (!tagsPerPeeledCommitId.ContainsKey(commitId))
{
// A Commit may be pointed at by more than one Tag
tagsPerPeeledCommitId.Add(commitId, new List<Tag>());
}
tagsPerPeeledCommitId[commitId].Add(tag);
}
return tagsPerPeeledCommitId;
}
Here is another version of nulltoken's answer but with using ILookupclass instead of dictionary. A bit nicer IMO:
private static ILookup<ObjectId, Tag> CreateCommitIdToTagLookup(Repository repo)
{
var commitIdToTagLookup =
repo.Tags
.Select(tag => new { Commit = tag.PeeledTarget as Commit, Tag = tag })
.Where(x => x.Commit != null)
.ToLookup(x => x.Commit.Id, x => x.Tag);
return commitIdToTagLookup;
}
and simple usage example:
using (var repo = new Repository("Path/to/your/repo"))
{
var commitIdToTagLookup = CreateCommitIdToTagLookup(repo);
foreach (var commit in repo.Commits)
{
foreach (var tag in commitIdToTagLookup[commit.Id])
{
Console.WriteLine($"Tag {tag.FriendlyName} points at {commit.Id}");
}
}
}

Copying sitecore rendering to new template programmatically using renderingDefinition.ItemId?

I have a custom sitecore button which changes the template of the current item, simple enough.
However as part of this I'm trying to also migrate the renderings of the old layout to a new layout if it's of a certain sublayout type by ItemId. However the ItemId that is returned is always null, the only value I get back from the RenderingDefinition is the UniqueId.
What am I doing wrong?
I have used this blog post as a guide.
The Code
public class ConvertToNewTemplateCommand : Command
{
protected void Run(ClientPipelineArgs args)
{
if (!SheerResponse.CheckModified())
return;
Item item = Context.ContentDatabase.Items[args.Parameters["id"]];
if (args.IsPostBack)
{
if (args.Result == "yes")
{
//Get current layout details
var originalLayoutXml = item[FieldIDs.LayoutField];
//Get new template
TemplateItem hubTemplate = Context.ContentDatabase.GetTemplate("some guid...");
//Change template
item.ChangeTemplate(hubTemplate);
//Reset laytout
ResetLayout(item);
//Get reset layout
var newLayoutXml = item[FieldIDs.LayoutField];
//Add all the module containers to the new layout in the central column
MoveModuleContainers(item, originalLayoutXml, newLayoutXml);
}
}
}
private void MoveModuleContainers(Item item, string oldXml, string newXml)
{
var oldLayout = LayoutDefinition.Parse(oldXml);
var newLayout = LayoutDefinition.Parse(newXml);
bool updated = false;
var oldRenderings = (oldLayout.Devices[0] as DeviceDefinition).Renderings;
var newRenderings = (newLayout.Devices[0] as DeviceDefinition).Renderings;
foreach (RenderingDefinition rendering in oldRenderings)
{
// Here is where the rendering.ItemID is always null
if (rendering != null && !String.IsNullOrEmpty(rendering.ItemID) && new Guid(rendering.ItemID) == new Guid("matching guid..."))
{
rendering.Placeholder = "middlecolumn";
newRenderings.Add(rendering);
updated = true;
}
}
if (updated)
{
// Save item...
}
}
}
I got onto Sitecore support in the end which informed me that I should use:
Sitecore.Data.Fields.LayoutField.GetFieldValue(item.Fields[Sitecore.FieldIDs.LayoutField])
instead of:
item[FieldIDs.LayoutField]
to get the items layoutField correctly. This results in the rendering values being parsed correctly and as they say the rest is history.

'foreach' failing when using Parallel Task Library

The following code creates the correct number of files, but every file contains the contents of the first list. Can anyone spot what I've done wrong please?
private IList<List<string>> GetLists()
{
// Code omitted for brevity...
}
private void DoSomethingInParallel()
{
var lists = GetLists();
var tasks = new List<Task>();
var factory = new TaskFactory();
foreach (var list in lists)
{
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
}
Task.WaitAll(tasks.ToArray());
}
The reason why is down to the way C# evaluates anonymous methods, they're not true closures. It really has nothing to do with the TPL. The following code prints out all d's. This is not what yoy would expect
List<Task> tasks = new List<Task>();
List<string> lists = new List<string>();
lists.AddRange(new string[] { "a", "b", "c", "d" });
foreach (var list in lists)
{
tasks.Add(Task.Factory.StartNew(() =>
{
Console.WriteLine(list);
}));
}
The reason is because the value of list when the anonymous method was created is not the one that gets evaluated in the method body. The value of list at the time the method was executed is used. You can force a fix for this by doing the following:
List<Task> tasks = new List<Task>();
List<string> lists = new List<string>();
lists.AddRange(new string[] { "a", "b", "c", "d" });
foreach (var list in lists)
{
var localList = list;
tasks.Add(Task.Factory.StartNew(() =>
{
Console.WriteLine(localList);
}));
}
You don't have to pass in the list value to the anonymous method explicitly.
This blog post goes into this in much more detail:
Link
Apologies for not replying to this earlier. I found a solution - although I don't understand why it works...
Originally, I had this ...
foreach (var list in lists)
{
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
}
Changing the sequential foreach to a parallel foreach fixes the problem...
Parallel.ForEach<string>(lists, list =>
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
);
I am not sure why you have a list for "tasks", you are only ever using one of them.
edit:
factory.StartNew Creates and starts a System.Threading.Tasks.Task!!
Thinking out loud:
so there is a separate task for each of the List<String> in its list which calls WriteListToLogFile?
I think you will need to use
ThreadPool.QueueUserWorkItem
in your code after task.Add
look at this example (see the accepted answer post) link
Ran into this same problem myself. I'm still not sure why it happens, but I was able to get it to work properly by passing in a state object
foreach (var list in lists)
{
tasks.Add(factory.StartNew((o) =>
{
var l = o as List<string>;
WriteListToLogFile(l);
}, list));
}