Seeding data in Configuration.cs, creating duplicate data with AddOrUpdate - c#

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..

Related

asp.net / EntityFramework - Why does my application pool crashes?

I'm trying to add a new method to my API.
The method's goal is to return a list of Partenaires having the given Prestation in their prestations.
When I'm calling the method by a GET request, the application pool of my API crashes. In the event logs, I have a warning called Microsoft-Windows-WAS and the linked error is:
A process serving application pool 'UphairApi2' suffered a fatal communication error with the Windows Process Activation Service. The process id was '3960'. The data field contains the error number.
And the network developer tool says that it failed to load response data.
Failed to load response data
When I'm returning return db.Partenaires.Where(p => p.PartenairePrestations.Any(pp => pp.Prestation.NomPrestation == prestation.Value)).ToString();, here's the returned request:
"SELECT\r\nExtent1.IdPartenaire, \r\nExtent1.FirstName, \r\nExtent1.LastName, \r\nExtent1.Email, \r\nExtent1.Password, \r\nExtent1.PasswordSalt, \r\nExtent1.Type, \r\nExtent1.Pays, \r\nExtent1.Ville, \r\nExtent1.CodePostale, \r\nExtent1.Adresse, \r\nExtent1.Lat, \r\nExtent1.Lng, \r\nExtent1.ImageUrl, \r\nExtent1.CouvertureUrl, \r\nExtent1.DateNaissance, \r\nExtent1.ADomicile, \r\nExtent1.SeDeplace, \r\nExtent1.DateAjout, \r\nExtent1.AdresseComplement, \r\nExtent1.TelMobile, \r\nExtent1.ValidationAutomatique, \r\nExtent1.NotifEmailMessage, \r\nExtent1.NotifEmailReservation, \r\nExtent1.NotifEmailPaiement, \r\nExtent1.NotifEmailNewsletter, \r\nExtent1.NotifSmsMessage, \r\nExtent1.NotifSmsReservation, \r\nExtent1.IdUserMango, \r\nExtent1.Iban, \r\nExtent1.TitulaireCompte, \r\nExtent1.IdWallet, \r\nExtent1.IdAccount, \r\nExtent1.Valide, \r\nExtent1.Son, \r\nExtent1.Push, \r\nExtent1.IdPhone\r\nFROM Partenaire AS Extent1\r\n WHERE EXISTS(SELECT\r\n1 AS C1\r\nFROM PartenairePrestation AS Extent2 INNER JOIN Prestation AS Extent3 ON Extent2.IdPrestation = Extent3.IdPrestation\r\n WHERE (Extent1.IdPartenaire = Extent2.IdPartenaire) AND ((Extent3.NomPrestation = #p__linq__0) OR ((Extent3.NomPrestation IS NULL) AND (#p__linq__0 IS NULL))))"
And the equivalent for Mysql Workbench:
SELECT Extent1.IdPartenaire, Extent1.FirstName, Extent1.LastName, Extent1.Email, Extent1.Password, Extent1.PasswordSalt, Extent1.Type, Extent1.Pays, Extent1.Ville, Extent1.CodePostale, Extent1.Adresse, Extent1.Lat, Extent1.Lng, Extent1.ImageUrl, Extent1.CouvertureUrl, Extent1.DateNaissance, Extent1.ADomicile, Extent1.SeDeplace, Extent1.DateAjout, Extent1.AdresseComplement, Extent1.TelMobile, Extent1.ValidationAutomatique, Extent1.NotifEmailMessage, Extent1.NotifEmailReservation, Extent1.NotifEmailPaiement, Extent1.NotifEmailNewsletter, Extent1.NotifSmsMessage, Extent1.NotifSmsReservation, Extent1.IdUserMango, Extent1.Iban, Extent1.TitulaireCompte, Extent1.IdWallet, Extent1.IdAccount, Extent1.Valide, Extent1.Son, Extent1.Push, Extent1.IdPhone FROM Partenaire AS Extent1 WHERE EXISTS(SELECT 1 AS C1 FROM PartenairePrestation AS Extent2 INNER JOIN Prestation AS Extent3 ON Extent2.IdPrestation = Extent3.IdPrestation WHERE (Extent1.IdPartenaire = Extent2.IdPartenaire) AND ((Extent3.NomPrestation = 'Barbe')))
I tested this request in MysqlWorkbench and a set of datas is well returned.
Here's my method:
// GET: api/Partenaires_prestations
[Authorize]
[Route("api/Partenaires_prestations")]
public List<PartenaireMapItem> GetPartenairesWithPrestations() {
Random rnd = new Random();
var queryString = Request.GetQueryNameValuePairs();
var prestation = queryString.FirstOrDefault();
return db.Partenaires.Where(p => p.PartenairePrestations.Any(pp => pp.Prestation.NomPrestation == prestation.Value))
.ToList()
.Select(p => new PartenaireMapItem {
IdPartenaire = p.IdPartenaire,
FirstName = p.FirstName,
LastName = p.LastName,
NomComplet = p.LastName.Substring(0,1).ToUpper() + ". " + p.FirstName,
Type = p.Type,
DureeMin = 50,
Lat = p.Lat,
Lng = p.Lng,
ImageUrl = p.ImageUrl,
SeDeplace = p.SeDeplace,
ADomicile = p.ADomicile,
Notes = p.NoteClientPartenaires,
Prestations = p.PartenairePrestations.Select(y => y.Prestation.NomPrestation).ToList();
}).ToList();
}
What am I doing wrong ?
Any help would be appreciated since I couldn't find another related thread on internet.
You will probably need to debug the API and specify more detail to help narrow down a cause. A couple of things I can see:
var prestation = queryString.FirstOrDefault();
// Handle when prestation comes back #null. Is that valid?
var results = db.Partenaires.Where(p => p.PartenairePrestations.Any(pp => pp.Prestation.NomPrestation == prestation.Value))
// .ToList() // Avoid .ToList() here... Select the entity properties you need.
.Select(p => new PartenaireMapItem {
IdPartenaire = p.IdPartenaire,
FirstName = p.FirstName,
LastName = p.LastName,
// NomComplet = p.LastName.Substring(0,1).ToUpper() + ". " + p.FirstName, // Remove. Make this a computed property in your view model.
Type = p.Type,
// DureeMin = 50, // Can remove, can be a computed property.
Lat = p.Lat,
Lng = p.Lng,
ImageUrl = p.ImageUrl,
SeDeplace = p.SeDeplace, // Ok if a String/value.
ADomicile = p.ADomicile, // Ok if a String/value.
Notes = p.NoteClientPartenaires, // Ok if a String/value.
Prestations = p.PartenairePrestations.Select(y => y.Prestation.NomPrestation).ToList(); // Assuming this is retrieving the names of presentations. List<string>.
}).ToList();
return results;
The early .ToList() was required because you were attempting to compute values (NameComplet) in the Linq expression that normally would have been fed to EF which your DB provider will not understand. For efficiency, only select mapped properties, and instead change any computed values to read-only properties on your view model. (PartenaireMapItem)
private string _nomComplet = null;
public string NomComplet
{
get { return _nomComplet ?? (_nomComplet = LastName.Substring(0,1).ToUpper() + ". " + FirstName); }
}
That example buffers the result assuming that the name details are read-only. If First/Last name can be updated, just return the calculated name each time.
The other properties should be fine, assuming that SeDeclace/ADomicile are string values and not child entities. The same goes for the list of Prestations. A list of strings for the Prestation names should be fine.
The other minor change I made was to retrieve the view models in a variable to inspect prior to returning. This better facilitates using a breakpoint to inspect the results before returning. From here determine if any error is coming back from the computation of the results, or something else such as serializing the resulting view models back to the client.

Entity Framework Core SQLite - Simulate Eager Loading in test

I have a repository that loads my entity (City) and its related data (PointsOfInterest)
public City GetCity(int cityId, bool includePointsOfInterest)
{
var city = _context.Cities.SingleOrDefault(x => x.Id == cityId);
if (includePointsOfInterest)
{
_context.Entry(city)
.Collection(x => x.PointsOfInterest)
.Load();
}
return city;
}
To test this method, I decided to go with SQLLite InMemory, as I could test the Eager load functionality.
Setup of the context:
SqliteConnection connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<CityInfoContext>()
.UseSqlite(connection)
.Options;
var context = new CityInfoContext(options);
var cities = new List<City>()
{
new City()
{
Id = 1,
Name = "New York City",
Description = "The one with that big park.",
PointsOfInterest = new List<PointOfInterest>()
{
new PointOfInterest()
{
Id = 1,
Name = "Central Park",
Description = "The most visited urban park in the United States."
},
new PointOfInterest()
{
Id = 2,
Name = "Empire State Building",
Description = "A 102-story skyscraper located in Midtown Manhattan."
}
}
}
}
context.Cities.AddRange(cities);
context.SaveChanges();
But looks like the SQLite always load its related data, which makes sense, since it is already in memory. But since it is supposed to simulate a Relational Database, is there a way to make it not load the related data automatically?
If not, how can I effectively test it? Should I go for in disk SQLite for my repository tests?
(I'm using EF in memory provider to test code that depends on the Repository)
Are you testing against the same instance of your context and the same DbSet collection you just inserted? If you are, the objects are there because you've just inserted them a step earlier, still in the graph.
Try querying your context like:
var c1 = context.Set<City>().AsQueryable().FirstOrDefault();
// assuming you have initialized the PointsOfInterest coll. in City.cs
Assert.Empty(c1.PointsOfInterest);
You can now apply the same access as _context.Set<City>().AsQueryable() in your repository.

CRM 2011 Create Product Plugin

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()
};

LINQ to Sharepoint InsertOnSubmit Question

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

How do I order a sql datasource of uniqueidentifiers in Linq by an array of uniqueindentifiers

I have a string list(A) of individualProfileId's (GUID) that can be in any order(used for displaying personal profiles in a specific order based on user input) which is stored as a string due to it being part of the cms functionality.
I also have an asp c# Repeater that uses a LinqDataSource to query against the individual table. This repeater needs to use the ordered list(A) to display the results in the order specified.
Which is what i am having problems with. Does anyone have any ideas?
list(A)
'CD44D9F9-DE88-4BBD-B7A2-41F7A9904DAC',
'7FF2D867-DE88-4549-B5C1-D3C321F8DB9B',
'3FC3DE3F-7ADE-44F1-B17D-23E037130907'
Datasource example
IndividualProfileId Name JobTitle EmailAddress IsEmployee
3FC3DE3F-7ADE-44F1-B17D-23E037130907 Joe Blo Director dsd#ad.com 1
CD44D9F9-DE88-4BBD-B7A2-41F7A9904DAC Maxy Dosh The Boss 1
98AB3AFD-4D4E-4BAF-91CE-A778EB29D959 some one a job 322#wewd.ocm 1
7FF2D867-DE88-4549-B5C1-D3C321F8DB9B Max Walsh CEO 1
There is a very simple (single-line) way of doing this, given that you get the employee results from the database first (so resultSetFromDatabase is just example data, you should have some LINQ query here that gets your results).
var a = new[] { "GUID1", "GUID2", "GUID3"};
var resultSetFromDatabase = new[]
{
new { IndividualProfileId = "GUID3", Name = "Joe Blo" },
new { IndividualProfileId = "GUID1", Name = "Maxy Dosh" },
new { IndividualProfileId = "GUID4", Name = "some one" },
new { IndividualProfileId = "GUID2", Name = "Max Walsh" }
};
var sortedResults = a.Join(res, s => s, e => e.IndividualProfileId, (s, e) => e);
It's impossible to have the datasource get the results directly in the right order, unless you're willing to write some dedicated SQL stored procedure. The problem is that you'd have to tell the database the contents of a. Using LINQ this can only be done via Contains. And that doesn't guarantee any order in the result set.
Turn the list(A), which you stated is a string, into an actual list. For example, you could use listAsString.Split(",") and then remove the 's from each element. I’ll assume the finished list is called list.
Query the database to retrieve the rows that you need, for example:
var data = db.Table.Where(row => list.Contains(row.IndividualProfileId));
From the data returned, create a dictionary keyed by the IndividualProfileId, for example:
var dic = data.ToDictionary(e => e.IndividualProfileId);
Iterate through the list and retrieve the dictionary entry for each item:
var results = list.Select(item => dic[item]).ToList();
Now results will have the records in the same order that the IDs were in list.

Categories