I am trying to create a new product with a plugin, but I get this exception:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
This is the code to create the product
EntityReference ugRef = new EntityReference(ug.LogicalName, ug.UoMScheduleId.Value);
EntityReference uRef = new EntityReference(u.LogicalName, u.UoMId.Value);
Product product = new Product()
{
Name = pName,
ProductNumber = pNumber,
QuantityDecimal = 2,
DefaultUoMScheduleId = ugRef,
DefaultUoMId = uRef
};
service.Create(product);
All variables have been tested, they all have values. The unit is correct for the unit group - if I change either I get an exception saying as much.
The problem is definitely with this piece of the code as there is a lovely lead with the expected 1st and last name when the code is altered to this:
EntityReference ugRef = new EntityReference(ug.LogicalName, ug.UoMScheduleId.Value);
EntityReference uRef = new EntityReference(u.LogicalName, u.UoMId.Value);
Lead l = new Lead();
l.FirstName = uRef.Id.ToString();
l.LastName = uRef.LogicalName;
service.Create(l);
/*
Product product = new Product()
{
Name = (String)staged.Attributes["wishlist_name"],
ProductNumber = (String)staged.Attributes["wishlist_barcode"],
QuantityDecimal = 2,
DefaultUoMScheduleId = ugRef,
DefaultUoMId = uRef
};
service.Create(product);
*/
pName and pNumber are strings.
u and ug are a Unit and a Unit Group.
I changed the code to:
query = new QueryByAttribute("uom");
query.ColumnSet = new ColumnSet("name", "uomscheduleid");
query.Attributes.AddRange("name");
query.Values.AddRange("1");
UoM unit = (UoM)service.RetrieveMultiple(query).Entities[0];
Product newProduct = new Product
{
ProductNumber = "1t2y3u",
Name = "Example Banana Product",
QuantityDecimal = 1,
DefaultUoMScheduleId = unit.UoMScheduleId,
DefaultUoMId = unit.ToEntityReference()
};
service.Create(newProduct);
The same error is thrown.
I am about to strip my moer with this.
Couple things to look at.
It looks like your tried to simplify your code in the first example, but may have removed the source of your bug, but luckily, you added it to your last example :) I'm guessing staged does not contain "wishlist_name", and therefor is giving you the error you see. You should always use the typed GetAttributeValue method defined in the Entity class: staged.GetAttributeValue<String>("wishlist_name"). It will perform a null check and return the default for the type.
Check all of your other plugins, to see if another plugin is fired upon the creation of the Product, that is possibly doing some extra logic if the DefaultUoMScheduleId or DefaultUoMId is populated. Your create in this plugin could be getting an error from another "nested" plugin.
Instead of creating temporary Entity Reference variables, use the ToEntityReference() method defined in the entity class, it makes the code look a little cleaner IMHO.
Product product = new Product()
{
Name = (String)staged.Attributes["wishlist_name"],
ProductNumber = (String)staged.Attributes["wishlist_barcode"],
QuantityDecimal = 2,
DefaultUoMScheduleId = ug.ToEntityReference(),
DefaultUoMId = u.ToEntityReference()
};
Related
I am using Migrations in an MVC 4 EF5 application in Visual Studio 2012 Express with SQL Server 2012 Express, using Code First.
I use the Seed method in configuration.cs, firstly creating a Tags table. When I execute 'Update-Database -verbose -force' from Package Manager, it works correctly and doesn't create duplicate tags - and re-creates them if deleted:
db.Tags.AddOrUpdate(
t => t.Name,
new Tag { Name = "Bakery", NamePlural = "Bakeries" },
new Tag { Name = "Bar", NamePlural = "Bars" },
new Tag { Name = "Bookshop", NamePlural = "Bookshops" }
);
db.SaveChanges();
I then try and add related Places data:
db.Places.AddOrUpdate(
p => p.Name,
new Place
{
Name = "Shoreditch Grind",
URL = "shoreditch-grind-cafe",
Address = "213 Old St",
City = "London",
PostCode = "EC1V 9NR",
Website = "www.shoreditchgrind.com",
Phone = "020 7490 0101",
About = "Good coffee on the Silicon Roundabout",
Image = "noimage.png",
Tag = db.Tags.Single(t => t.Name == "Bar")
},
new Place
{
Name = "The Old Blue Last",
URL = "old-blue-last-pub",
Address = "38 Great Eastern St",
City = "London",
PostCode = "EC2A 3ES",
Website = "www.theoldbluelast.com",
Phone = "020 7739 7033",
About = "Pub of Vice Magazine",
Image = "noimage.png",
Tag = db.Tags.Single(t => t.Name == "Bakery")
}
);
This however creates duplicates, adding all the places again every time I execute 'Update-Database -verbose -force'
I'm new to MVC - and I also don't fully understand what this does:
p => p.Name,
I have a feeling perhaps I should be manually adding ID values to each object?
How can I run this without creating duplicate Places?
It would also be useful to be able to mark each Tag.Name as unique simply.
Thanks.
This may work:
var place = new Place
{
Name = "The Old Blue Last",
URL = "old-blue-last-pub",
Address = "38 Great Eastern St",
City = "London",
PostCode = "EC2A 3ES",
Website = "www.theoldbluelast.com",
Phone = "123 456 789", // updated number
About = "Pub of Vice Magazine",
Image = "noimage.png",
TagID = db.Tags.Single(t => t.Name == "Bakery").TagID
};
db.Places.AddOrUpdate(p => p.Name, place);
db.SaveChanges();
Since "The Old Blue Last" is already there, and we've updated based on p.Name, it should only update that entry changing Phone to "123 456 789". This similar to what you have tried, but may work. See more here.
You also mentioned that you are not sure what p => p.Name does. The => is called a Lambda Expression. It is an anonymous function. It is a method without a declaration, access modifier, return type, name etc. It's a short hand expression that allows you to write a method in the place you are going to use it.
See more here and here..
In our application, we create a few thousand phonecall records. Each phonecall should have a different owner, determined by a method named GetAnyAppropriateSystemUser(), which finds some random SystemUser based on some criteria.
In the code example below, we create a phonecall, and later use AssignRequest on it to specify its owner.
PhoneCall phoneCall = new PhoneCall();
//
// stuff to set up the new PhoneCall instance here; populate fields, etc...
//
// determine this phonecall's owner through some algorithm
Guid appropriateOwner = GetAnyAppropriateSystemUser();
Guid createdPhoneCallId = _serviceProxy.Create(phoneCall);
if (createdPhoneCallId != Guid.Empty)
{
AssignRequest phoneCallAssign = new AssignRequest();
phoneCallAssign.Assignee = new EntityReference(SystemUser.EntityLogicalName, appropriateOwner);
phoneCallAssign.Target = new EntityReference(PhoneCall.EntityLogicalName, createdPhoneCallId);
_serviceProxy.Execute(phoneCallAssign);
}
This works allright, but there are two calls, one to create, and one to assign. Is it ok to just set "ownerid" of the PhoneCall record before calling Create() method, thus eliminating the need to call an AssignRequest later? It seems to work, and I even found an example doing a similar thing in the SDK, as shown below.
SDK Sample: Roll Up Goal Data for a Custom Period Against the Target Revenue
// Create three goals: one parent goal and two child goals.
Goal parentGoal = new Goal()
{
Title = "Parent Goal Example",
RollupOnlyFromChildGoals = true,
ConsiderOnlyGoalOwnersRecords = true,
TargetMoney = new Money(300.0M),
IsFiscalPeriodGoal = false,
MetricId = new EntityReference
{
Id = _metricId,
LogicalName = Metric.EntityLogicalName
},
GoalOwnerId = new EntityReference
{
Id = _salesManagerId,
LogicalName = SystemUser.EntityLogicalName
},
OwnerId = new EntityReference
{
Id = _salesManagerId,
LogicalName = SystemUser.EntityLogicalName
},
GoalStartDate = DateTime.Today.AddDays(-1),
GoalEndDate = DateTime.Today.AddDays(30)
};
_parentGoalId = _serviceProxy.Create(parentGoal);
Although it seems to work, are there anything that we must be aware of if we set ownerid before creating the new record? Are there any differences?
Thank you very much in advance.
As you already found is allowed to set the ownerid when you create the record.
But is not possible to edit the owner of an existing record in the same way, in that case you must use the AssignRequest.
Check also this question:
ETL Software, can't retrieve owner of a contact
For example I have a list called Product and it has 3 columns, ProductName (which is the Title), ProductPrice and ProductType.
ProductName is a string
ProductPrice is a currency (double)
ProductType is a LookUp on ProductTypes List
Normally this is easy for me if it does not contain a LookUp column, but I dont know how to deal with look up columns when Inserting.
I had tried this one but it returns an error Specified cast is not valid.
Here is the current code
EntityList<ProductTypeItem> ProductTypes = dc.GetList<ProductTypeItem>("ProductType");
ProductItem newProduct = new ProductItem();
newProduct.Title = txtProductName.Text;
newProduct.ProductPrice = double.Parse(txtProductPrice.Text);
newProduct.ProductType = (from a in ProductTypes where a.Title == ddProductType.SelectedItem.Text select a).FirstOrDefault();
dc.Product.InsertOnSubmit(newProduct);
dc.SubmitChanges();
What would I do with the newProduct.ProductType as here is where the error occurs.
Please note that the ddProductType DataSource is the ProductType List and uses Title in its DataTextField and DataValueField
This might help you out. The first example explains how the insert should work with links to existing data. This sample code should give you enough hints to help you fix your problem:
AdventureWorksDataContext db = new AdventureWorksDataContext();
// LINQ query to get StateProvince
StateProvince state = (from states in db.StateProvinces
where states.CountryRegionCode == "AU" && states.StateProvinceCode == "NSW"
select states).FirstOrDefault();
// LINQ function to get AddressType
AddressType addrType = db.AddressTypes.FirstOrDefault(s => s.Name == "Home");
Customer newCustomer = new Customer()
{
ModifiedDate= DateTime.Now,
AccountNumber= "AW12354",
CustomerType='I',
rowguid= Guid.NewGuid(),
TerritoryID= state.TerritoryID // Relate record by Keys
};
Contact newContact = new Contact()
{
Title = "Mr",
FirstName = "New",
LastName = "Contact",
EmailAddress = "newContact#company.com",
Phone = "(12) 3456789",
PasswordHash= "xxx",
PasswordSalt= "xxx",
rowguid = Guid.NewGuid(),
ModifiedDate = DateTime.Now
};
Individual newInd = new Individual()
{
Contact= newContact, // Relate records by objects (we dont actually know the Keys for the new records yet)
Customer= newCustomer,
ModifiedDate= DateTime.Now
};
Address newAddress = new Address()
{
AddressLine1= "12 First St",
City= "Sydney",
PostalCode= "2000",
ModifiedDate=DateTime.Now,
StateProvince= state,
rowguid = Guid.NewGuid()
};
// Link our customer with their address via a new CustomerAddress record
newCustomer.CustomerAddresses.Add(new CustomerAddress() { Address = newAddress, Customer = newCustomer, AddressType = addrType, ModifiedDate = DateTime.Now, rowguid = Guid.NewGuid() });
// Save changes to the database
db.SubmitChanges();
I'm not familiar with Linq to SharePoint, but I assume it is similar to the Client Object model. If so, you'll need to use a FieldLookupValue for the value of newProduct.ProductType and use the ID of the lookup as the value:
newProduct.ProductType = new FieldLookupValue { LookupId = 1 };
This means you'll need to have access to the lookup value's ListID in your ProductTypes query.
The way you are doing seems correct to me. It is the same way I've been doing it with success. Are you sure the problem is the lookup column? Is double the correct type for the currency? Often currency is saved as a decimal, not a double.
By the way, you don't have to get the Entitylist separately. SPMetal makes the shorthand dc.ProductType, like the one you already use in the insertOnSubmit. No idea why all examples do this...
Try to split the assignments a bit and debug again. See if everything is the way it should be.
ProductItem newProduct = new ProductItem();
string selectedProductType = ddProductType.SelectedItem.Text;
ProductTypeItem productType = (from a in dc.ProductType
where a.Title == selectedProductType
select a).FirstOrDefault();
newProduct.Title = txtProductName.Text;
newProduct.ProductPrice = decimal.Parse(txtProductPrice.Text);
newProduct.ProductType = productType;
dc.Product.InsertOnSubmit(newProduct);
dc.SubmitChanges();
Hope this helps
It works now and here is the solution
EntityList<Item> ProductTypes = dc.GetList<Item>("ProductType");
Item oProductType = (from a in ProductTypes where a.Title == ddProductType.SelectedItem.Text select a).FirstOrDefault();
ProductItem newProduct = new ProductItem();
newProduct.Title = txtProductName.Text;
newProduct.ProductPrice = double.Parse(txtProductPrice.Text);
newProduct.ProductType = (ProductTypeItem)oProductType;
dc.Product.InsertOnSubmit(newProduct);
dc.SubmitChanges();
The only thing I changed is to initialize the the Product Type as Item rather than ProductTypeItem, then cast it to ProductTypeItem now it works
The LINQ to SharePoint generated code is out of sync with you list. Re-generate the list and it will work -Paul Beck
hiya, i have the following code but when i try and create a new IQuerable i get an error that the interface cannot be implemented, if i take away the new i get a not implemented exception, have had to jump back and work on some old ASP classic sites for past month and for the life of me i can not wake my brain up into C# mode.
Could you please have a look at below and give me some clues on where i'm going wrong:
The code is to create a list of priceItems, but instead of a categoryID (int) i am going to be showing the name as string.
public ActionResult ViewPriceItems(int? page)
{
var crm = 0;
page = GetPage(page);
// try and create items2
IQueryable<ViewPriceItemsModel> items2 = new IQueryable<ViewPriceItemsModel>();
// the data to be paged,but unmodified
var olditems = PriceItem.All().OrderBy(x => x.PriceItemID);
foreach (var item in olditems)
{
// set category as the name not the ID for easier reading
items2.Concat(new [] {new ViewPriceItemsModel {ID = item.PriceItemID,
Name = item.PriceItem_Name,
Category = PriceCategory.SingleOrDefault(
x => x.PriceCategoryID == item.PriceItem_PriceCategory_ID).PriceCategory_Name,
Display = item.PriceItems_DisplayMethod}});
}
crm = olditems.Count() / MaxResultsPerPage;
ViewData["numtpages"] = crm;
ViewData["curtpage"] = page + 1;
// return a paged result set
return View(new PagedList<ViewPriceItemsModel>(items2, page ?? 0, MaxResultsPerPage));
}
many thanks
you do not need to create items2. remove the line with comment try and create items2. Use the following code. I have not tested this. But I hope this works.
var items2 = (from item in olditems
select new ViewPriceItemsModel
{
ID = item.PriceItemID,
Name = item.PriceItem_Name,
Category = PriceCategory.SingleOrDefault(
x => x.PriceCategoryID == item.PriceItem_PriceCategory_ID).PriceCategory_Name,
Display = item.PriceItems_DisplayMethod
}).AsQueryable();
I have a Store that contains a list of Products:
var store = new Store();
store.Products.Add(new Product{ Id = 1, Name = "Apples" };
store.Products.Add(new Product{ Id = 2, Name = "Oranges" };
Database.Save(store);
Now, I want to edit one of the Products, but with a transient entity. This will be, for example, data from a web browser:
// this is what I get from the web browser, this product should
// edit the one that's already in the database that has the same Id
var product = new Product{ Id = 2, Name = "Mandarin Oranges" };
store.Products.Add(product);
Database.Save(store);
However, trying to do it this way gives me an error:
a different object with the same identifier value was already associated with the session
The reason is because the store.Products collection already contains an entity with the same Id. How do I get around this problem?
Instead of trying to merge the transient instance. Why not start with the actual instance...simply get the product by id, update the fields, and commit.
var product = session.Get<Product>(2);
product.Name = "Mandarin Oranges";
tx.Commit();
or the merge way...
var product = new Product{ Id = 2, Name = "Mandarin Oranges" };
var mergedProduct = (Product) session.Merge(product);
tx.Commit();
I'm not 100% positive in this case without more context, but a session merge might work.
http://ayende.com/Blog/archive/2009/11/08/nhibernate-ndash-cross-session-operations.aspx
Maybe You should call Database.SaveOrUpdate(store); instead of pure Save(store) ?