tag:blogger.com,1999:blog-57106171951899608192024-03-13T11:58:59.149+00:00The Code GrimoireSpells, invocations, alchemy and code... well just code actually.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.comBlogger92125tag:blogger.com,1999:blog-5710617195189960819.post-14011698346791284812020-05-28T17:19:00.000+01:002020-06-16T03:46:10.124+01:00Synchronous and Asychronous ThreadSafe Blitzkrieg Caching<div style="text-align: center; width: 100%;">
<span style="font-size: large; width: 100%;"><span style="background-color: yellow; width: 100%;">Update! You can download </span><a href="https://www.nuget.org/packages/BlitzCache/" target="_blank"><span style="background-color: yellow;">BlitzCache</span></a><span style="background-color: yellow;"> from nuget now</span></span></div>
<br />
<br />
<span style="font-size: large;">Caching is necessary</span><br />
Over the years I have used a lot of caching. In fact, I consider that some things like user permissions should normally have a cache of at least one minute.<br />
<br />
<span style="font-size: large;">Blitzkrieg Caching</span><br />
Even when a method is cached there are cases when it is called again before it has finished the first time and this results in a new request to the database, and this time much slower. This is what I call <strong>The Blitzkrieg Scenario</strong>.<br />
<br />
The slowest the query the more probabilities you have for this to happen and the worse the impact. I have seen too many times SQL Server freeze in the struggle of replying to the exact same query while the query is already being executed...<br />
<br />
Ideally <strike>at least in my mind</strike> a cached method should only calculate its value once per cache period. To achieve this we could use a lock... But if I am caching different calls I want more than one call to be executed at the same time, exactly one time per cache key in parallel. This is why I created the LockDictionary class.<br />
<br />
<span style="font-size: large;">The LockDictionary</span><br />
Instead of having a lock in my cache service that will lock all the parallel calls indiscriminately I have a dictionary of locks to lock by cache key.<br />
<br />
<pre class="c#" name="code">public static class LockDictionary
{
private static readonly object dictionaryLock = new object();
private static readonly Dictionary<string, object> locks = new Dictionary<string, object>();
public static object Get(string key)
{
if (!locks.ContainsKey(key))
{
lock (dictionaryLock)
{
if (!locks.ContainsKey(key)) locks.Add(key, new object());
}
}
return locks[key];
}
}</pre>
With this I can very easily select what I want to lock
<br />
<br />
<span style="font-size: large;">GetBlitzkriegLocking</span><br />
Now I can check if something is cached and return it or lock that call in particular and calculate the value of the function passed as a parameter.<br />
<br />
<pre class="c#" name="code">public class CacheService : ICacheService
{
private readonly IMemoryCache memoryCache;
public CacheService(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
public T GetBlitzkriegLocking<T>(string cacheKey, Func<T> function, double milliseconds)
{
if (memoryCache.TryGetValue(cacheKey, out T result)) return result;
lock (LockDictionary.Get(cacheKey))
{
if (memoryCache.TryGetValue(cacheKey, out result)) return result;
result = function.Invoke();
memoryCache.Set(cacheKey, result, DateTime.Now.AddMilliseconds(milliseconds));
}
return result;
}
}</pre>
And how do I use it?<br />
<pre class="c#" name="code">var completionInfo = cacheService.GetBlitzkriegLocking($"CompletionInfo-{legalEntityDto.Id}", () => GetCompletionInfoDictionary(legalEntityDto), 500));
//Look ma, I am caching this for just 500 milliseconds and it really makes a difference
</pre>
I find this method extremely useful but sometimes the function I am calling needs to be awaited... And you <strong>Cannot await in the body of a lock statement</strong>. What do I do?<br />
<br />
<span style="font-size: large;">The SemaphoreDictionary</span><br />
Semaphores do allow you to await whatever you need, in fact they themselves are awaitable. If we translate the LockDictionary class to use semaphores it looks like this:<br />
<br />
<pre class="c#" name="code">public static class SemaphoreDictionary
{
private static readonly object dictionaryLock = new object();
private static Dictionary<string, SemaphoreSlim> locks = new Dictionary<string, SemaphoreSlim>();
public static SemaphoreSlim Get(string key)
{
if (!locks.ContainsKey(key))
{
lock (dictionaryLock)
{
if (!locks.ContainsKey(key)) locks.Add(key, new SemaphoreSlim(1,1));
}
}
return locks[key];
}
}</pre>
And using this I can await calls while I am locking stuff.<br />
<br />
<br />
<span style="font-size: large;">The Awaitable GetBlitzkriegLocking</span><br />
<br />
The main rule about semaphores is that you must make sure you release them or they will be locked forever. <strike>Catching the error is optional though</strike>.
<br />
<pre class="c#" name="code">public async Task<T> GetBlitzkriegLocking<T>(string cacheKey, Func<Task<T>> function, double milliseconds)
{
if (memoryCache.TryGetValue(cacheKey, out T result)) return result;
var semaphore = SemaphoreDictionary.Get(cacheKey);
try
{
await semaphore.WaitAsync();
if (!memoryCache.TryGetValue(cacheKey, out result))
{
result = await function.Invoke();
memoryCache.Set(cacheKey, result, DateTime.Now.AddMilliseconds(milliseconds));
}
}
finally
{
semaphore.Release();
}
return result;
}</pre>
And how do I use it?<br />
<pre class="c#" name="code">await cache.GetBlitzkriegLocking($"RequestPermissions-{userSid}-{workplace}", () => RequestPermissionsAsync(userSid, workplace), 60 * 1000);</pre>
Please give these methods a try and let me know how you would improve them. I am using them every now and then and I really enjoy how they simplify the code. I hope you like them too.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-27700636024736033952019-04-22T09:13:00.001+01:002019-04-22T09:14:11.297+01:00Concurrency Errors and Value Objects in Entity Framework 2.xWe have been using value objects in our databases for a couple of months and after we passed the first hurdles with entity framework we have noticed improvements in speed and a significant decrease in the Includes wich is all what we wanted to get... but suddenly...<br />
<br />
In one of the microservices we noticed we were getting concurrency exceptions every time we updated a value object. And it wasn't a complicated nested value object, it was a value object made of two guids and two strings.<br />
<br />
I spent a good afternoon trying to figure out why were we getting the error and could not see anything wrong in the code... and I thought... it's entity framework again!<br />
<br />
I asked for help to the rest of the team and they found this thread <a href="https://github.com/aspnet/EntityFrameworkCore/issues/12865">Changes on Owned Entites Properties causes a concurrency conflict on same dbContext</a> where they propose a workaround. We tried and it didn't work but we thought it was going in the right direction so we debug it and change it a bit and voilà the rowversion column was being updated again both in SQL and in my backend!<br />
<br />
The modified code is this:<br />
<pre class="c#" name="code">private void ConcurrencyFix()
{
var changedEntriesWithVos = ChangeTracker.Entries().Where(e =>
e.State == EntityState.Unchanged
&& e.References.Any(r =>
r.TargetEntry != null
&& (r.TargetEntry.State == EntityState.Modified || r.TargetEntry.State == EntityState.Added)
&& r.TargetEntry.Metadata.IsOwned()
&& e.Metadata.Relational().TableName == r.TargetEntry.Metadata.Relational().TableName)).ToArray();
foreach (var entry in changedEntriesWithVos)
entry.State = EntityState.Modified;
}</pre>
<br />
And we have placed it in our SaveChanges methods in our base context, from which all of our contexts are inheriting so we are sure this code is always being executed when we save.
<br />
<pre class="c#" name="code">public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
ConcurrencyFix();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
ConcurrencyFix();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}</int></pre>
<br />
Have fun everyone!Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-19931430663895657622019-03-23T10:17:00.002+00:002019-03-23T10:18:30.798+00:00Configuring and Updating nested value objects in Entity FrameworkWow three years since the last post... not bad!<br />
<br />
For those of you who are curious I have finally left SharePoint and now I am sure we both are happier. But with a new platform come new issues.<br />
<br />
I am working now in a web application with .net core and entity framework and we have started using ValueObjects. All was fun and games until last wednesday when I decided to make one value object a child of another.<br />
<br />
We have User:
<br />
<pre class="c#" name="code">public class User: ValueObject
{
public string ActiveDirectorySID { get; private set; }
public string Email { get; private set; }
public string Name { get; private set; }
(...)
}</pre>
<br />
We have Position:
<br />
<pre class="c#" name="code">public class Position : ValueObject
{
public Guid PositionId { get; private set; }
public string JobTitle { get; private set; }
public User Employee { get; private set; }
(...)
}</pre>
<br />
And Finally we have CompanyPolicy:
<br />
<pre class="c#" name="code">public class CompanyPolicy : Entity
{
public string Title { get; private set; }
(...)
public Position Owner { get; private set; }
public Position Reviewer { get; private set; }
public Position Validator { get; private set; }
(...)
}</pre>
<br />
As you can see CompanyPolicy owns three Positions and each position owns an User. The first issue we need to deal with is the mappings of the CompanyPolicy object. After reading half of the internet the solution ended up being easy, the configuration code for the Positions inside a CompanyPolicy is:
<br />
<pre class="c#" name="code">public override void Configure(EntityTypeBuilder<CompanyPolicy> builder)
{
base.Configure(builder);
builder.Property(x => x.Title);
(...)
builder.OwnsOne(p => p.Owner, cb => { cb.OwnsOne(e => e.Employee); });
builder.OwnsOne(p => p.Reviewer, cb => { cb.OwnsOne(e => e.Employee); });
builder.OwnsOne(p => p.Validator, cb => { cb.OwnsOne(e => e.Employee); });
(...)
}</pre>
<br />
So far so good, we add the migration an it does not fail, we update the databases and it works... we add a value and all the fields get populated... We did it?! NOPE. The values never update. No matter what you do.<br />
<br />
We read the other half of the internet and found <a href="https://github.com/aspnet/EntityFrameworkCore/issues/9803">this thread</a> where they show a workaround and say this is already solved in the 3.0 preview, and as we don't want to wait and we don't want to install VS2019 we decided to go for the workaround.<br />
<br />
Based on it and after a couple of iterations we came up with this method:
<br />
<pre class="c#" name="code">public static void UpdateChildValueObjects<TEntity, TParent>(
this BaseAllensContext context,
TEntity rootEntity,
Expression<Func<TParent, ValueObject>> getChildValueObject,
params Expression<Func<TEntity,TParent>>[] getParentValueObjectArray)
where TEntity : class where TParent:class
{
var values = new List<ValueObject>();
//In the DetectChanges and when setting the values the rootEntity gets
//the values from the database. That's why we need to save them before!
for (int i = 0; i < getParentValueObjectArray.Length; i++)
{
values.Add(getChildValueObject.Compile()
.Invoke(getParentValueObjectArray[i].Compile().Invoke(rootEntity)));
}
context.ChangeTracker.DetectChanges();
for (var i = 0; i < getParentValueObjectArray.Length; i++)
{
context.Entry(rootEntity).Reference(getParentValueObjectArray[i])
.TargetEntry.Reference(getChildValueObject)
.CurrentValue = values[i];
}
context.Entry(rootEntity).State = EntityState.Modified;
}</pre>
<br />
Ugly anyone? Hard to read? It is not ugly, I like it. And it sits in your repository beautifully.
<br />
<br />
In the repository we have added this method:<br />
<br />
<pre class="c#" name="code">public void Update(CompanyPolicy policy)
{
Context.UpdateChildValueObjects(policy, x => x.Employee,
x => x.Owner, x => x.Validator, x => x.Reviewer);
}</pre>
<br />
<strike>This is what I wanted to show :)</strike><br />
So with this method in the repository you are telling that you want to update the Employee ValueObject in the Owner, Validator and Reviewer ValueObjects of the Entity policy.
And you might be thinking... That looks nice but, how do you use it?
<br />
Well this is the ugly part...<br />
<br />
First after we update the CompanyPolicy entity as we would normally do we need to make sure these value objects are updated so... I call the update first on the entity and then on the repository, like this:
<br />
<pre class="c#" name="code">var owner= companyPolicyDTO.Owner != null
? Mapper.Map<Position>(companyPolicyDTO.Owner)
: Position.Empty;
var reviewer = companyPolicyDTO.Reviewer != null
? Mapper.Map<Position>(companyPolicyDTO.Reviewer)
: Position.Empty;
var validator = companyPolicyDTO.Validator != null
? Mapper.Map<Position>(companyPolicyDTO.Validator)
: Position.Empty;
companyPolicy.Update(companyPolicyDTO.Title, owner, reviewer, validator);
companyPolicyRepository.Update(companyPolicy);</pre>
<br />
And this is it. First the standard update then our workaround.<br />
<br />
Known Issues:<br />
- The method UpdateChildValueObjects sets the values of the non updated fields of the entity to what they where in the database. This means you can't call this method twice in the same transaction because the second time it will not update the values!
<br />
<br />
<pre class="c#" name="code">public void Update(CompanyPolicy policy)
{
Context.UpdateChildValueObjects(policy, x => x.Employee,
x => x.Owner, x => x.Validator, x => x.Reviewer);
}</pre>
<span style="color: #38761d;">OK
</span><br />
<br />
<pre class="c#" name="code">public void Update(CompanyPolicy policy)
{
Context.UpdateChildValueObjects(policy, x => x.Employee,
x => x.Owner);
Context.UpdateChildValueObjects(policy, x => x.Employee,
x => x.Reviewer);
Context.UpdateChildValueObjects(policy, x => x.Employee,
x => x.Validator);
}</pre>
<span style="color: #cc0000;">Only the owner will be updated!</span><br />
<span style="color: #cc0000;"><span style="color: black;"><br /></span></span>
<span style="color: #cc0000;"><span style="color: black;">I hope this helps someone!</span></span>Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com1tag:blogger.com,1999:blog-5710617195189960819.post-26832961454286697512015-09-22T17:09:00.000+01:002015-09-22T17:20:47.795+01:00How To Clone a Virtual Machine in AzureThis is quite simple, once you know how to do it but... when you don't...<br />
<br />
My favourite way of cloning virtual machines has always been copying the hard disks and deploying the virtual machine in a different network segment. We do have all the ingredients we need for this recipe in Azure so let's get to it!<br />
<br />
You will need:<br />
<br />
<ul>
<li>The blob that contains the vhd you want to clone</li>
<li>A new Cloud Service (or one where the source virtual machine is not running)</li>
<li>Notepad.exe (or similar, although nothing beats the original)</li>
<li>Azure PowerShell</li>
<li>Attention to detail</li>
<li>Just a bit of patience</li>
</ul>
<div>
Let's start.</div>
<div>
<br /></div>
<div>
The first thing you need to do is to find out the disk(s) you want to clone. In the virtual machine's dashboard page, if you scroll down a bit, you will be able to see them.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk91oTxFoJ74Oc6BeKBstDLyPyepolT7YQgznTKXPdCvfdUEQ21h5Ld7FsFOayApMmK4ualpiql35qrhwV90r4QMtr8sRJjrjoW0R5AtdSUB_q50UA5GUe_XiD8kkHhs6uDHnw9frDVyDc/s1600/BlobUrl.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk91oTxFoJ74Oc6BeKBstDLyPyepolT7YQgznTKXPdCvfdUEQ21h5Ld7FsFOayApMmK4ualpiql35qrhwV90r4QMtr8sRJjrjoW0R5AtdSUB_q50UA5GUe_XiD8kkHhs6uDHnw9frDVyDc/s320/BlobUrl.png" width="320" /></a></div>
<div>
After you scroll down do not forget to scroll right too, the URL we need is a bit hidden... copy it in your notepad.</div>
<div>
<br /></div>
<div>
This is the address of the blob that contains the disk and in it you can find:</div>
<div>
<br /></div>
<div>
http://[<span style="color: red;">yourStorageAccount</span>].blob.core.windows.net/[<span style="color: orange;">yourContainerName</span>]/[<span style="color: lime;">yourBlobName</span>].vhd</div>
<div>
<br /></div>
<div>
Finally you need the primary access key for your storage account. </div>
<div>
<br /></div>
<div>
Go to Storage, select your Storage Account and look on the footer of the page for the key icon:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWMgLqqyOZmgDHeEUW2X303Nv5cethcgO3U6xKkdxxD5RxZ2FlpzgfBA7UQv7WXkPZzJ-CXo6ATODzihMVnuWA5wW9ZtOUo0x7f-m2O8iC1K-gbLu0UlEjpfCgbdjIRe57UkJjrZbdPB5U/s1600/ManageKeys.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWMgLqqyOZmgDHeEUW2X303Nv5cethcgO3U6xKkdxxD5RxZ2FlpzgfBA7UQv7WXkPZzJ-CXo6ATODzihMVnuWA5wW9ZtOUo0x7f-m2O8iC1K-gbLu0UlEjpfCgbdjIRe57UkJjrZbdPB5U/s1600/ManageKeys.PNG" /></a></div>
<div>
<br /></div>
<div>
There copy your primary key and paste it to your notepad.</div>
<div>
<br /></div>
<div>
And that's all you need.</div>
<div>
<br /></div>
<div>
The PowerShell code I use (that I copied mostly from <a href="http://michaelwasham.com/windows-azure-powershell-reference-guide/copying-vhds-blobs-between-storage-accounts/">http://michaelwasham.com/windows-azure-powershell-reference-guide/copying-vhds-blobs-between-storage-accounts/</a>) is this:<br />
<br /></div>
<pre class="html" name="code">#From http://michaelwasham.com/windows-azure-powershell-reference-guide/copying-vhds-blobs-between-storage-accounts/
#And after that from http://www.codegrimoire.com
####################################################################################################################
### Source VHD - anonymous access container ###
$storageUri = "http://yourStorageAccount.blob.core.windows.net/"
$containerName = "yourContainerName" ##I am using the same source and destination here
$sourceBlobName = "yourBlobame.vhd"
### Destination Blob
$destBlobName = "newBlobName.vhd"
######### New Disk Name
$newDiskName = "newDiskName"
$newDiskLabel = "BootDisk" ##In the documentation it is either BootDisk or DataDisk but you can call it something else
$isOsDisk = $true ##$true Or $false. As I only work with windows I will not bother about other OSs in this script
### Target Storage Account ###
$storageAccount = "yourStorageAccount"
$storageKey = "yourPrimaryKey" ##Primary Access Key
###################################################################################
############ Automated script for creating the new VHD ##########################
###################################################################################
$srcUri = ($storageUri.trim('/'), $containerName.trim('/'), $sourceBlobName) -join '/'
$destUri = ($storageUri.trim('/'), $containerName.trim('/'), $destBlobName) -join '/'
### Create the destination context for authenticating the copy
$destContext = New-AzureStorageContext –StorageAccountName $storageAccount -StorageAccountKey $storageKey
### Create the target container in storage
### Not necessary as i am using an existing one ### New-AzureStorageContainer -Name $containerName -Context $destContext
### Start the Asynchronous Copy ###
$blob1 = Start-AzureStorageBlobCopy -srcUri $srcUri -DestContainer $containerName -DestBlob $destBlobName -DestContext $destContext
### Loop until complete ###
Do{
$status = $blob1 | Get-AzureStorageBlobCopyState
### Print out status ###
$status.Status
Start-Sleep 5
}While($status.Status -eq "Pending") ##This doesn't work as you would expect but the idea is good and maybe they will change the way Get-AzureStorageBlobCopyState works :)
######## After the new blob has been created we will add the new disk #############
if ($isOsDisk){
Add-AzureDisk -DiskName $newDiskName -MediaLocation $destUri -Label $newDiskLabel -OS "Windows"
}
else{
Add-AzureDisk -DiskName $newDiskName -MediaLocation $destUri -Label $newDiskLabel
}</pre>
<div>
<br /></div>
<div>
Once you edit the script with your data and run it you will have to wait for a couple of minutes before the new disk is available. You do not need to turn off the source virtual machine <strike>although it's better safe than sorry</strike><br />
<br />
After that go to Virtual Machines, New, Compute, Virtual Machine, From Gallery and choose My disks in the lower part of the left column:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1CPlpK-lQxUntq9Q7xPlWunIvfJciWPD69NOuLk3Lvwq8cUEuVA78mLwHxdHMtswutI0X42k2TxNM2ZoF35NosqWHO-6wCUpzkZRZwvEfybLynCYyEs310kPQpC4R4SPVqsM3SiKf-Gho/s1600/MyDisks.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1CPlpK-lQxUntq9Q7xPlWunIvfJciWPD69NOuLk3Lvwq8cUEuVA78mLwHxdHMtswutI0X42k2TxNM2ZoF35NosqWHO-6wCUpzkZRZwvEfybLynCYyEs310kPQpC4R4SPVqsM3SiKf-Gho/s320/MyDisks.png" width="320" /></a></div>
<br />
The disk you have just created will appear in the list and after that you just need to create the new virtual machine normally.<br />
<br />
By the way, we need an Azure + SharePoint administrator in London, do you oblige?<br />
<br />
<br />
<div style="text-align: right;">
<span style="color: #cccccc;">Is it a good idea to call the cloned VMs Dolly?</span></div>
</div>Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-84791625733665335872015-08-25T10:06:00.000+01:002015-08-25T10:06:21.208+01:00Windows 10 after the first monthActually I had W10 installed in a preview back on January <strike>and only because I wanted to be careful not installing the first insider preview</strike> but then I found some very annoying bugs that made me uninstall it and go back to W8.1. Finally <strike>and it looks like I mean Literally Finally as they say this is the last version of Windows ever</strike> I reinstalled The Real W10 on the 29th... And realised <strike>much more acquiescent than</strike> surprised that the bugs that made me uninstall it were already there.<br />
<br />
It's pretty obvious that my bugs are due to my particular set up and they are nothing you need to worry about. <strike>Who would have two monitors daisy chained to a Surface 3 Pro and a HiFi system hooked to them? If you have that type of set up you probably deserve loosing the image in your monitors and waking up every morning without sound.</strike><br />
<br />
I already know how to work around these issues. If your monitors stop working you need to unplug them off from the wall (turning them off or disconnecting the DP cable will not do, you need to pull the plug off the wall) and back on again. That plus a reset fixes it. About the sound it's either changing the quality of the sound or restarting the machine but enough of my old man whining already.<br />
<br />
I have been working with Windows 10 in my everyday computer for three or four months and the single thing that makes me love the system every day is the fact that is Desktop-Centrinc <strike>this is my innovative use of the English language again</strike>.<br />
<br />
Windows 8 was based in apps and the operative system wanted to push you to use the apps first... Even the desktop was an app... <strike>The desktop... AN APP!? the more I think about it the more outrageous it feels</strike>. It kind of makes sense if you have a tablet but 99% of people who works with a computer does it sitting on their offices with two 24" monitors. <strike>The desktop an app ... that's nonsense.. an app...</strike>.<br />
<br />
Windows 10 runs from the desktop (in desktop computers) and that small change makes everything make sense again.<br />
<br />
Your computer starts with your familiar desktop and there you have all your familiar applications you feel like home. Suddenly you need to download something... you can either use your familiar browser to go to the wilderness that it is internet and download a random piece of software from a random server that will have access to most of the resources in your computer without you noticing after installing a bar in the browser or you can go to the now natural place to download software: The Store.<br />
<br />
The apps from the store work now as windows in the desktop. You can maximise them as you always could, you can stick them to a side of the screen as you always could and you can use it in all the usual ways with all the freedom you are used to. That is fantastic. That makes the boundary between a mobile device like a phone or a tablet and your desktop disappear. You can use the same app everywhere but if you are in your desktop you will be able to run it right besides your beloved Winamp <strike>(Why Winamp? because you always dreamed secretly about whipping a llama's ass)</strike>.<br />
<br />
I do not consider myself a common computer user, in fact the vast majority of the days I use just the remote desktop and maybe the browser for reading the news if I'm in that mood but since I have W10 I have downloaded a couple of apps that I use sometimes and the more apps you download and use the more natural it feels.<br />
<br />
With Windows 10 the ability to run the apps in Windows phones is not be the driver to migrating programs to apps any more. <strike>Windows programs are so from the 80's...</strike><br />
<br />
If the integration between apps and EXEs is so seamless and apps are the coolest thing around, Why have not all the companies migrated all the code from the last 10 years to apps already? After all it has been a month since they released Windows 10...<br />
<br />
Because it took them 10 years to create that code in the first place.<br />
<br />
I'm not saying that migrating an app would take the same amount of time than creating it from scratch but it's still an investment. It will mean migrating, at least, the whole user experience and, How often do programs change their user interface?<br />
<br />
<br />
I suspect the risk adverse software companies (or the bigger ones that take longer to react) will wait until they need an interface change to adopt the new paradigm while the new software companies and the more dynamic ones will embrace the apps world as soon as they release a new version.<br />
<br />
What do you think?Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-14537951742543697772015-07-29T09:29:00.001+01:002015-07-29T09:29:36.006+01:00Where is my Windows 10 Upgrade?That's obviously the first thing I asked myself today :)<br />
<br />
It's in your desktop windows update!<br />
<br />
Right click on the Start button and open the Control Panel.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzA2VkBuqfZgP356jZIc1vJiP8nzoaPU5D3Yi5U8PvDrzbIHpSqM8EuzbWRplv5y355R2wTU3QIC5lzTAiRmT5tdeMNmjXvnLz0gvu4VIBXxnOoh4OFm1ESzal_25Or_0J6ddnqVw2buLe/s1600/OpenControlPanel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzA2VkBuqfZgP356jZIc1vJiP8nzoaPU5D3Yi5U8PvDrzbIHpSqM8EuzbWRplv5y355R2wTU3QIC5lzTAiRmT5tdeMNmjXvnLz0gvu4VIBXxnOoh4OFm1ESzal_25Or_0J6ddnqVw2buLe/s320/OpenControlPanel.png" width="213" /></a></div>
<span id="goog_635239708"></span><span id="goog_635239709"></span><br />
Once there look for Update<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkFBcb9ipZXECrHG8qR9hSaX077lM6c5qpfVtGLF2jLe89eOiAs6W2JZb0uEsjjmb_1NBTBvYfe3GLqY-7y3aPDOFEueGy7lpi_OPk_Pq9sUHd450Isddfi265lXKo2rAiFK05WKBAc73N/s1600/SearchForUpdate.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="103" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkFBcb9ipZXECrHG8qR9hSaX077lM6c5qpfVtGLF2jLe89eOiAs6W2JZb0uEsjjmb_1NBTBvYfe3GLqY-7y3aPDOFEueGy7lpi_OPk_Pq9sUHd450Isddfi265lXKo2rAiFK05WKBAc73N/s320/SearchForUpdate.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Finally click on Windows Update and...</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4zlc5J6Fe3O1RiNfGHRWKxkYctYs9Sj4DVcSfB-PMEB20A39ZekrUWjV3pCvYB4wxWcxBbhmmk6OuSLuncelPDZdzvOoIje3CIdQJxcGBHk2x6U_QVP-tscSwx6s3atuJB7CrFN6NNwAd/s1600/Windows10Update.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4zlc5J6Fe3O1RiNfGHRWKxkYctYs9Sj4DVcSfB-PMEB20A39ZekrUWjV3pCvYB4wxWcxBbhmmk6OuSLuncelPDZdzvOoIje3CIdQJxcGBHk2x6U_QVP-tscSwx6s3atuJB7CrFN6NNwAd/s320/Windows10Update.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
I am So Excited! <strike>Yes, I am that kind of person</strike></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Well... almost 3GB... I can't wait... I'll see you in a while... I can't wait!</div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-85154260316220405422015-07-09T12:29:00.002+01:002015-07-09T13:17:45.467+01:00Is JavaScript The Emperor's New Clothes?<a class="mw-redirect" href="https://en.wikipedia.org/wiki/Script_(computing)" style="background-attachment: initial; background-clip: initial; background-image: none; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0b0080; text-decoration: none;" title="Script (computing)">Script (computing)</a>, a <span style="color: red;"><b><u><i>small</i></u></b></span> <b>non-compiled</b> program written for a scripting language or command interpreter.<br />
<br />
If JavaScript has Script in its name, what made you think it would be a suitable language to base the whole web and half of the mobile software on it?<br />
<br />
If <a href="http://www.amazon.com/dp/0596517742" target="_blank">JavaScript: The good parts</a> is a best seller and it's only a 150 pages manual (50 are appendixes and only 2 are about beautiful features <strike>and I am pretty sure even Mr Crockford found it excruciating to write so many</strike>)<br />
<br />
If you are required to migrate from one version to another every now and then because the software provider will only support your bits for the next five years, What makes you think it's a good idea to download and install in the core of your software a random minified js file that you can't read and you don't know where it comes from and, of course, was forgotten by its developer five days after he published it?<br />
<br />
Will I work with it?<br />
<br />
Of course I will, in fact I have been doing it for ages, just not at this level. If you can do anything with a <a href="http://morphett.info/turing/turing.html" target="_blank">Turing machine</a> why not embracing a script language for making applications of thousands of lines? <strike>That's what the brave would call a challenge.</strike><br />
<br />
I have been creating classes and constructors and inheritance and it all can be done in JS but I am pretty sure translating ancient OO structures and patterns to JS is not the right way, probably that's my problem. Instead of embracing the new language I am trying to translate my old jokes. That will probably change with the time as I learn the new ones.<br />
<br />
The fact that we have no other option plays a role here too. If you are working for the web you are free to use JavaScript or not to work for the web <strike>à la Apple</strike>.<br />
<br />
Do I understand its advantages?<br />
<br />
The learning curve is great as the basics are simpler. No types <strike>no safety net</strike>. No need of an IDE <strike>no great IDEs either, if you are used to Visual Studio you'll love loosing all the features you are used to</strike>. It can be executed everywhere <strike>if they have the right browser <b>and that's not the case in many big companies</b></strike>. And a great community of fans <strike>à la Apple</strike>.<br />
<br />
Do I like JavaScript?<br />
<br />
Do I like a piece of technology that, if we are lucky, will solve 5 years in the future all the problems we solved with Silverlight eight years ago?<br />
<br />
<strike>I probably will when both me and the technology are mature enough.</strike><br />
<br />
Of course I do, I want to be cool...<br />
<br />
<br />
<div style="text-align: right;">
<span style="color: #cccccc;"><strike>I learnt to love WPF but it didn't take this long.</strike></span></div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-7564676835116256122015-06-11T17:51:00.002+01:002015-06-11T17:57:24.617+01:00Migrating SharePoint Users to a New Domain<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: left;">
<strike>I have been dreading this type of migration for years... so many years that I already had planned a couple of ways of solving the issue. It finally happened.</strike></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Scenario:<br />
<br />
Someone decides we need to change the farm from one environment to a new one completely different with a new AD and in a new city.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyFLnx9SyzF5PIAoXohm2gIiOmNRJ0up2ZikDaOU5lqjJt9jzieTHCGfzidYx_pRSLMUQNUksk4BaMN9GuymutBtCGm2IlGssO9ICPqJjQqSHsOfdXRtp7OMdlTzIoOobTxDuAZKf4vNO2/s1600/Migration.png" imageanchor="1" style="clear: right; display: inline !important; float: right; margin-bottom: 1em; margin-left: 1em; text-align: center;"><img border="0" height="100" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyFLnx9SyzF5PIAoXohm2gIiOmNRJ0up2ZikDaOU5lqjJt9jzieTHCGfzidYx_pRSLMUQNUksk4BaMN9GuymutBtCGm2IlGssO9ICPqJjQqSHsOfdXRtp7OMdlTzIoOobTxDuAZKf4vNO2/s200/Migration.png" width="200" /></a>Well, let's get to it. We have created a new SharePoint farm in the new environment and we have backed up and restored the content databases. We have manually changed the admin of the site collection to the new admin in the new AD in the new farm and we can access the site and see the data. Fantastic.<br />
<br />
Fantastic?</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
The users in the list items are the users from the old farm. And we have several lists with a lot of user fields. And some of our lists have tens or hundreds of thousands of rows. Changing them manually is not an option.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<span style="color: blue;">First idea: Go refined and try <b><a href="https://technet.microsoft.com/en-us/library/cc262141(v=office.12).aspx" target="_blank">stsadm -o migrateuser</a></b>:</span></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Ohh so easy... we change the login name of the user to something else and we are good because the user IDs are still the same... NO.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
This is a new domain and we don't have access to the old domain users so the migrateuser parameter throws a nice User not found error.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<span style="color: blue;">Second idea: Go berserk and change the strings in the list items</span></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
And that worked. Oh the beauty of a simple idea. The process is pure brute force... beautiful in its barbarity... If you have read up to here you are probably desperate for a solution.<br />
<br />
<span style="color: red;">Step One:</span><br />
Get all of the users from the old farm in an XML file or something really high tech (a csv could work too).<br />
<br />
<pre class="c#" name="code">static void Main(string[] args)
{
using (SPSite site = new SPSite(args[0]))
{
using (SPWeb web = site.OpenWeb())
{
XElement users = new XElement("Users");
foreach (SPUser user in web.SiteUsers)
{
XElement xmlUser = new XElement("User");
xmlUser.Add(new XAttribute("Name", user.Name));
xmlUser.Add(new XAttribute("LoginName", user.LoginName));
xmlUser.Add(new XAttribute("ID", user.ID));
users.Add(xmlUser);
}
users.Save("SiteUsers.xml");
}
}
Console.WriteLine("\nProcess finished...");
Console.ReadLine();
}</pre>
<br />
<br />
<span style="color: red;">Step Two:</span><br />
Make sure all the users you need are in the new AD. As you have a list in XML you can pass it to someone with privileges in the AD.<br />
<br />
<span style="color: red;">Step Three:</span><br />
Ensure the users in SharePoint, add them to a group with reading permissions and then iterate through all the items in the list changing the users from the old domain to the users in the new one.<br />
<br /></div>
<pre class="c#" name="code">static void Main(string[] args)
{
XElement users = XElement.Load("SiteUsers.xml");
string newDomain = "XXXXXXXX";
string ListName = string.Empty;
if (args.Length == 2) ListName = "Stratex Framework";
else ListName = args[2];
//Args are SiteUrl VisitorsGroup ListName
using (SPSite site = new SPSite(args[0]))
{
using (SPWeb web = site.OpenWeb())
{
SPList listToUpdate = web.Lists[ListName];
Dictionary<string, SPUser> NewUsers = new Dictionary<string, SPUser>();
foreach (XElement user in users.Descendants("User"))
{
string LoginName = FixDomain(user.Attribute("LoginName").Value, newDomain);
SPUser spUser = null;
try
{
//We try to ensure all the users from the XML file. We'll probably need them
spUser = web.EnsureUser(LoginName);
}
catch
{ Logger.WriteLine("The user {0} could not be found.", LoginName); }
if (spUser != null)
{
SPGroup viewers = web.Groups[args[1]];
viewers.AddUser(spUser);
//Finally we add them to a group with read permissions
//We can worry about restricting this further after the migration
if (!NewUsers.ContainsKey(LoginName)) NewUsers.Add(user.Attribute("ID").Value, spUser);
}
}
web.Update();
UpdateUsersInList(listToUpdate, NewUsers);
}
}
Logger.WriteLine("\nProcess finished...");
Console.ReadLine();
}
private static void UpdateUsersInList(SPList list, Dictionary<string, SPUser> NewUsers)
{
int itemsInList = list.ItemCount;
Logger.WriteLine("Updating users at {0}. {1} items.", list.Title, itemsInList.ToString());
SPQuery qry = new SPQuery();
qry.ViewAttributes = "Scope=\"RecursiveAll\"";
SPListItemCollection allItems = list.GetItems(qry);
int count = 0;
UpdateCount(count++, itemsInList);
foreach (SPListItem item in allItems)
{
try
{
bool changed = false;
SPFieldCollection allFields;
//If the item has content type it has usualy less fields
if (item.ContentType == null)
allFields = item.Fields;
else
allFields = item.ContentType.Fields;
foreach (SPField field in allFields)
{
if (field is SPFieldUser)
changed = ChangeUserToNewDomain(item, field, NewUsers) || changed;
}
changed = ChangeUserToNewDomain(item, item.Fields.GetFieldByInternalName("Author"), NewUsers) || changed;
changed = ChangeUserToNewDomain(item, item.Fields.GetFieldByInternalName("Editor"), NewUsers) || changed;
if (changed) item.SystemUpdate(false); //if the item has not been changed we won't update it to save time
}
catch (Exception ex) { Logger.WriteLine("Failed to update item {0}. Exception {1}", item.Title, ex.Message); }
UpdateCount(count++, itemsInList);
}
UpdateCount(count++, 0);
}
private static bool ChangeUserToNewDomain(SPListItem item, SPField field, Dictionary<string, SPUser> NewUsers)
{
bool changed = false;
string fieldContent = item[field.InternalName] == null ? null : item[field.InternalName].ToString();
if (string.IsNullOrEmpty(fieldContent)) return false;
List<string> oldUserIds = GetUserIDs(fieldContent.Split(new string[] { ";#" }, StringSplitOptions.RemoveEmptyEntries));
if (oldUserIds.Count == 1)
{ //The field has only one user in it
SPFieldUserValue foundUser = FindUser(NewUsers, oldUserIds[0]);
if (foundUser != null)
{
item[field.InternalName] = foundUser;
changed = true;
}
}
else if (oldUserIds.Count > 1)
{ //The field has several users in it
SPFieldUserValueCollection usersInField = new SPFieldUserValueCollection();
foreach (string oldUser in oldUserIds)
{
SPFieldUserValue foundUser = FindUser(NewUsers, oldUser);
if (foundUser != null)
usersInField.Add(foundUser);
}
if (usersInField.Count > 0)
{
item[field.InternalName] = usersInField;
changed = true;
}
}
return changed;
}
private static List<string> GetUserIDs(string[] UserTokens)
{ //We do not care about the login name. The ID is gold
List<string> result = new List<string>();
if (UserTokens.Length > 0)
{
for (int i = 0; i < UserTokens.Length; i++)
{
int id;
if (i % 2 == 0 && int.TryParse(UserTokens[i], out id))
result.Add(id.ToString());
}
}
return result;
}
private static SPFieldUserValue FindUser(Dictionary<string, SPUser> NewUsers, string oldUser)
{
SPUser foundUser = null;
if (NewUsers.ContainsKey(oldUser)) foundUser = NewUsers[oldUser];
else
{
//If we can't find the ID of the user we will still try with the login or even with the Display Name
foreach (SPUser newUser in NewUsers.Values)
{
if (newUser.Name == oldUser || newUser.LoginName == oldUser) { foundUser = newUser; break; }
}
}
if (foundUser != null)
return new SPFieldUserValue(foundUser.ParentWeb, foundUser.ID, foundUser.Name);
else
return null;
}
private static string FixDomain(string loginName, string newDomain)
{
//Here we change the users from XXXXX\\User to YYYYY\\User
//The source domain was claim based
if (loginName.Contains("|")) loginName = loginName.Split('|')[1];
string[] tokens = loginName.Split('\\');
tokens[0] = newDomain;
return string.Join("\\", tokens);
}
private static void UpdateCount(int currentItem, int itemsInList)
{
int percentage;
if (currentItem == 0) percentage = 0;
else if (itemsInList == 0) percentage = 100;
else
{
//We will only change the value every 10 times to make the process faster.
if (currentItem % 10 != 0) return;
percentage = currentItem * 100 / itemsInList;
}
Console.Write("\r");
if (percentage >= 0 && percentage < 10)
Console.Write(" ");
else if (percentage >= 10 && percentage < 100)
Console.Write(" ");
Console.Write("{0}%", percentage);
}</pre>
<div style="text-align: left;">
<div style="text-align: center;">
<br /></div>
This is a first prototype that has worked as expected but it's not fully tested (by far) if you need it you can use it as a base to develop your own tool.<br />
<br />
<div style="text-align: right;">
<span style="color: #cccccc;">The one who possesses the strings has the power.</span></div>
</div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-60257443465964100502015-06-10T11:48:00.000+01:002015-06-10T11:48:44.758+01:00Why Windows 10 will be a game changer?<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoQuIR-Qes9cF9_zG36IzOSCrT2fyjQ5_NVamJTjCrGOm6kJT8dho90ms3-AdCRySiCZ2YbAUkTNp9UUL53v3p965hIFwteegQ_lT0gfJgFMeKXgQO1bx2O5ltmOHUeWRvsewEHa_ZChxY/s1600/Windows10.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoQuIR-Qes9cF9_zG36IzOSCrT2fyjQ5_NVamJTjCrGOm6kJT8dho90ms3-AdCRySiCZ2YbAUkTNp9UUL53v3p965hIFwteegQ_lT0gfJgFMeKXgQO1bx2O5ltmOHUeWRvsewEHa_ZChxY/s1600/Windows10.png" /></a></div>
Do you remember having to meet at your friend's house to ask him to lend you his <a href="https://www.youtube.com/watch?v=lRR7N363wPc" target="_blank">Zanac</a> cassette tape?<br />
<br />
Do you remember buying a double cassette player to be able to copy the game <strike>just for testing purposes</strike>?<br />
<br />
The floppy disks arrived then and we had those huge bendy disks of 5 1/4 they were really fast and their capacity was amazing but they didn't change the fact that you needed to go to your friend's house to get a copy of <a href="https://www.youtube.com/watch?v=GdKfG6VKLt0" target="_blank">Alley Cat</a>.<br />
<br />
Then the internet arrived. And you still had to go to your friend's house to get the diskette if <a href="https://www.youtube.com/watch?v=Ax-WFCPCgWg" target="_blank">Ishar</a> because it took much less time to get to the other side of the planet walking than downloading 1MB.<br />
<br />
But the internet was a game changer. After some time you completely forgot about 3,5 diskettes and you would only visit your friend to get the CD of <a href="https://www.youtube.com/watch?v=AnR1n5e1Amc" target="_blank">Dungeon Keeper II</a> or something like that.<br />
<br />
And some time later (today) you can download 60GB virtual machines in minutes. <strike>It looks like the moment proper internet arrived I stopped enjoying computers...</strike><br />
<br />
Windows 10 is similar when it comes to changing the paradigm of the distribution of the software and I will explain my point.<br />
<br />
<br />
Most of the programs and games for desktops were created for Windows and not for Linux or Mac and there's a good reason for that. The market share of the windows desktop made working for any other platform a waste of money. <br />
<br />
The the market places arrived. And instead of going out to the scary internet to download you programs you would go to a supervised environment where you could download your programs safely.<br />
<br />
BUT<br />
<br />
When it comes to desktops only a "ridiculous" 16.45% of the computers (W8 + 8.1) can execute market applications while the vast majority of desktops (W7 + XP) a 72.36% are still stuck in the old paradigm. (<a href="http://www.netmarketshare.com/operating-system-market-share.aspx?qprid=10&qpcustomd=0" target="_blank">source</a>)<br />
<br />
And the question comes again... Why develop an application for the 16% of the computers when, for the same amount of money I can create the same app for the 88.81% (given that W8 and 8.1 are compatible with the old desktop code)? It's a no brainer.<br />
<br />
And here comes Windows 10.<br />
<br />
Offering Windows 10 as a free upgrade will surely convince most of the users out there with W7, 8 and 8.1 to get the latest bits and with them the ability to be clients of this new market.<br />
<br />
Not only that. Given that Windows 10 apps will also work in any hardware capable of executing W10 the target audience will be not only increased but probably multiplied.<br />
<br />
You will develop an app once and it will be downloadable by the desktop users, but also by the Windows Phone users, the XBox users, the Raspberry Pi users... the Hololens users! you name it.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1WcAPLy7U1WIC-SB2wgFy1NLqB-ShlMa76xl0_8ZF6DgiOnG6DEokHoJo2_ekTxfwwbJs-Jz7xHhbXaYvwV74oF-MHmHrJv61nD3GhJYfBgAGv3Ukf6DBpPWw4QIEKh1VZI3UGQfSlwY0/s1600/W10Everywhere.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1WcAPLy7U1WIC-SB2wgFy1NLqB-ShlMa76xl0_8ZF6DgiOnG6DEokHoJo2_ekTxfwwbJs-Jz7xHhbXaYvwV74oF-MHmHrJv61nD3GhJYfBgAGv3Ukf6DBpPWw4QIEKh1VZI3UGQfSlwY0/s320/W10Everywhere.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The estimations of Microsoft about this is that in a couple of years there will be <a href="http://techcrunch.com/2015/04/29/microsoft-expects-1-billion-windows-10-devices-in-2-3-years/" target="_blank">one billion W10 devices out there</a> and then the question will be again:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<b><span style="font-size: large;">Why would anyone develop for any other platform?</span></b></div>
<br />Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-18542835084445182462015-06-04T11:25:00.000+01:002015-06-04T11:25:43.263+01:00Geeky T-Shirt For Free... Count me on Xamarin!<div class="separator" style="clear: both; text-align: center;">
<a href="http://xamarin.com/c-sharp-shirt" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://xamarin.com/content/images/pages/sharp-shirt/sharp-shirt-web-FINAL.png" height="190" width="200" /></a></div>
I firmly believe that once the majority of the desktop computers of the world are capable of running Microsoft Store applications (Windows 10 will be released in July 29) the natural way for developing anything will be using the Microsoft Store <strike>I usually get excited whenever Microsoft says anything, excited or angry, but mostly excited because I am quite a naïve guy</strike>.<br />
<br />
But even though, in the same way some android and IOS developers port their applications to the Microsoft Store, we might need to port our applications to other platforms, <strike>the other markets will be ridiculously small, but still</strike>.<br />
<br />
<br />
I have been waiting for the right opportunity to give <a href="http://xamarin.com/" target="_blank">Xamarin</a> a go and now, <strike>for a free t-shirt</strike> that we are exploring new platforms for a new tablet project, seems like the right moment.<br />
<br />
The installation process in my Surface 3 Pro has not been easy. I struggled to make it run in VS2015 and started using the Xamarin Studio. I had one small issue with XS, nothing that 30 second in the Xamarin forums could not fix. All looked good, but then when I tried starting the application in the virtual devices I couldn't. Using real hardware was not an option because I only have Windows Phones at home <strike>6.1 to 10</strike>.<br />
<br />
I found the solution getting and installing the <a href="http://developer.xamarin.com/guides/android/getting_started/installation/android-player/" target="_blank">Xamarin Player</a>, after that <strike>which also installed VBox</strike>, I downloaded and configured a Nexus 4 VM.<br />
<br />
In the last step after you select which t-shirt you want you need to go to the code and do one small change. Even though the change was small it made me feel more inclined to hack through the code and modify it to do something else.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEib-7AhiL_8DgmjT4gyS4bBdgBqP3YYXgkrBHuqSIRy8xWnLofzw1xR3RbVHuwvc_lwQrXW9TDmxDspR4H-uHiKD5oRFqbZyryPdwYhgSt4J4SLpcd3V4pDE4Ys8gksjG3s_wUSje8m2jkQ/s1600/Xamarin.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEib-7AhiL_8DgmjT4gyS4bBdgBqP3YYXgkrBHuqSIRy8xWnLofzw1xR3RbVHuwvc_lwQrXW9TDmxDspR4H-uHiKD5oRFqbZyryPdwYhgSt4J4SLpcd3V4pDE4Ys8gksjG3s_wUSje8m2jkQ/s320/Xamarin.PNG" width="212" /></a></div>
<br />
<br />
I can't wait to get the t-shirt and be one of the LINQ <strike>do the process if you want to know what i mean here</strike>.<br />
<br />
Oh and the app has a lot of code that you will probably reuse too... another gift!<br />
<br />
<br />
<br />
<div style="text-align: right;">
<span style="color: #999999; font-size: xx-small;"><a href="https://www.youtube.com/watch?v=7PCkvCPvDXk" style="background-color: white;" target="_blank">Because you know I'm all about Win10 about Win10 no Android</a></span><span id="goog_1774957606"></span><a href="https://www.blogger.com/"></a><span id="goog_1774957607"></span></div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-28130922164409296022015-02-11T10:01:00.000+00:002015-02-11T12:42:47.414+00:00No more recursive functions to define CAML Queries thanks to CamlexSome times you have a random number of conditions to check in a CAML query and in those cases I used to define the queries using a random recursive function that <strike>I usually have to debug a few times</strike> works perfectly on the first go.<br />
<br />
The code for those queries would be something like this (and this is a simple one):<br />
<br />
<pre class="c#" name="code">public List<string> GetSomeInfo(string fieldsToSearch, string contentTypesToSearch)
{
...
var queryval = string.Empty;
if (contentTypesToSearch.IsNullOrEmpty())
queryval = string.Format("<Where>" + GenerateFieldsQuery(fieldsToSearch.Split(','), 0) + "</Where>", text);
else
queryval = string.Format("<Where><And>" + GenerateCTypesQuery(contentTypesToSearch.Split(','), 0) + GenerateFieldsQuery(fieldsToSearch.Split(','), 0) + "</And></Where>", text);
var scope = "Scope=\"RecursiveAll\"";
...
}
private static string GenerateFieldsQuery(string[] fields, int index)
{
if (fields.Length == 0) return string.Empty;
if (fields.Length == index + 1)
return "<Contains><FieldRef Name='" + fields[index] + "' /><Value Type='Text'>{0}</Value></Contains>";
return "<Or><Contains><FieldRef Name='" + fields[index] + "' /><Value Type='Text'>{0}</Value></Contains>" + GenerateFieldsQuery(fields, ++index) + "</Or>";
}
private static string GenerateCTypesQuery(string[] cTypes, int index)
{
if (cTypes.Length == 0) return string.Empty;
if (cTypes.Length == index + 1)
return "<Eq><FieldRef Name='ContentType' /><Value Type='Choice'>" + cTypes[index] + "</Value></Eq>";
return "<Or><Eq><FieldRef Name='ContentType' /><Value Type='Choice'>" + cTypes[index] + "</Value></Eq>" + GenerateCTypesQuery(cTypes, ++index) + "</Or>";
}
</pre>
<br />
That was until now... Thanks to <a href="https://camlex.codeplex.com/" target="_blank">Camlex</a> (and thanks to Luis for showing it to me), that code can be written like this:
<br />
<br />
<pre class="c#" name="code">public List<string> GetSomeInfo(string fieldsToSearch, string contentTypesToSearch)
{
...
var queryVal = string.Empty;
var fieldExtensions = new List<Expression<Func<SPListItem, bool>>>();
var cTypeExtensions = new List<Expression<Func<SPListItem, bool>>>();
if (!contentTypesToSearch.IsNullOrEmpty())
{
foreach (var cType in contentTypesToSearch.Split(','))
cTypeExtensions.Add(x => (string)x["ContentType"] == cType);
}
foreach (var field in fieldsToSearch.Split(','))
fieldExtensions.Add(x => ((string)x[field]).Contains(text));
var expressions = new List<Expression<Func<SPListItem, bool>>>();
expressions.Add(ExpressionsHelper.CombineOr(cTypeExtensions));
expressions.Add(ExpressionsHelper.CombineOr(fieldExtensions));
queryVal = Camlex.Query().WhereAll(expressions).ToString();
...
}
</pre>
<br />
<div style="text-align: right;">
<span style="color: #cccccc;">I'll miss the recursive methods though... they made me feel special...</span></div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-37767376120831660592015-01-24T11:26:00.002+00:002015-01-24T11:26:36.390+00:00Get Cortana in Windows 10 Desktop Outside the USOne of the features I wanted to test the most on the new release of Windows 10 was Cortana but living outside the US it was disabled :(<br />
<br />
<strike>Fear not,</strike> it will take seconds to have the system configured.<br />
<br />
Look for Region and Language Settings <strike>in the search bar where Cortana should be</strike> and open it.<br />
<br />
There change your location to United States and then click on Add a language.
Of course the language you need to add is English (United States) then make English (United States) your primary language (you don't need to remove your other languages or keyboards).<br />
<br />
Once your screen look like this:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaLR67wyWnHDa-Ed92DulsgZ38J4jPyyiYaJpOGorZumd_1U-wVH8cf0pblrrcG5N5EGCQGv7h69vYTcMt51sNhGF5A5uVxeE97tPegJrsq_LTDdZbd-ed9LWEUCYBABhQmF6I7JQFN8F_/s1600/Region+And+Language+Settings.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaLR67wyWnHDa-Ed92DulsgZ38J4jPyyiYaJpOGorZumd_1U-wVH8cf0pblrrcG5N5EGCQGv7h69vYTcMt51sNhGF5A5uVxeE97tPegJrsq_LTDdZbd-ed9LWEUCYBABhQmF6I7JQFN8F_/s320/Region+And+Language+Settings.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Reset the machine and you'll have Cortana waiting for you there.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicafSfd2U2cj5fC-n2JYqx3yQv-Osgq1jqg3Io3M7opP85bQawZB4XUVqG4A822lZUk6YBGmZT5QgK5sJZ7D4nUsc9RMM8SJ9mqQTxaYHIAuMakV_CMzHAH5sNwu_6HHdA7yj7zt1WjP_l/s1600/Search+bar+with+Cortana.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicafSfd2U2cj5fC-n2JYqx3yQv-Osgq1jqg3Io3M7opP85bQawZB4XUVqG4A822lZUk6YBGmZT5QgK5sJZ7D4nUsc9RMM8SJ9mqQTxaYHIAuMakV_CMzHAH5sNwu_6HHdA7yj7zt1WjP_l/s320/Search+bar+with+Cortana.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
If you are anything like me you would be yelling "Hey Cortana" to the computer closer and closer to the mic. Don't. Before it works you need to configure it.
Click on the search bar, look for the hamburguer icon and click on Settings:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjio_DKHj0t0ru_mxvahmbK_piUpwDrB3au8korWJmNkr0NENun5W43HhhwYobk0Xal0eQwdZheXwdgMU7uF2_Q6q5pSMYy8EKJWnwqper76snj0XaPcluE1eJ0xrpksDCbQJKGx1YWvz3A/s1600/Search+Bar+Settings.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjio_DKHj0t0ru_mxvahmbK_piUpwDrB3au8korWJmNkr0NENun5W43HhhwYobk0Xal0eQwdZheXwdgMU7uF2_Q6q5pSMYy8EKJWnwqper76snj0XaPcluE1eJ0xrpksDCbQJKGx1YWvz3A/s320/Search+Bar+Settings.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
And finally enable everything :)
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPzmwwMvUg9So4dVSkQQ4rdXNanyOWx6prZRANezn5YOAIDoGZISBwrFV1S8N2S2KsujK8TpRakgPzSyvffp_pvbwomn0DSIuelbnv2ZbBc_LfilHHNpdRcMvOHrqGkYEfLbC2E6EF2lck/s1600/Cortana+Settings.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPzmwwMvUg9So4dVSkQQ4rdXNanyOWx6prZRANezn5YOAIDoGZISBwrFV1S8N2S2KsujK8TpRakgPzSyvffp_pvbwomn0DSIuelbnv2ZbBc_LfilHHNpdRcMvOHrqGkYEfLbC2E6EF2lck/s320/Cortana+Settings.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
And now say: "Hey Cortana, How are you doing?"
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtu7VlLeJSlF178K_HKCnOm6918sFGqCkEq7HtW2TCJ5YCO15p-vbHyPPo25qjJh54LZxShN1-Wmkp4cXLoDIO95hVX_4E4nkeOBox5N4XP1eF003Vz9hIdjoSn2uThSWOtHYmxIsTWbDm/s1600/Cortana+quite+well.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtu7VlLeJSlF178K_HKCnOm6918sFGqCkEq7HtW2TCJ5YCO15p-vbHyPPo25qjJh54LZxShN1-Wmkp4cXLoDIO95hVX_4E4nkeOBox5N4XP1eF003Vz9hIdjoSn2uThSWOtHYmxIsTWbDm/s320/Cortana+quite+well.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
She's so polite...<br />
<br />
<div style="text-align: right;">
<span style="color: #cccccc; font-size: x-small;">I just need to ask her why the accent colour of my Windows is suddenly brown U_U</span></div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-34837719205974297932015-01-15T18:50:00.000+00:002015-01-15T19:12:43.968+00:00Win RT Universal app with a DocumentDB<div style="margin: 0in;">
<span style="font-family: Calibri;"><span style="font-size: 15px;">NoSql databases have been brought to my attention a couple of <strike>thousand </strike>times in the last months and given that I am not the tidiest of the database designers and also that NoSql databases are supposed to be designed to escale I have decided to give them a go.</span></span><br />
<span style="font-family: Calibri;"><span style="font-size: 15px;"><br /></span></span>
<span style="font-family: Calibri;"><span style="font-size: 15px;">My platform of choice is Microsoft and looks like we have all the tools we need: Windows Apps that work the same desktops and phones and DocumentDB in Azure, fantastic, let's begin.</span></span><br />
<span style="font-family: Calibri;"><span style="font-size: 15px;"><br /></span></span>
<span style="font-family: Calibri;"><span style="font-size: 15px;">Let's start with the database, as it takes a while to start it will give you time to work in parallel with the visual studio in the meantime.</span></span><br />
<span style="font-family: Calibri;"><span style="font-size: 15px;"><br /></span></span>
<span style="font-family: Calibri;"><span style="font-size: 15px;">As I already have my azure account and everything set up I went straight to the creation of the database account: <a href="https://portal.azure.com/#gallery/Microsoft.DocumentDB" target="_blank">preview management portal DocumentDB Creation</a>.</span></span><br />
<span style="font-family: Calibri;"><span style="font-size: 15px;"><br /></span></span>
<span style="font-family: Calibri;"><span style="font-size: 15px;">The process is explained in detail here: <a href="http://azure.microsoft.com/en-us/documentation/articles/documentdb-create-account/%C2%A0" target="_blank">http://azure.microsoft.com/en-us/documentation/articles/documentdb-create-account/ </a></span></span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">First you need to specify a couple of parameters <strike>you won't see the page in Spanish necessarily, in fact I don't know why it is not showing it to me in English</strike></span><span style="font-family: Calibri;"> to set up the new database.</span><br />
<span style="font-family: Calibri;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijVoDENJNegVWKOOCX8rbf3p9_s5k7oaxFvC9wVOwouQ2rg56iQ_dGlZgvF5R5YEpsS9jO-d4oLbBmUvKKEwfAhOiIzL9nabTFJDp9JFKTJuYJKKIABNdwAPoK4nZiv2Sjkq-R4xkPukpm/s1600/DatabaseParameters.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijVoDENJNegVWKOOCX8rbf3p9_s5k7oaxFvC9wVOwouQ2rg56iQ_dGlZgvF5R5YEpsS9jO-d4oLbBmUvKKEwfAhOiIzL9nabTFJDp9JFKTJuYJKKIABNdwAPoK4nZiv2Sjkq-R4xkPukpm/s1600/DatabaseParameters.png" height="320" width="260" /></a></div>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">And once you are done you will be taken to a home page of the azure portal while you wait...</span><br />
<span style="font-family: Calibri;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMHCr6JnRlWqVesGZ8yyoXB_19_NhNPzbu-1HkcPMBU40XUrJumcZu6cABZJ-x1OiPbQR5RrIwqhZM47P40Bu__8zEYgKIbLF2WsSRPDu_tUXAsJPf34NrHE4LxbwUPsD8IxkABAoS4qNl/s1600/AzurePortalWaiting.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMHCr6JnRlWqVesGZ8yyoXB_19_NhNPzbu-1HkcPMBU40XUrJumcZu6cABZJ-x1OiPbQR5RrIwqhZM47P40Bu__8zEYgKIbLF2WsSRPDu_tUXAsJPf34NrHE4LxbwUPsD8IxkABAoS4qNl/s1600/AzurePortalWaiting.png" height="181" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<span style="font-family: Calibri;">And while we wait we can go to the Visual Studio and start creating the projects we need.<strike> I usually say things like easy, piece of cake etc. but surely not so often today.</strike></span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">First of all I have created an universal app in Visual Studio 2013 <strike>as I am so outstandingly good with the user experience it will be the natural option for me to create two user interfaces, one for tablets and one for phones...</strike></span><br />
<span style="font-family: Calibri;"><strike><br /></strike></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0_9yaUop0q6tbImq-LI61Qyl0yoGy5UT4OXW_6ZFBeE5bWE5u0d9WzOhfUHYxh6fqg3mpp24rD1GKOkjxLdAj4cBGjJWKuNbzZ9tGQ6QnuwoczsEaJusf2WQjoyc8NnsTbYlPm9M0jraf/s1600/NewUniversalApp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0_9yaUop0q6tbImq-LI61Qyl0yoGy5UT4OXW_6ZFBeE5bWE5u0d9WzOhfUHYxh6fqg3mpp24rD1GKOkjxLdAj4cBGjJWKuNbzZ9tGQ6QnuwoczsEaJusf2WQjoyc8NnsTbYlPm9M0jraf/s1600/NewUniversalApp.png" height="222" width="320" /></a></div>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">Now let's create a "Class Library (Portable for Universal Apps) to manage the DocumentDB connections and queries:</span><br />
<span style="font-family: Calibri;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdMI1_v05FjbaXYGQ_KJ6HSH5Nodnaj_V_-AUF8c12e0-gvsUBUCJkYGHAJN_QxlwMXf4_oYjwOsqb1VexQf10g3kSY0b25rDwTyMkU0poXxTdklyGqoWozJoW0yOkJWQUwhbHIPE7ENtT/s1600/PortableDll.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdMI1_v05FjbaXYGQ_KJ6HSH5Nodnaj_V_-AUF8c12e0-gvsUBUCJkYGHAJN_QxlwMXf4_oYjwOsqb1VexQf10g3kSY0b25rDwTyMkU0poXxTdklyGqoWozJoW0yOkJWQUwhbHIPE7ENtT/s1600/PortableDll.png" height="223" width="320" /></a></div>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">Once the DLL project was created I added a folder called Models with a class DbUser and a class Device:</span></div>
<pre class="c#" name="code">namespace DocDbBridge.Models
{
public class DbUser
{
public string Email { get; set; }
public string Name { get; set; }
public Device[] Devices { get; set; }
}
}
namespace DocDbBridge.Models
{
class Device
{
public string Name { get; set; }
public string Brand { get; set; }
}
}</pre>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">Finally I went to the main class (which I called Connection) and added the following usings:</span><br />
<span style="font-family: Calibri;"><br /></span>
<br />
<pre class="c#" name="code">using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
</pre>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">It doesn't work because we are missing the<a href="http://www.nuget.org/packages/Microsoft.Azure.Documents.Client/0.9.2-preview" target="_blank"> </a><b><a href="http://www.nuget.org/packages/Microsoft.Azure.Documents.Client/0.9.2-preview" target="_blank">Microsoft Azure DocumentDB Client Library 0.9.2-preview</a>.</b> In order to get it I have set the DLL to use .Net Framework 4.5.1</span><br />
<span style="font-family: Calibri;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtMWHnn8rVx_ZCkgBZYPYmLCUpxa0GZfCG0aATDPgAZTqOZwC4c8uPOi7ShnJee3WhRpVOqOIviyqU5OP_yG6n00lk00fsr05hKdgYBoooFZIFfOUmVwFvEzXtR78-qSpc5l69-A_DDPTk/s1600/SetFw4.5.1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtMWHnn8rVx_ZCkgBZYPYmLCUpxa0GZfCG0aATDPgAZTqOZwC4c8uPOi7ShnJee3WhRpVOqOIviyqU5OP_yG6n00lk00fsr05hKdgYBoooFZIFfOUmVwFvEzXtR78-qSpc5l69-A_DDPTk/s1600/SetFw4.5.1.png" height="169" width="320" /></a></div>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">Then got Newtonsoft.Json from Nuget:</span><br />
<span style="font-family: Calibri;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWZOY5jC7gctzua105Gl50djonHik1nHmBINlbpZ8ML6-1y5MboVyobWWyKNaKOb7mgeDGCHSHT9OQzbBBSZo2AOJCBLnMqCFd0uQ9nsPs1eFHFpd0u22I67h5j8RNAmorD4jKYiNttVFd/s1600/JsonNet.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWZOY5jC7gctzua105Gl50djonHik1nHmBINlbpZ8ML6-1y5MboVyobWWyKNaKOb7mgeDGCHSHT9OQzbBBSZo2AOJCBLnMqCFd0uQ9nsPs1eFHFpd0u22I67h5j8RNAmorD4jKYiNttVFd/s1600/JsonNet.png" height="214" width="320" /></a></div>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">That creates a packages.config file in the project. In it I have added a line for the Microsoft.Azure.Documents.Client and rebuilt the project. </span><span style="font-family: Calibri;">The packages.config file looks like this:</span><br />
<span style="font-family: Calibri;"><br /></span>
<pre class="xml" name="code"><?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Azure.Documents.Client" version="0.9.2-preview" targetFramework="portable-net451+win81+wpa81" />
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="portable-net451+win81+wpa81" />
</packages>
</pre>
<br />
<span style="font-family: Calibri;">Finally, after the rebuild i have added a reference to the Microsoft.Azure.Documents.Client by browsing the project folders and finding the newly downloaded dll:</span><br />
<span style="font-family: Calibri;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYXjnOEc3wK-mCgbpcHH0ebYAxcgURXOv9-hk2p_f8B9s37AXYudhvzQG6V83FOvxH5aQpy5xFsjI4MJdQWWyu3_3ACNwF2k5Rmw9tS6bDpD-HR2FQ2L7nTITDHki3qf7zyWMh2IJEEutC/s1600/AddDllBrowsing.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYXjnOEc3wK-mCgbpcHH0ebYAxcgURXOv9-hk2p_f8B9s37AXYudhvzQG6V83FOvxH5aQpy5xFsjI4MJdQWWyu3_3ACNwF2k5Rmw9tS6bDpD-HR2FQ2L7nTITDHki3qf7zyWMh2IJEEutC/s1600/AddDllBrowsing.png" height="221" width="320" /></a></div>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">I have built the project again and it seems to be working, let's try to connect to the database now. Based in an example provided by Microsoft for the version 0.9.0 I have created a Connection class that goes like this:</span><br />
<span style="font-family: Calibri;"><br /></span>
<pre class="c#" name="code">using DocDbBridge.Models;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace DocDbBridge
{
public class Connection : IDisposable
{
string endPoint = "https://chan.documents.azure.com:443/";
string authKey = "Weird string with a lot of meaningless characters";
DocumentClient client { get; set; }
Database database { get; set; }
DocumentCollection collection { get; set; }
public Connection()
{
client = new DocumentClient(new Uri(endPoint), authKey);
database = ReadOrCreateDatabase("QuickStarts");
collection = ReadOrCreateCollection(database.SelfLink, "Documents");
CreateDocuments(collection.SelfLink);
}
public DbUser QueryDocumentsLinq(string UserEmail)
{
// The .NET SDK for DocumentDB supports 3 different methods of Querying for Documents
// LINQ queries, lamba and SQL
//LINQ Lambda
//return client.CreateDocumentQuery<dbuser>(collection.SelfLink).Where(u => u.Email == UserEmail).AsEnumerable().FirstOrDefault();
return client.CreateDocumentQuery<dbuser>(collection.SelfLink).ToList().Where(u => u.Email == UserEmail).FirstOrDefault();
}
public DbUser QueryDocumentsSQL(string SqlQuery)
{
//3. SQL
//var query = client.CreateDocumentQuery<dbuser>(collection.SelfLink, "SELECT * " +
// "FROM UserDbs u " +
// "WHERE u.email='andrew@stratex.com'");
var query = client.CreateDocumentQuery<dbuser>(collection.SelfLink, SqlQuery);
return query.AsEnumerable().FirstOrDefault<dbuser>();
}
public void Dispose()
{
Cleanup(database.SelfLink);
client.Dispose();
}
#region Private Methods
private Database ReadOrCreateDatabase(string databaseId)
{
// Most times you won't need to create the Database in code, someone has likely created
// the Database already in the Azure Management Portal, but you still need a reference to the
// Database object so that you can work with it. Therefore this first query should return a record
// the majority of the time
Database db = client.CreateDatabaseQuery().ToList().Where(d => d.Id == databaseId).FirstOrDefault();
//db = client.CreateDatabaseQuery()
// .Where(d => d.Id == databaseId)
// .AsEnumerable()
// .FirstOrDefault();
// In case there was no database matching, go ahead and create it.
if (db == null)
{
//Console.WriteLine("2. Database not found, creating");
db = client.CreateDatabaseAsync(new Database { Id = databaseId }).Result;
}
return db;
}
private DocumentCollection ReadOrCreateCollection(string databaseLink, string collectionId)
{
DocumentCollection col = client.CreateDocumentCollectionQuery(databaseLink).ToList().Where(c => c.Id == collectionId).FirstOrDefault(); ;
//col = client.CreateDocumentCollectionQuery(databaseLink)
// .Where(c => c.Id == collectionId)
// .AsEnumerable()
// .FirstOrDefault();
// For this sample, if we found a DocumentCollection matching our criteria we are simply deleting the collection
// and then recreating it. This is the easiest way to clear out existing documents that might be left over in a collection
//
// NOTE: This is not the expected behavior for a production application.
// You would likely do the same as with a Database previously. If found, then return, else create
if (col != null)
{
//Console.WriteLine("3. Found DocumentCollection.\n3. Deleting DocumentCollection.");
client.DeleteDocumentCollectionAsync(col.SelfLink).Wait();
}
//Console.WriteLine("3. Creating DocumentCollection");
return client.CreateDocumentCollectionAsync(databaseLink, new DocumentCollection { Id = collectionId }).Result;
}
private void CreateDocuments(string collectionLink)
{
// DocumentDB provides many different ways of working with documents.
// 1. You can create an object that extends the Document base class
// 2. You can use any POCO whether as it is without extending the Document base class
// 3. You can use dynamic types
// 4. You can even work with Streams directly.
//
// This sample method demonstrates only the first example
// For more examples of other ways to work with documents please consult the samples on MSDN.
// Work with a well defined type that extends Document
// In DocumetnDB every Document must have an "id" property. If you supply one, it must be unique.
// If you do not supply one, DocumentDB will generate a unique value for you and add it to the Document.
var task1 = client.CreateDocumentAsync(collectionLink, new DbUser
{
Email = "chan@stratex.com",
Name = "Test user",
Devices = new Device[]
{
new Device { Name="Lumia 920", Brand="Nokia"},
new Device { Name="Surface 3", Brand="Microsoft"},
}
});
var task2 = client.CreateDocumentAsync(collectionLink, new DbUser
{
Email = "andrew@stratex.com",
Name = "Andrew",
Devices = new Device[]
{
new Device { Name="Lumia 925", Brand="Nokia"},
new Device { Name="Surface 3", Brand="Microsoft"},
}
});
// Wait for the above Async operations to finish executing
Task.WaitAll(task1, task2);
}
private void Cleanup(string databaseId)
{
client.DeleteDatabaseAsync(databaseId).Wait();
}
#endregion
}
}
</pre>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">As you might have noticed you also need the URL and the Auth Key, you can get them from the azure portal which by now will probably have your database up and running:</span><br />
<span style="font-family: Calibri;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEin35WkGZfHbf1Sds4ncKMbkZmvQiVOlWYWES5MChY7UolksQGz6msWYvAucozcs0aKE87_MtFYgdFjn3ANrrS1gZufQ9vRbDIr2Ynv2OZTPa78DCKGksubtjzUZQh2pJqnERqETTDd30sh/s1600/DatabaseAuthKeys.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEin35WkGZfHbf1Sds4ncKMbkZmvQiVOlWYWES5MChY7UolksQGz6msWYvAucozcs0aKE87_MtFYgdFjn3ANrrS1gZufQ9vRbDIr2Ynv2OZTPa78DCKGksubtjzUZQh2pJqnERqETTDd30sh/s1600/DatabaseAuthKeys.png" height="128" width="320" /></a></div>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">After that I have added a reference to the DocDbBridge DLL in my app project:</span><br />
<span style="font-family: Calibri;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_q0MANmzdwwlAKwjp7T9n7LeJ6C2CIVpMKmifLYMgjTC3P-rOfN4BAYMMsDpVIGFtNKvVJSrAfbIKuTHnYMV1anDMC1A3GgqquYTI2fWd2vazchrSG676BSQ5HbQvsoV8thVRNi9YD0dg/s1600/AddProjectToApp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_q0MANmzdwwlAKwjp7T9n7LeJ6C2CIVpMKmifLYMgjTC3P-rOfN4BAYMMsDpVIGFtNKvVJSrAfbIKuTHnYMV1anDMC1A3GgqquYTI2fWd2vazchrSG676BSQ5HbQvsoV8thVRNi9YD0dg/s1600/AddProjectToApp.png" height="222" width="320" /></a></div>
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">I have, after that, executed my app, by the way, this is the code... <strike>Impressive</strike></span><br />
<span style="font-family: Calibri;"><br /></span>
<br />
<pre class="c#" name="code"> private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
using (Connection conn = new Connection())
{
var user = conn.QueryDocumentsLinq("andrew@stratex.com");
if (user != null)
Result.Text = user.Name;
else
Result.Text = "User not found.";
}
}
catch (Exception ex)
{
Result.Text = "Exception found";
}
}
</pre>
<span style="font-family: Calibri;">And it has failed miserably because it needs again the Newtonsoft.Json package... I have installed it on the W8.1 app project.</span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">After that I executed the project again, clicked my <strike>marvellous</strike> button and I got Exception: Microsoft.Azure.Documents.BadRequestException: Syntax error, unexpected end-of-file here:</span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;"><br /></span>
<br />
<pre class="c#" name="code">client.CreateDatabaseQuery()
.Where(d => d.Id == databaseId)
.AsEnumerable()
.FirstOrDefault();
//And here:
client.CreateDocumentCollectionQuery(databaseLink)
.Where(c => c.Id == collectionId)
.AsEnumerable()
.FirstOrDefault();
</pre>
<span style="font-family: Calibri;">It looks like there's something not working quite right in this release... I have changed the line to: </span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;"><i>Database db = client.CreateDatabaseQuery().ToList().Where(d => d.Id == databaseId).FirstOrDefault();</i></span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">
This is a huge issue because it means that you need to retrieve all of them first and then query the database... Completely Unusable! :( <strike>Could it be because I am using a piece of software that is in preview in a platform that it's not supported?... some would say yes...</strike></span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">Again in the Linq query the same issue, I had to change from: </span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;"><i>return client.CreateDocumentQuery<DbUser>(collection.SelfLink).Where(u => u.Email == UserEmail).AsEnumerable().FirstOrDefault();</i></span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">To</span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;"><i>return client.CreateDocumentQuery<DbUser>(collection.SelfLink).ToList().Where(u => u.Email == UserEmail).FirstOrDefault();</i></span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">And I got again the same error when tried to execute the queries with SQL.</span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;"><b>Wrapping up:</b></span><br />
<span style="font-family: Calibri;"><i>Upsides</i>: It kind of works and that's really cool.</span><br />
<span style="font-family: Calibri;"><i>Downsides</i>: What is a database if you cannot query? I would say... Not Ideal. </span><br />
<span style="font-family: Calibri;"><br /></span>
<span style="font-family: Calibri;">But then again this is a preview software in an unsupported platform.... And it works <strike>more or less</strike>. Just imagine how cool would the software be in a couple of iterations!</span>Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-48312046432112209012014-10-23T15:45:00.001+01:002014-10-23T15:45:27.958+01:00Surface 3 WiFi and Hyper V<br />
Looks like something is not fully compatible between the surface and the Hyper V.<br />
<br />
And if you have installed the latest Visual Studio you have probably noticed that it takes longer than usual for the Wi-Fi to connect <strike>let alone that you don't have the sleep mode anymore</strike>.<br />
<br />
In order to workaround this issue I have created a couple of shortcuts in the desktop, one for turning off Hyper V and one for turning it back on when you really need it.<br />
<br />
Pretty simple stuff:<br />
Right click on your desktop and select New > Shortcut<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_yZCtA9J9SSkNuB90deFz182b2liE8e7ALZYnc9e7OO1xnIs1hq04W90970S0f3FW7cystQRQRbkDBBr1o_qiAK8VTQh9bn3q9ueNq-UI-Pu1EmdRgr0k5qXlUxjZSXNtUbjgFh2Q6Dhq/s1600/newshortcut.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_yZCtA9J9SSkNuB90deFz182b2liE8e7ALZYnc9e7OO1xnIs1hq04W90970S0f3FW7cystQRQRbkDBBr1o_qiAK8VTQh9bn3q9ueNq-UI-Pu1EmdRgr0k5qXlUxjZSXNtUbjgFh2Q6Dhq/s1600/newshortcut.png" height="285" width="320" /></a></div>
You'll see the window for selecting the program to which the shortcut is going to be pointing at.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp2rDWqbNrzRidBsxdiFjHd1ILiQ6NQpo7UYIaxtGzcEt9x7juajl9JOIJUR4oijwEnlVDBFR8oJCijy8ORhMLW-zFEGvUp5PaWPWqUNhX27_UqPKJ-y41BbiiBQuawt59LkpY65WC-Mat/s1600/NameShortcutPNG.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp2rDWqbNrzRidBsxdiFjHd1ILiQ6NQpo7UYIaxtGzcEt9x7juajl9JOIJUR4oijwEnlVDBFR8oJCijy8ORhMLW-zFEGvUp5PaWPWqUNhX27_UqPKJ-y41BbiiBQuawt59LkpY65WC-Mat/s1600/NameShortcutPNG.PNG" height="259" width="320" /></a></div>
Right after that we will find bcdedit.exe in the system32 folder.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkJKyXyCh5hkMPKArhEpdsuW9L68huPWNUMAelVptIbKr50NGwhZxVQD1XMZ2p_yMGLrpb4p_9WVki_qQ6xB3rBmfyFhkP-rlp4R9ZLhpRJf4lqj8RyuamSsyookSHtBN9b4UvIONQ-_8A/s1600/NameShortcutPNG2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkJKyXyCh5hkMPKArhEpdsuW9L68huPWNUMAelVptIbKr50NGwhZxVQD1XMZ2p_yMGLrpb4p_9WVki_qQ6xB3rBmfyFhkP-rlp4R9ZLhpRJf4lqj8RyuamSsyookSHtBN9b4UvIONQ-_8A/s1600/NameShortcutPNG2.PNG" height="259" width="320" /></a></div>
<div style="clear: both; text-align: center;">
</div>
<br />
And finally we will name the shortcut something meaningful.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVBvmlqbCyVOrKLN8iGOH2SkPp0PV80FIkPuaQkhu10PrPX7fiw5O6kjtBLyiYrIEYW2A3clMXn3t0Vebo9Ep-er6FOHK02sevE4J60gM8GY_OS3teUYhF56IQnfP1t5c9_QKW16bJu1nR/s1600/Shortcut.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVBvmlqbCyVOrKLN8iGOH2SkPp0PV80FIkPuaQkhu10PrPX7fiw5O6kjtBLyiYrIEYW2A3clMXn3t0Vebo9Ep-er6FOHK02sevE4J60gM8GY_OS3teUYhF56IQnfP1t5c9_QKW16bJu1nR/s1600/Shortcut.PNG" /></a></div>
<span id="goog_460657679"></span><span id="goog_460657680">Now the tricky part. We will right click on the newly created shortcut and select properties.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivIxby7ndIu4W1oMOirZ8AludB8r4sv5VOJytjAHH0L4VY3tEUriX1a_ueLvs1KlBxJWWgKs7gQDVKQLoSOWuWBOmiKOH2bGUBT5yky9NT5DjTChW0gp0YV7O3BFkAq_9SWUG0qxif7940/s1600/ModifyTarget.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"></a><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivIxby7ndIu4W1oMOirZ8AludB8r4sv5VOJytjAHH0L4VY3tEUriX1a_ueLvs1KlBxJWWgKs7gQDVKQLoSOWuWBOmiKOH2bGUBT5yky9NT5DjTChW0gp0YV7O3BFkAq_9SWUG0qxif7940/s1600/ModifyTarget.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivIxby7ndIu4W1oMOirZ8AludB8r4sv5VOJytjAHH0L4VY3tEUriX1a_ueLvs1KlBxJWWgKs7gQDVKQLoSOWuWBOmiKOH2bGUBT5yky9NT5DjTChW0gp0YV7O3BFkAq_9SWUG0qxif7940/s1600/ModifyTarget.PNG" height="320" width="232" /></a></div>
<br />
For the HyperV <strong>On</strong> shortcut you'll need this text I the Target box:<br />
<blockquote class="tr_bq">
C:\Windows\System32\bcdedit.exe /set hypervisorlaunchtype auto</blockquote>
For the HyperV <strong>Off</strong> you'll need this:<br />
<blockquote class="tr_bq">
C:\Windows\System32\bcdedit.exe /set hypervisorlaunchtype off</blockquote>
After that click on the Advanced properties and select Run as administrator.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrqilAjcMDPxead3b_LLIdu-ARTJjrNnnots8v3_jRoivyLNC7ehAB2SjSaFPTPOWMMkeyWk5qdFYO_wkfzHw_vdDCZmRkkqzWBHDfqeIQe20jNoFjaMiGRTP3lwS1FofXTMe_A3qm-svK/s1600/Administrator.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrqilAjcMDPxead3b_LLIdu-ARTJjrNnnots8v3_jRoivyLNC7ehAB2SjSaFPTPOWMMkeyWk5qdFYO_wkfzHw_vdDCZmRkkqzWBHDfqeIQe20jNoFjaMiGRTP3lwS1FofXTMe_A3qm-svK/s1600/Administrator.PNG" height="241" width="320" /></a></div>
<br />
<br />
And finally select a nice icon for the shortcut<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6LbvBk9CKJavRfEerVfRjZnvxT40QA3qbsJb9Q96XICh1KgODGIFc29caWDI0NvryBDCwMl6KvvcqRFntR6W5BZHaBNh6P2HX6ucnHYcZPM4yf98-lz7vdTnVgFrRUxqkDkoupBt2SITZ/s1600/changeicon.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6LbvBk9CKJavRfEerVfRjZnvxT40QA3qbsJb9Q96XICh1KgODGIFc29caWDI0NvryBDCwMl6KvvcqRFntR6W5BZHaBNh6P2HX6ucnHYcZPM4yf98-lz7vdTnVgFrRUxqkDkoupBt2SITZ/s1600/changeicon.PNG" height="320" width="274" /></a></div>
<br />
Remember to do the HyperV On and the HyperV Off icons.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2z2jsUL3zmzQTz5UgszSfWo9eAm1KzI-7biqLhN4_lLif-Pc1rcTtYw7cif_AM6_C_XOxduKxJV74e20B04SzdiqReoMMGW3auhRIjZdZS3Lg-SwQYjtNpqDXa1mSsWTu0YkTzcTp787a/s1600/FinishShortcut.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2z2jsUL3zmzQTz5UgszSfWo9eAm1KzI-7biqLhN4_lLif-Pc1rcTtYw7cif_AM6_C_XOxduKxJV74e20B04SzdiqReoMMGW3auhRIjZdZS3Lg-SwQYjtNpqDXa1mSsWTu0YkTzcTp787a/s1600/FinishShortcut.PNG" /></a></div>
<br />
And finally remember that after you execute these shortcuts, in order for the change to take effect you'll have to restart the computer.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-39816682685572542602014-08-27T09:22:00.001+01:002014-08-27T09:22:38.649+01:00Cannot access my Azure VM (And how I solved it)Cannot access my Azure VM (And how I solved it)
I was creating a new firewall rule in one of my azure VMs and I went a tad greedy when it came to blocking IPs... I blocked ALL the traffic.<br />
<br />
What to do?<br />
<br />
<ul>
<li>My first thought was to download the VM, make the change locally and upload it again... but it's 250GB.</li>
<li>I tried changing the firewall rule through powershell from the azure subscription but Get-AzureWinRMUri was not working.</li>
<li>Finally after asking a friend who asked a friend of his I was told it was possible to edit the registry of a windows OS disk form a different windows. Wow! That was the answer!</li>
</ul>
<br />
<br />
And, in fact, once I knew it could be done I found <a href="http://social.technet.microsoft.com/wiki/contents/articles/18710.troubleshoot-azure-vm-by-attaching-os-disk-to-another-azure-vm.aspx" target="_blank">a detailed post</a> about how to do it.<br />
<br />
So I deleted my beloved VM, attached the VHD to another VM and from the registry I deleted the firewall rule that was blocking all the traffic.<br />
<br />
Then I detached the VHD and created a new VM with it and ¡listo! we had the VM up and running and accessible.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-19638934255682236962014-07-29T16:07:00.000+01:002014-07-29T16:11:41.018+01:00Azure VM Snapshots Even SimplerI have been thinking about taking and restoring snapshots of my Azure Virtual Machines for a while but never had the time to do it... until today.<br />
<br />
It is extremely easy when you know how to do it but it could be a bit daunting when you have to start from scratch and as I have to write a post explaining it <strike>and it was about time for me to write another post in my blog</strike> I thought it could be a good idea to do it here.<br />
<br />
From the beginning:<br />
<ul>
<li>You need to install the <a href="http://azure.microsoft.com/en-us/documentation/articles/install-configure-powershell/#Install" target="_blank">Azure PowerShell</a></li>
<li>Then you need to connect it to your subscription</li>
<ul>
<li>Go to <a href="https://windows.azure.com/download/publishprofile.aspx" rel="nofollow">https://windows.azure.com/download/publishprofile.aspx</a> </li>
<li>Download the file and name it something simple like <span style="color: #6aa84f;">c:\tmp.publishsettings</span></li>
<li>In the Azure PowerShell run <span style="color: #6aa84f;">Import-AzurePublishSettingsFile 'c:tmp.publishsettings'</span></li>
</ul>
</ul>
Luckily by now you will have it working.<br />
<br />
Now the real thing. <a href="http://blogs.msdn.com/b/cclayton/" target="_blank">Chris Clayton</a> has created a set of <a href="http://gallery.technet.microsoft.com/scriptcenter/Backup-and-Restore-Windows-c928fa13" target="_blank">scripts that work wonderfully for managing Azure VM Screenshots</a> and that is what I am using right now. If you are too lazy to go to the page and find the link you can get the scripts from <a href="http://gallery.technet.microsoft.com/scriptcenter/Backup-and-Restore-Windows-c928fa13/file/116862/1/Scripts.zip" target="_blank">here</a>.<br />
The only thing you need to configure is the Subscriptions.csv file. It's a CSV file so in the first line you have the column names and in the rest of them you have the data. It looks like this:<br />
<blockquote class="tr_bq">
SubscriptionName,SubscriptionId,CertificateThumbprint<br />
"IT","XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"</blockquote>
This file will make the executing the commands easier as it has all the information about the subscription(s) that you are going to use. Yes, cool but... where to I get the data? look.<br />
<br />
First, to get all the information we need about our subscription in PowerShell we can run:<br />
<blockquote class="tr_bq">
<span style="color: #6aa84f;">Get-AzureSubscription</span></blockquote>
That will give us a screen like this one:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWVeBAJsnum3R79oWCk_CsGTd6y1vTa98xVC938N0WzdEM5jFRsuJGiIbEztC3Q0yFENMDkR5LidPMMloLMNLdqSYNeU9c60EYe8e3cVlZV042d1okevNk9lsuKxbwG2liTddPujUFd01v/s1600/AzureSubscriptionInfo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWVeBAJsnum3R79oWCk_CsGTd6y1vTa98xVC938N0WzdEM5jFRsuJGiIbEztC3Q0yFENMDkR5LidPMMloLMNLdqSYNeU9c60EYe8e3cVlZV042d1okevNk9lsuKxbwG2liTddPujUFd01v/s1600/AzureSubscriptionInfo.png" height="162" width="320" /></a></div>
Those are the parameters you need to fill in. Simple stuff.<br />
<br />
Finally in order to run the commands you will have to provide data about which subscription to use and inside it which virtual machine. Simple stuff, we get the list of virtual machines we have in our subscription executing <span style="color: #6aa84f;">Get-AzureVM</span> and we will get a screen like this one:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRuyIBSsRWV7bqbUIsTVPD7aUSn7ow1Li-U7ZXYUAaO3BQ3pnq81GQvGlZahXMzAfkAT49tGtuqFW9SG3SuyBvXPhh47RVhwK4L_zrjT1SHK_ltwXcpTypN_ajjFiwXbIgx089XvnA5ire/s1600/GetAzureVM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRuyIBSsRWV7bqbUIsTVPD7aUSn7ow1Li-U7ZXYUAaO3BQ3pnq81GQvGlZahXMzAfkAT49tGtuqFW9SG3SuyBvXPhh47RVhwK4L_zrjT1SHK_ltwXcpTypN_ajjFiwXbIgx089XvnA5ire/s1600/GetAzureVM.png" height="42" width="320" /></a></div>
<br />
Now we have all the parameters we need. Just for testing it we will launch the script for getting the list of snapshots in a virtual machine.<br />
<br />
<br />
<blockquote class="tr_bq">
<span style="color: #6aa84f;">./GetSnapshotList.ps1 -subscriptionName "NameOfTheSubscription" -cloudServiceName "ServiceName" -virtualMachineName "Name" -maximumDays 15</span></blockquote>
Where:<br />
<ul>
<li>-subscriptionName is the SubscriptionName you got from Get-AzureSubscription and then wrote in Subscriptions.csv</li>
<li>-cloudServiceName is the ServiceName column you can see at Get-AzureVM</li>
<li>-virtualMachineName is the Name column you can see at Get-AzureVM</li>
</ul>
<br />
And that's it.<br />
<br />
Things to remember:<br />
<ul>
<li>The VM should be shut down before taking the snapshot (there's a handy parameter for that in the PowerShell command)</li>
<li>The data in your "Temporary Storage" drives will not be backed up or restored because they are temporary. <strike>Yes I knew it was going to work that way but I had to try.</strike></li>
</ul>
<br />
As a test I took a snapshot of one of my SharePoint development machines, made some <a href="http://www.goear.com/listen/72617e4/changes-david-bowie" target="_blank">changes</a>, restored it and so how the <a href="http://www.goear.com/listen/72617e4/changes-david-bowie" target="_blank">changes</a> were reverted, just as expected. And I would even say faster than I expected.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-20082665654879095682013-09-19T11:39:00.002+01:002013-09-19T11:46:35.120+01:00Get the fields available in an SPListItemWhen you retrieve the items from a CAML query with the ViewFields parameters set there’s no way (or at least i don’t know it) to find out which fields you have available and populated with data.<br />
<br />
The usual way of getting the list of fields from the content type doesn’t work because the content type is null in that kind of items…<br />
<br />
The answer, my friend, is sitting in the Xml.<br />
<br />
<pre class="c#" name="code">static List<string> ExcludedFields = new List<string> { "z", "ows_ServerRedirected", "ows_FileRef", "ows_PermMask", "ows_FSObjType", "ows__Level", "ows__ModerationStatus" };
/// <summary>
/// Gets a dictionary with the available fields and its values.
/// </summary>
public static Dictionary<string, string> getAvailableFields(this SPListItem item)
{
XElement row = XElement.Parse(item.Xml);
Dictionary<string, string> Fields = new Dictionary<string, string>();
foreach (XAttribute field in row.Attributes())
{
if (!ExcludedFields.Contains(field.Name.LocalName))
Fields.Add(field.Name.LocalName.Substring(4), field.Value);
}
return Fields;
}</pre>
<br />
Using this I have been able to reduce the traffic on a web service method in a 65%.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-3979816365371039812013-08-23T04:23:00.001+01:002013-08-23T04:23:04.091+01:00Scopes in a CAML QueryI have been working for quite a while now with CAML queries and the scope is always something very important to bear in mind. <strike>How many times my queries returned nothing when I was <strong>sure</strong> they should bring back something…</strike><br />
<br />
Basically we have two modifiers Recursive and All <strike>and nothing, could we call nothing a modifier?</strike> All will bring back folders and files. Recursive will repeat the query in all the folders under the one we are working with.<br />
<br />
If you don’t set the scope to All it will only bring files. If you don’t set it to recursive it will only retrieve items from the folder you are at. There are not that many variants so let’s make an example of each.<br />
<br />
Let’s imagine we have a SharePoint folder like this one and we want to query it:<br />
<br />
<img alt="CamlScopeTreeSample" border="0" height="152" src="http://lh3.ggpht.com/-cbntV8XkZVM/UhbUvbYHxTI/AAAAAAAAAQc/8UguJdW84wU/CamlScopeTreeSample_thumb%25255B1%25255D.png?imgmax=800" style="display: block; float: none; margin-left: auto; margin-right: auto;" title="CamlScopeTreeSample" width="129" /><br />
<strike><br /></strike>
<strike>I am not good at paint, I know but</strike> what I want to show here is a tree where we have a Root folder (the root of the queries) and two sub-folders with files. For each scope possible I’ll highlight what you can expect to retrieve.<br />
<br />
Just before we start, allow me to remind you that the scope is set in the property ViewAttributes of the SPQuery item.<br />
<br />
<div align="left">
To add a bit more of clarity I have also painted the levels:<br />
<br /></div>
<a href="http://lh5.ggpht.com/-CbtJTGv_11Q/UhbUv3A7EKI/AAAAAAAAAQg/gaGGtu38Knk/s1600-h/CamlScopeTreeSampleLevels%25255B5%25255D.png"><img alt="CamlScopeTreeSampleLevels" border="0" height="146" src="http://lh3.ggpht.com/-CDrzqWcNpQY/UhbUwIQ4h1I/AAAAAAAAAQs/cTQTgSHsdWw/CamlScopeTreeSampleLevels_thumb%25255B1%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeTreeSampleLevels" width="127" /></a><br />
<br />
The green line marks what’s inside the root folder, the blue line marks the contents of SubFolder1 and the red line SubFolder2.<br />
<br />
<span style="font-size: large;">ViewAttributes left by default:</span><br />
<span style="font-size: large;"><br /></span>
<a href="http://lh4.ggpht.com/-TSgAnvInGv4/UhbUw11abdI/AAAAAAAAAQ0/UDa1JKLFFV0/s1600-h/CamlScopeByDefault%25255B8%25255D.png"><img alt="CamlScopeByDefault" border="0" height="152" src="http://lh4.ggpht.com/-VpxY_AfVzQw/UhbUxe2AezI/AAAAAAAAAQ8/lKlhTgCCu_g/CamlScopeByDefault_thumb%25255B2%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeByDefault" width="129" /></a><br />
This is just the files under the root folder.<br />
<br />
<span style="font-size: large;">ViewAttributes = "Scope=<span style="color: #ffc000;">'Recursive'</span>"</span><br />
<span style="font-size: large;"><br /></span>
<a href="http://lh6.ggpht.com/-HApzSZ-YP4o/UhbUxtWR8PI/AAAAAAAAARE/EGflQiaDsW4/s1600-h/CamlScopeRecursive%25255B5%25255D.png"><img alt="CamlScopeRecursive" border="0" height="152" src="http://lh3.ggpht.com/-FXigjzQOYyo/UhbUyGLYPhI/AAAAAAAAARM/T0VogAyZxvY/CamlScopeRecursive_thumb%25255B1%25255D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeRecursive" width="129" /></a><br />
<br />
This means all the files in all the folders.<br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">ViewAttributes = "Scope=<span style="color: #ffc000;">'All'</span>"</span><br />
<span style="font-size: large;"><br /></span>
<a href="http://lh5.ggpht.com/-aFc7P8ezIQQ/UhbUynw0SPI/AAAAAAAAARU/bE5--zH5UeY/s1600-h/CamlScopeAll%25255B2%25255D.png"><img alt="CamlScopeAll" border="0" height="152" src="http://lh3.ggpht.com/-EjCd490752M/UhbUzPfwC7I/AAAAAAAAARc/Luv3lK4WyW4/CamlScopeAll_thumb.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeAll" width="129" /></a><br />
<br />
This scope will bring folders and files under root.<br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">ViewAttributes = "Scope=<span style="color: #ffc000;">'RecursiveAll'</span>"</span><br />
<span style="font-size: large;"><br /></span>
<div align="center">
<a href="http://lh6.ggpht.com/-dqjWEoHgpXc/UhbUzsEhxJI/AAAAAAAAARk/kzHQ45qKk0Q/s1600-h/CamlScopeRecursiveAll%25255B2%25255D.png"><img alt="CamlScopeRecursiveAll" border="0" height="152" src="http://lh4.ggpht.com/-Crz7f3wefS8/UhbU0ILZzpI/AAAAAAAAARs/U7x3Yt_95lw/CamlScopeRecursiveAll_thumb.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="CamlScopeRecursiveAll" width="129" /></a></div>
<div align="left">
<br />
And finally with RecursiveAll you can bring back everything under the root entity.</div>
<div align="left">
<br /></div>
<div align="right">
<span style="color: #cccccc; font-size: x-small;">Good luck with your queries.</span></div>
Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-84237858690594972162013-08-19T09:54:00.001+01:002013-08-19T10:09:35.110+01:00SharePoint Client Object Model Is GreatThis is one of those things that you know they are there but never use because you already know a different way.<br />
<br />
Just four years after I started working with SharePoint 2010 I thought “Why not giving the Client Object Model a go?” <strike>actually I had a requirement from a client</strike>. and I must say I am VERY impressed. The simplicity, the speed and the predictability is pretty good for the standards we are used to.<br />
<br />
Even though the COM is very good I have created a series of methods, wrappers and extensions to help me deal with my most common functions. Some of them translated from the methods I use in the SharePoint Object Model and some of them new. Let’s begin.<br />
<br />
It is really easy to connect using Windows authentication or Forms Based Authentication. Easier than anything else I have seen to date.<br />
<br />
<pre class="c#" name="code">public static ClientContext GenerateClientContextWinAuth(string URL)
{
return new ClientContext(URL);
}
public static ClientContext GenerateClientContextFBAAuth(string URL, string userName, string password)
{
ClientContext ctx = new ClientContext(URL);
ctx.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication;
ctx.FormsAuthenticationLoginInfo = new FormsAuthenticationLoginInfo(userName, password);
return ctx;
}
</pre>
<br />
Windows auth 1 line of code FBA 3. Nice.<br />
<br />
Do you remember how it was reading and writing files to SharePoint? Look how easy it is using the COM.<br />
<br />
<pre class="c#" name="code">public static string ReadFile(ClientContext Context, string ListName, string FileName)
{
List StorageList = Context.Web.Lists.GetByTitle(ListName);
string SharePointFilePath = GetFilePathInSharePoint(Context, StorageList, FileName);
FileInformation fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(Context, SharePointFilePath);
using (fileInfo.Stream)
{
using (StreamReader sr = new StreamReader(fileInfo.Stream))
{
return sr.ReadToEnd();
}
}
}
public static void WriteFile(ClientContext Context, string LocalFilePath, string ListName, string SPFileName)
{
List StorageList = Context.Web.Lists.GetByTitle(ListName);
string SharePointFilePath = GetFilePathInSharePoint(Context, StorageList, SPFileName);
using (FileStream fs = new FileStream(LocalFilePath, FileMode.Open))
Microsoft.SharePoint.Client.File.SaveBinaryDirect(Context, SharePointFilePath, fs, true);
}
private static string GetFilePathInSharePoint(ClientContext ctx, List StorageList, string FileName)
{
if (StorageList.RootFolder.ServerObjectIsNull != false)
{
ctx.Load(StorageList.RootFolder);
ctx.ExecuteQuery();
}
string ListRootFolderURLDocuments = StorageList.RootFolder.ServerRelativeUrl;
return Path.Combine(ListRootFolderURLDocuments, FileName);
}
</pre>
<br />
Other than the small function to find the url of the item in the document list using folders it's as simple as it can be.
I have also created a couple of methods for making easier to work with the StratexFramework.<br />
<br />
If you are creating some program and want to use some of the code feel free. If you are not working with StratexPoint feel free to modify it to suit your environment.<br />
<br />
Here's a method to bring back the root entity of the framework:
<br />
<pre class="c#" name="code">public static ListItem GetRootEntity(this List ComList)
{
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml = string.Format(@"<View>
<Query>
<Where>
<Eq>
<FieldRef Name='ContentType'/>
<Value Type='Choice'>Entity</Value>
</Eq>
</Where>
<RowLimit>1</RowLimit>
</Query>
</View>");
ListItemCollection items = ComList.GetItemsExecuted(camlQuery);
if (items.Count == 1)
return items[0];
else
return null;
}</pre>
<br />
Nice and easy.<br />
<br />
The query is a bit different to the usual we do in the SPQuery, but still very similar.<br />
<br />
Then I have created a method that will help when you want to run a query under a folder regardless of the rest of the framework. First I will paste the helper query and then I will paste the samples on how to use it:
<br />
<br />
<pre class="c#" name="code">public static CamlQuery CreatePositionedQuery(this ListItem StartEntity)
{
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml = string.Format(@"<View Scope='RecursiveAll' >
<Query>
<Where>
<And>
<Eq><FieldRef Name='FileDirRef' /><Value Type='Text'>{0}</Value></Eq>
{1}
</And>
</Where>
</Query>
</View>", GetChildrenFolder(StartEntity), "{0}"); //This is the folder url and the placeholder for the real query.
return camlQuery;
}
private static string GetChildrenFolder(ListItem startItem)
{
startItem.InitializeIfNeeded("FileDirRef");
startItem.InitializeIfNeeded("FileLeafRef");
return startItem["FileDirRef"] + "/" + startItem["FileLeafRef"];
}</pre>
<br />
With this we basically are saying SharePoint to execute the query under the folder of the item we are passing as a parameter. And we can use this code easily to position our queries in the folder tree.<br />
<br />
First one function to get all the child entities under a given entity, and then another function to bring an item with a given title inside a given folder:
<br />
<br />
<pre class="c#" name="code">public static ListItemCollection GetChildEntities(ListItem StartEntity)
{
List ComList = StartEntity.ParentList;
CamlQuery camlQuery = CreatePositionedQuery(StartEntity);
camlQuery.ViewXml = string.Format(camlQuery.ViewXml,
@"<And>
<Eq><FieldRef Name='State' /><Value Type='Choice'>Live</Value></Eq>
<Eq><FieldRef Name='ContentType' /><Value Type='Choice'>Entity</Value></Eq>
</And>");
return ComList.GetItemsExecuted(camlQuery);
}
public static ListItem GetChildItem(this ListItem StartEntity, string Title)
{
List ComList = StartEntity.ParentList;
CamlQuery camlQuery = CreatePositionedQuery(StartEntity);
camlQuery.ViewXml = string.Format(camlQuery.ViewXml,
string.Format("<Eq><FieldRef Name='Title' /><Value Type='Text'>{0}</Value></Eq>", Title));
ListItemCollection result = ComList.GetItemsExecuted(camlQuery);
if (result.Count > 0)
return result[0];
else
return null;
}</pre>
Creating a new item is also easy. The hardest thing to do is deciding if it's a folder or a leaf:
<br />
<pre class="c#" name="code">public static ListItem CreateItemUnder(this ListItem ParentItem, string Title, string ContentType, bool isLeaf)
{
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
itemCreateInfo.FolderUrl = GetChildrenFolder(ParentItem);
itemCreateInfo.LeafName = Title;
if (isLeaf)
itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.File;
else
itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.Folder;
ListItem NewItem = ParentItem.ParentList.AddItem(itemCreateInfo);
if (ContentType != null)
NewItem["ContentTypeId"] = GetContentType(ParentItem.ParentList, ContentType).Id;
return NewItem;
}
</pre>
<pre class="c#" name="code">
</pre>
A small shortcut to execute queries:
<br />
<br />
<pre class="c#" name="code">public static ListItemCollection GetItemsExecuted(this List listToQuery, CamlQuery query)
{
ListItemCollection childItems = listToQuery.GetItems(query);
listToQuery.Context.Load(listToQuery);
listToQuery.Context.Load(childItems);
listToQuery.Context.ExecuteQuery();
return childItems;
}</pre>
<pre class="c#" name="code">
</pre>
In some cases the field you want to read didn't come in the execution <strike>maybe because you were a bit too restrictive with the viewfields clause...</strike> You can try with this trick:
<br />
<br />
<pre class="c#" name="code">public static void InitializeIfNeeded(this ListItem item, string InternalName)
{
if (!item.FieldValues.ContainsKey(InternalName))
{
item.Context.Load(item);
item.Context.ExecuteQuery();
}
}</pre>
<pre class="c#" name="code">
</pre>
Are you trying to get a content type from a List? Easy.
<br />
<br />
<pre class="c#" name="code">public static ContentType GetContentType(List list, string cTypeName)
{
list.Context.Load(list.ContentTypes);
list.Context.ExecuteQuery();
foreach (ContentType ctype in list.ContentTypes)
{
if (ctype.Name == cTypeName) return ctype;
}
return null;
}</pre>
<pre class="c#" name="code">
</pre>
Are you trying to save a SPFieldUserValue in a SPFieldUser using the client object model or read it back? Complex, I would say unnecessarily complex, but I have one last trick:<br />
<br />
<pre class="c#" name="code">public static string UserToString(object fieldUserValue)
{
if (fieldUserValue == null) return string.Empty;
FieldUserValue user = fieldUserValue as FieldUserValue;
return string.Format("{0};{1}", user.LookupId, user.LookupValue);
}
public static FieldUserValue StringToUser(object fieldUserValueString)
{
FieldUserValue user = new FieldUserValue();
if (string.IsNullOrEmpty(fieldUserValueString.ToStringSafe())) return user;
string[] tokens = fieldUserValueString.ToStringSafe().Split(';');
user.LookupId = tokens[0].ToNullableInt() ?? 0;
return user;
}
</pre>
<pre class="c#" name="code">
</pre>
The client object model has been really powerful and really easy to work with for me so far. I really encourage you to give it a try if you haven't already.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-78845188529562548782013-08-09T08:15:00.002+01:002013-08-09T08:15:52.704+01:00The process wlms.exe has initiated the power off of computerI was getting random shutdowns of one of my virtual machines hosted in azure... I thought it was something related to azure but no.<br />
<br />
I had a look to the shutdown log and found out this:
<br />
<br />
<pre>Log Name: System
Source: USER32
Date: 08/08/2013 17:39:04
Event ID: 1074
Task Category: None
Level: Information
Keywords: Classic
User: SYSTEM
Computer: COMPUTERNAME.local
Description:
The process wlms.exe has initiated the power off of computer COMPUTERNAME on behalf of user NT AUTHORITY\SYSTEM for the following reason: Other (Unplanned)
Reason Code: 0x0
Shutdown Type: power off
Comment:
Event Xml:
<event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<system>
<provider name="USER32">
<eventid qualifiers="32768">1074</eventid>
<level>4</level>
<task>0</task>
<keywords>0x80000000000000</keywords>
<timecreated systemtime="2013-08-08T16:39:04.000000000Z">
<eventrecordid>147067</eventrecordid>
<channel>System</channel>
<computer>COMPUTERNAME.local</computer>
<security userid="S-1-5-18">
</security></timecreated></provider></system>
<eventdata>
<data>wlms.exe</data>
<data>COMPUTERNAME</data>
<data>Other (Unplanned)</data>
<data>0x0</data>
<data>power off</data>
<data>
</data>
<data>NT AUTHORITY\SYSTEM</data>
</eventdata>
</event></pre>
<pre>
</pre>
A fast googling told me that in order to stop the VM from shutting down I needed to <b>activate </b>it :)Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-34832629006470241292013-04-30T16:43:00.000+01:002013-04-30T16:43:02.559+01:00The Ultimate Blog Challenge Has Finished<strike>Finally!</strike><br />
<br />
It has been really hard to find the time to write one post a day. I suppose it would also depend on the month and this one for me has been hectic. <strike>All of them are...</strike><br />
<strike><br /></strike>
I thought it was going to be harder to find topics to write about, the length and the amount of code in the posts have also decreased a lot during this month to be true.<br />
<br />
The idea of the challenge was that if you post often your blog will get more visits. I can tell you that's not true.<br />
<br />
I suppose, if you have a blog where people use to come and your posts are interesting you'll get them back more often if you post more, but in my case I only get a couple of visits from searches in Google <strike>Bing refuses to index the blog for some obscure reason</strike> and posting more doesn't really make a difference.<br />
<br />
Writing about topics that are very actual helps. But if you are busy working in your projects a 125% of your time you don't get to do that many interesting things... I would really like to change that.<br />
<br />
In the end the results of the Ultimate Blog Challenge in the visits of the blog look like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-JUJnWVtJM44ODXDTiOkJMOEobSsTaiuIr0R_gpHfWyi3ZQW-KzeewU7cRJHkl4Ff9gH5uyqlXJqoulSTzJTyOSeOi2mYRdVkuvmRRa0pMjlg5eCPVMzefrmEiwfdAtMMnOh2U1dUc_C_/s1600/ChallengeTrends.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-JUJnWVtJM44ODXDTiOkJMOEobSsTaiuIr0R_gpHfWyi3ZQW-KzeewU7cRJHkl4Ff9gH5uyqlXJqoulSTzJTyOSeOi2mYRdVkuvmRRa0pMjlg5eCPVMzefrmEiwfdAtMMnOh2U1dUc_C_/s320/ChallengeTrends.png" width="320" /></a></div>
<br />
The first two weeks I had a lot of visits, <strike>I suppose from my fellow bloggers in the challenge</strike>, after that the amount of attention the blog draws has decreased to the usual levels. <strike>You see that peak? Yes, that one was me changing the template.</strike><br />
<strike><br /></strike>
I have now a couple more of followers in twitter and not a single more subscription than I had last month.<br />
<strike><br /></strike>
Anyway I am happy to have been able to write the same amount I usually write in two years in just one month.<br />
<br />
I used to think a lot about what I was going to write in the blog and what wasn't interesting enough and now it has become more natural. I don't think I am going to write one post a day anymore <strike>or not until I get paid for it somehow</strike> but I will definitely write more often.<br />
<br />
I'm sorry for the twits and the messages in LinkedIn etc. <strike>In the other hand you don't hear from me that much in the social networks usually.</strike><br />
<strike><br /></strike>
Back to normal now.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-64380438467345730512013-04-30T16:11:00.000+01:002013-09-19T11:14:48.211+01:00How Much Are you Bringing Back In Your CAML Queries?I don't know you but I am bringing back too much.<br />
<br />
This is one of those things you don't notice until it's too late. I thought just adding very strict filters to the CAML Queries to bring back just the items you needed was enough but there's one more thing you can do.<br />
<br />
You can restrict the fields you are retrieving... and you should.<br />
<br />
The SQL Queries that SharePoint generates for retrieving the items when you let the ViewFields parameter of the CAML empty almost doubles the complexity of the one it generates when you specify the fields you want to query, or use a view.<br />
<br />
Two simple lines like these:<br />
<br />
<blockquote class="tr_bq">
query.ViewFields = "<FieldRef Name=\"Value\" />";<br />
query.ViewFieldsOnly = true;</blockquote>
Can make your query better.<br />
<br />
Who could resist to do it right when it's this simple?<br />
<br />Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-63788670168906462142013-04-30T15:36:00.001+01:002013-04-30T15:36:52.019+01:00Disposing every SPWeb you use... Is it necessary?I am becoming a bit paranoid about disposing lately. In example.<br />
<br />
Until now I would have used things like Web.ParentWeb and then forgotten about them. As I was not "Opening" them manually I was not disposing them either.<br />
<br />
Now I usually do things like this:<br />
<blockquote class="tr_bq">
<blockquote class="tr_bq">
using (SPWeb web = anotherWeb.ParentWeb)</blockquote>
<blockquote class="tr_bq">
{</blockquote>
<blockquote class="tr_bq">
//Do Whatever you need here with the parent web</blockquote>
<blockquote class="tr_bq">
}</blockquote>
</blockquote>
I haven't had the time to check whether this is reducing the memory leaks or just making my application more unstable (if I close the web while I am using it somewhere else it could cause an exception it the other part of the code)<br />
<br />
Do you think when you dispose a web it automatically disposes every parent web automatically?<br />
It could be...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-zHoF6qBqyiU/UX_Wehz3RHI/AAAAAAAAAPU/hCLaRPzMvMg/s1600/Close+Twice.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://3.bp.blogspot.com/-zHoF6qBqyiU/UX_Wehz3RHI/AAAAAAAAAPU/hCLaRPzMvMg/s320/Close+Twice.png" width="320" /></a></div>
<br />
<br />
I need to test this and once I do it I'll post about it.Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-60782819230121985682013-04-30T15:01:00.001+01:002013-04-30T15:01:40.301+01:00SharePoint Makes you HumbleI have learn this well enough during the last seven years. SharePoint is a hard beast to tame, so hard I have never met anyone working in this platform who claims to know everything about it <strike>actually if anyone had I would regard it as a proof of complete ignorance</strike>.<br />
<br />
In a world where trying beyond extenuation is common and the deadlines are pressing you find individuals whose levels of tolerance to frustration are sky high.<br />
<br />
You definitely need a sense of humor to be a SharePoint developer. <strike>I would even say a taste for black humor</strike>.<br />
<br />
I remember smiling back in the 2007 after selling a SharePoint based solution when the client asked my for the instructions manual of SharePoint.<br />
<br />
There are so many things working together in so many different places that it's really easy for some small and hidden piece to make the whole farm lose balance. <strike>Do you remember the books of the D'nis? something like that.</strike><br />
<br />
I am writing this because I am four posts short for completing the Ultimate Blog Challenge and it has been impossible for me to write on the last four days... but I won't give up. Not when I am so close.<br />
<br />
As a SharePoint Developer I can overcome any issue <strike>or workaround it</strike>Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0tag:blogger.com,1999:blog-5710617195189960819.post-62747730088264791532013-04-25T16:50:00.002+01:002013-04-25T16:50:54.120+01:00customErrors mode Doesn't Work in my ServerI have been receiving the usual:
<br />
<h1>
Server Error in '/' Application.
<hr color="silver" size="1" width="100%" />
</h1>
<h2>
<i>Runtime Error</i> </h2>
<span style="font-family: Arial, Helvetica, Geneva, SunSans-Regular, sans-serif;"><b>Description:
</b>An application error occurred on the server. The current custom error
settings for this application prevent the details of the application error from
being viewed. <br /><br /><b>Details:</b> To enable the details of this specific
error message to be viewable on the local server machine, please create a
<customErrors> tag within a "web.config" configuration file located in the
root directory of the current web application. This <customErrors> tag
should then have its "mode" attribute set to "RemoteOnly". To enable the details
to be viewable on remote machines, please set "mode" to "Off".<br /><br />
<table bgcolor="#ffffcc" style="width: 100%px;">
<tbody>
<tr>
<td><code></code><br />
<pre><code><!-- Web.Config Configuration File -->
<configuration>
<system.web>
<customErrors mode="RemoteOnly"/>
</system.web>
</configuration></code></pre>
<code>
</code></td></tr>
</tbody></table>
</span><br />
<br />
Good, I now how to solve this one, it's easy, you just have to go to C:\inetpub\wwwroot\wss\VirtualDirectories\80 and change the customErrors mode in the web.confing<br />
<br />
Well it didn't work.<br />
<br />
After trying a thousand things I found out that there's a more obscure web.config you need to change in SharePoint 2010, it's at c:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\<br />
<br />
After changing the set there everything worked as expected.<br />
<br />
It's funny it only happens in some servers I suppose it's one of the reasons why SharePoint is so funny.<br />
<br />
<br />Chanhttp://www.blogger.com/profile/17399215503516469208noreply@blogger.com0