I'm currently consuming information from Sharepoint 2010 using WCF Data Services from C# (an asp.net mvc4 application).
With regular Sharepoint lists items, when saving the changes to the context, the id of the new item is automatically populated on the original object. Meaning:
SomeContext context = /* creation of the context*/;
var someEntity = new SomeEntity {...};
context.AddToSomeEntityItems(someEntity);
context.SaveChanges();
var newId = someEntity.Id; //this will have the new id
However, if what you are creating is an item for a document library, the id doesn't seem to be updated on the saved entity. For example:
SomeContext context = /* creation of the context*/;
var someOtherEntity = new SomeOtherEntity {...};
Stream data = /* some stream*/;
context.AddToSomeOtherEntityItems(someOtherEntity);
context.SetSaveStream(someOtherEntity, data, true, "SomeMIMEType", "SomeSlugPath");
context.SaveChanges();
var newId = someOtherEntity.Id; //this will instead always have 0
I initially thought this was a bug in the first version of WCF Data Services so I updated to the latest, 5.4.0. But the behavior seems to be the same.
I would prefer to have to avoid strange lookups that ultimately could fail during heavy load, such as using .OrderByDescending(x => x.Id).First() to get the recently created item.
When looking at the actual network traffic using Fiddler, I can see that the initial upload of the binary data actually does return the information for the item along with its ID. And if I configure other relationships to that item using SetLink prior to saving changes they are linked correctly, so the context does handle the value accordingly.
Is there any way to get WCF Data Services to update the ID on the entity when the item is for a document library?
Typically, I set the Id manually before inserting it with
someOtherEntity.Id = Guid.NewGuid();
That way you can TDD it more easily as well. I think it is best to leave the responsibilities to your code than to rely upon an external storage system to tell you what the Id is.
What I am doing is parsing out the id from the response headers:
var responses = context.SaveChanges();
var created = responses.FirstOrDefault(r => r.StatusCode == (int)HttpStatusCode.Created);
if (response != null)
{
var location = response.Headers.First(h => h.Key == "Location").Value;
//location will be http://sharepoint.url/site/_vti_bin/ListData.svc/{listname}(id)
//you can parse out the id from the end of that string
}
It's not exactly elegant, but it works.
Related
I'm making a c# call to the Stripe.net API to fetch a balance history for a connected account. I'm trying to expand on the balance transaction object to see where the charge is coming from (ex. the customer who made the charge) as all the charges to connected accounts on my platform are from charge objects with a destination property to the connected account.
Here is my code and a screenshot of what the expanded source looks like, but think I should see a charge id or a customer or something refering me to the initial customer somewhere, but I don't...
var balanceService = new StripeBalanceService();
balanceService.ExpandSource = true;
var list = new List <string> () {
"data.source.source_transfer"
};
StripeList <StripeBalanceTransaction> balanceTransactions
= balanceService.List(
new StripeBalanceTransactionListOptions() {
Limit = 20,
Type = "payment",
Expand = list
},
new StripeRequestOptions() {
StripeConnectAccountId = accountId
}
);
foreach(var transaction in balanceTransactions) {
var test = transaction;
}
I feel like I should see a charge id (ex. ch_xxx) or a Customer value (which is null) all I see of any relevance is a payment id (ex. py_xxx)
It is possible to get the charge object(ch_xxx), it is just a little involved!
As you are using destination charges, the charge(ch_xxx) takes place on the platform account, and then a transfer(tr_xxx) is made to the connected account. That transfer creates a payment(py_xxx) on the connected account, which results in a balance transaction(txn_xxx).
As your code expands the source of those balance transactions, you get the payment(py_xxx). The payment is equivalent to a charge, so it has a source_transfer field. You can expand this field also! This will give you the transfer object(tr_xxx). Finally, the transfer has a source_transaction field, and this can be exapanded to give the original charge(ch_xxx)!
Putting that all together, you will want to expand on "data.source.source_transfer.source_transaction".
If you use a Stripe library in a dynamic language you can see this in action ... unfortunately, stripe-dotnet has an open issue right now which means that you can not do this directly. Instead, you will need to make the API calls manually by calling the various Retrieve functions on the IDs, instead of doing a single expansion. It would look something like this:
var paymentId = transaction.Source.Id;
var chargeService = new StripeChargeService();
var payment = chargeService.Get(
paymentId,
new StripeRequestOptions()
{
StripeConnectAccountId = accountId
}
);
var transferService = new StripeTransferService();
transferService.ExpandSourceTransaction = true;
var transfer = transferService.Get(payment.SourceTransferId);
var charge = transfer.SourceTransaction;
Console.WriteLine(charge.Id);
I'm working on a project that uses EFCore 2.1.0-preview1-final code first approach. Like in EF6 (and previous versions) I want to ensure the compatibility of my DbContext (and models) to the database.
In EF6 it was enabled by default and it was possible to deactivate it with Database.CompatibleWithModel(false);. As far as I know EF uses the __MigrationHistory table where the model information was stored. EFCore has no such column in __EFMigrationsHistory table that could provide such information.
I cannot find any information about compatibility check in EFCore. But I want to ensure the compatibility, because after some tests it seems not to be enabled by default (or does exist). I tested it by adding and deleting some columns from database manually and executing the application after the modifications. I - against my expectation - received no exception.
Does anybody know how to achieve a compatibility check from model to database and vice versa like in EF6 for EFCore?
Or could provide some helpful links for further information about it or why it doesn't exist in EFCore (because it is not necessary)?
I strongly advise against doing this since it uses internal components and is error-prone, but here's one way to do it.
using (var db = new MyDbContext())
{
var reporter = new OperationReporter(handler: null);
var designTimeServiceCollection = new ServiceCollection()
.AddSingleton<IOperationReporter>(reporter)
.AddScaffolding(reporter);
new SqlServerDesignTimeServices().ConfigureDesignTimeServices(designTimeServiceCollection);
var designTimeServices = designTimeServiceCollection.BuildServiceProvider();
var databaseModelFactory = designTimeServices.GetService<IScaffoldingModelFactory>();
var databaseModel = (Model)databaseModelFactory.Create(
db.Database.GetDbConnection().ConnectionString,
tables: new string[0],
schemas: new string[0],
useDatabaseNames: false);
var currentModel = db.Model;
// Fix up the database model. It was never intended to be used like this. ;-)
foreach (var entityType in databaseModel.GetEntityTypes())
{
if (entityType.Relational().Schema == databaseModel.Relational().DefaultSchema)
{
entityType.Relational().Schema = null;
}
}
databaseModel.Relational().DefaultSchema = null;
databaseModel.SqlServer().ValueGenerationStrategy =
currentModel.SqlServer().ValueGenerationStrategy;
// TODO: ...more fix up as needed
var differ = db.GetService<IMigrationsModelDiffer>();
if (differ.HasDifferences(databaseModel, currentModel))
{
throw new Exception("The database and model are out-of-sync!");
}
}
I'm using C# to work with AD (Win 2012R2).
We are syncing AD users,groups and their relationship to SQL database.
Full sync works well.
But when using synchronization cookie, the relationship changes does not detected.
What may be the reason?
Thanks.
Here is my code:
public void DirSyncChanges(DirectoryEntry de, byte[] cookie)
{
DirectorySynchronization syncData = new DirectorySynchronization(cookie);
srch = new DirectorySearcher(de)
{
Filter = "(&(objectClass=user)(objectCategory=person))",
SizeLimit = Int32.MaxValue,
Tombstone = true
};
srch.DirectorySynchronization = syncData;
syncData.Option = DirectorySynchronizationOptions.None;
using(SearchResultCollection results = srch.FindAll())
foreach (SearchResult res in results)
{
//results is empty. no loop
}
}
Please specify the DirectorySearcher.PropertiesToLoad. Only if any of the attributes in PropertiesToLoad are updated, you will get them in delta sync.
As i remember the search root of DirSync must be naming context root object.
Better use paged search. No matter how large the value you set to SizeLimit. It will only return at most 1000 or 1500 (forgot exact number) results.
My answer is based on .NET 3.5.
I've been able to integrate some scheduled web service calls into my Umbraco site that uses the response from the web service to update some of the content on my site. I can now handle text and some various other content but my main query is how should I deal with images that are delivered from the web service in byte[] format?
For a little context, the site I am developing uses web service calls to retreive the details of a product which users of our desktop software have created on their machine. Each of these products is pulled via a web service call into my Umbraco site and created as an individual product page under the parent node of products.
Products > Product
Each product has several properties such as an ID, a name, notes and a collection of images. Once I have called my web service I am creating these pages using the following code:
//Construct Service Request
svc = new StaticDataWebServiceSoapClient();
var response = svc.GetProducts(ref compressionSoapHeader,ref managedUserHeader);
//Umbraco Content Services
int rootID = 1117;
IContentService cs = ApplicationContext.Current.Services.ContentService;
var remove = cs.GetChildren(rootID).Where(x => x.ContentType.Alias == "product");
foreach (var child in remove)
{
cs.Delete(child);
}
foreach(var product in response){
var item = cs.CreateContent(product.ContinentName, rootID, "product");
//Set the properties using the SetValue method, passing in the alias of the property and the value to assign to it
item.SetValue("continentId", product.Id);
item.SetValue("continentName", product.ProductName);
item.SetValue("continentNotes", product.Notes);
foreach (var image in product.Images)
{
??? destination.SetValue("ProductImages", image._Image); ???
image.Caption;
image.FileName;
image.ImageId;
image.KeywordID;
}
cs.SaveAndPublishWithStatus(item);
}
As you can see from the code, each product has several images associated with it that I would also like to pull into the site and associate with the product page that is being created. How would I go about doing this? Would I need to use the Media Service and a specific datatype or would this structure fit easily into a multiple media picker?
You may find it easiest to loop through the images once you've retrieved them and create a new Media item for each of them, and then associate them with the product using a property based on something like the Multiple Media Picker datatype as you noted.
Because this data type stores it's values as a comma separated list of id's you could use something like the following:
// Setup
IContentService cs = ApplicationContext.Current.Services.ContentService;
var mediaService = ApplicationContext.Current.Services.MediaService;
int mediaRootId = 1111; // Replace with the id of your media parent folder
// Replace the image looping logic with the following:
// MultiMediaPicker stores it's values as a commma separated string of ids.
string imageIds = string.Empty;
foreach (var image in product.Images)
{
var fileName = image.FileName; // Assumes no path information, just the file name
var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower();
if (!UmbracoConfig.For.UmbracoSettings().Content.DisallowedUploadFiles.Contains(ext))
{
var mediaType = Constants.Conventions.MediaTypes.File;
if (UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.Contains(ext))
mediaType = Constants.Conventions.MediaTypes.Image;
var f = mediaService.CreateMedia(fileName, mediaRootId, mediaType);
// Assumes the image._Image is a Stream - you may have to do some extra work here...
f.SetValue(Constants.Conventions.Media.File, fileName, (Stream)image._Image); // Real magic happens here.
mediaService.Save(f);
imageIds += (imageIds.Length > 0 ? "," : "") + f.Id;
}
}
// Get the relevant property on the new item and save the image list to it.
var imagesProp = item.Properties.Where(p => p.Alias == "productImages").FirstOrDefault();
imagesProp.Value = imageIds;
Note - I haven't tested this code out, so ymmv.
i'm trying to import some legacy poll data into our sitecore solution. For part of the import, i'm trying to create new sitecore items to hold the data based off of a master page we already have set up for polls. this master has a couple of default items set up underneath of it, and I want to delete a couple of specific default items before I add the legacy poll data. however, when i attempt to delete one of the default items using item.DeleteChildren(), i get a NullReferenceException thrown by Sitecore.Tasks.ItemEventHandler.OnItemDeleted(Object sender, EventArgs args) in the sitecore kernel. if anyone has any idea what could be causing this, i'd appreciate it. we're on sitecore version 5.3.2.
here is the code i'm using to attempt to create/edit the item based off of a master. the creation all works perfectly, it's the DeleteChildren() call that doesn't work.
Guid LegacyPollFolderGuid = new Guid("8AE89A44-9DCD-4AC2-B0F3-DD438188A575");
Guid QuizOMaticMasterGuid = new Guid("74B95ABF-1898-4870-8B4F-50AF0078AE22");
var master = Sitecore.Configuration.Factory.GetDatabase("master");
var root = master.GetItem(new Sitecore.Data.ID(LegacyPollFolderGuid));
var quizMasterTemplate = master.Masters[new Sitecore.Data.ID(QuizOMaticMasterGuid)];
var quizPage = root.Add("Test Quiz", quizMasterTemplate);
if (quizPage != null)
{
var quiz = quizPage.Children["Column One"].Children["QuizOMatic"];
if (quiz != null)
{
var questionFolder = quiz.Children["Questions"];
var questionTemplate = questionFolder.Children[0].Template;
var resultsFolder = quiz.Children["Results"];
var linksFolder = quiz.Children["Links"];
using (new Sitecore.SecurityModel.SecurityDisabler())
{
questionFolder.DeleteChildren();
}
}
I've built a tool with the same functionality; removing all items under a specific folder. I got the same error, but I saw items being deleted. So I reran the tool multiple times and eventually all items were deleted.