How can I create a MetadataWorkspace using metadata loading delegates? - runtime

I followed this example Changing schema name on runtime - Entity Framework where I can create a new EntityConnection from a MetaDataWorkspace that I then use to construct a DbContext with a different schema, but I get compiler warnings saying that RegisterItemCollection method is obsolete and to "Construct MetadataWorkspace using constructor that accepts metadata loading delegates."
How do I do that? Here is the code that is working but gives the 3 warnings for the RegsiterItemCollection calls. I'm surprised it works since warning says obsolete not just deprecated.
public static EntityConnection CreateEntityConnection(string schema, string connString, string model)
{
XmlReader[] conceptualReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".csdl")
)
};
XmlReader[] mappingReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".msl")
)
};
var storageReader = XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".ssdl")
);
//XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl"; // this would not work!!!
XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
var storageXml = XElement.Load(storageReader);
foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet"))
{
var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
if (schemaAttribute != null)
{
schemaAttribute.SetValue(schema);
}
}
storageXml.CreateReader();
StoreItemCollection storageCollection =
new StoreItemCollection(
new XmlReader[] { storageXml.CreateReader() }
);
EdmItemCollection conceptualCollection = new EdmItemCollection(conceptualReader);
StorageMappingItemCollection mappingCollection =
new StorageMappingItemCollection(
conceptualCollection, storageCollection, mappingReader
);
//var workspace2 = new MetadataWorkspace(conceptualCollection, storageCollection, mappingCollection);
var workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(conceptualCollection);
workspace.RegisterItemCollection(storageCollection);
workspace.RegisterItemCollection(mappingCollection);
var connectionData = new EntityConnectionStringBuilder(connString);
var connection = DbProviderFactories
.GetFactory(connectionData.Provider)
.CreateConnection();
connection.ConnectionString = connectionData.ProviderConnectionString;
return new EntityConnection(workspace, connection);
}

I was able to get rid of the 3 warning messages. Basically it wants you to register the collections in the constructor of the MetadataWorkspace.
There are 3 different overloads for MetadataWorkspace, I chose to use the one which requires to to supply a path (array of strings) to the workspace metadata. To do this I saved readers to temp files and reloaded them.
This is working for me without any warnings.
public static EntityConnection CreateEntityConnection(string schema, string connString, string model) {
var conceptualReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".csdl"));
var mappingReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".msl"));
var storageReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".ssdl"));
XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
var storageXml = XElement.Load(storageReader);
var conceptualXml = XElement.Load(conceptualReader);
var mappingXml = XElement.Load(mappingReader);
foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet")) {
var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
if (schemaAttribute != null) {
schemaAttribute.SetValue(schema);
}
}
storageXml.Save("temp.ssdl");
conceptualXml.Save("temp.csdl");
mappingXml.Save("temp.msl");
MetadataWorkspace workspace = new MetadataWorkspace(new List<String>(){
#"temp.csdl",
#"temp.ssdl",
#"temp.msl"
}
, new List<Assembly>());
var connectionData = new EntityConnectionStringBuilder(connString);
var connection = DbProviderFactories.GetFactory(connectionData.Provider).CreateConnection();
connection.ConnectionString = connectionData.ProviderConnectionString;
return new EntityConnection(workspace, connection);
}

Not wanting to create temp files which slows the process down, I found an alternate answer to this is fairly simple. I replaced these lines of code -
//var workspace2 = new MetadataWorkspace(conceptualCollection, storageCollection, mappingCollection);
var workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(conceptualCollection);
workspace.RegisterItemCollection(storageCollection);
workspace.RegisterItemCollection(mappingCollection);
with this one line of code -
var workspace = new MetadataWorkspace(() => conceptualCollection, () => storageCollection, () => mappingCollection);
and that works fine.

Related

Connecting xamarin to woocommerce api

I'm trying to create my first app that will connect to a woocommerce api.
Has anyone any experience in this or can point me in the direction as to how to create a connection to pull in the product list?
Thanks
Since WooCommerce has a REST API, it should be fairly simple to connect using a plain HTTP request, or a library like RestSharp.
There is also a C# client for WooCommerce - I don't know if it plays well with Xamarin, you might need to modify it a bit to get it to build.
var api = new WoocommerceApiClient(StoreUrl, ConsumerKey, ConsumerSecret);
var result = await api.Products.Get();
It's an old post but I had faced a similar issue. I had tries WoocommerceSharp with Xamarin Studio 6.1.1 (mac version); I opened the .sln file, added the missing reference to system.net.http and it worked perfectly.
If you want make it work in PCL you have to use PCLCrypto in WoocommerceApiUrlGenerator.cs , here the updated version:
namespace SharpCommerce.Web
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using PCLCrypto;
internal class WoocommerceApiUrlGenerator
{
private const string SignatureMethod = "HMAC-SHA1";
private const string ApiV3RootEndpoint = "wc-api/v3/";
private readonly string baseURI;
private readonly string consumerKey;
private readonly string consumerSecret;
internal WoocommerceApiUrlGenerator(string storeUrl, string consumerKey, string consumerSecret)
{
if (
string.IsNullOrEmpty(consumerKey) ||
string.IsNullOrEmpty(consumerSecret) ||
string.IsNullOrEmpty(storeUrl))
{
throw new ArgumentException("ConsumerKey, consumerSecret and storeUrl are required");
}
this.consumerKey = consumerKey;
this.consumerSecret = consumerSecret;
// Need 'http://www.example.com' to be 'http://www.example.com/wc-api/v3/'
this.baseURI = String.Format("{0}/{1}", storeUrl.TrimEnd('/'), ApiV3RootEndpoint);
}
internal string GenerateRequestUrl(HttpMethod httpMethod, string apiEndpoint, Dictionary<string, string> parameters = null)
{
parameters = parameters ?? new Dictionary<string, string>();
parameters["oauth_consumer_key"] = this.consumerKey;
// oauth_timestamp = number of seconds since 1/1/1970 00:00:00 GMT
// must be a positive integer
// must be greater than timestamp of previous requests
parameters["oauth_timestamp"] =
Math.Round(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds).ToString(CultureInfo.InvariantCulture);
// oauth_nonce = a unique random string for the timestamp.
// defends against replay attacks
// service provide will know that this request has never been made before.
// Just going to hash the time stamp.
//parameters["oauth_nonce"] = GenerateNonce(parameters["oauth_timestamp"]);
// Create random 32 char alphnumeric to avoid reused nonces
parameters["oauth_nonce"] = GenerateNonce();
// Declare the hashing method your using
parameters["oauth_signature_method"] = SignatureMethod;
//parameters["oauth_version"] = "1.0";
parameters["oauth_signature"] = UpperCaseUrlEncode(this.GenerateSignature(httpMethod, apiEndpoint, parameters));
var sb = new StringBuilder();
foreach (var pair in parameters)
{
sb.AppendFormat("&{0}={1}", SafeUpperCaseUrlEncode(pair.Key), SafeUpperCaseUrlEncode(pair.Value));
}
// Substring removes first '&'
var queryString = sb.ToString().Substring(1);
var url = this.baseURI + apiEndpoint + "?" + queryString;
return url;
}
private string GenerateSignature(HttpMethod httpMethod, string apiEndpoint, Dictionary<string, string> parameters)
{
// 1) Set the HTTP method for the request.
// set through 'method'
//2) Set your base request URI – this is the full request URI without query string parameters – and URL encode according to RFC 3986:
// need 'http://www.example.com/wc-api/v3/orders'
// to become: 'http%3A%2F%2Fwww.example.com%2Fwc-api%2Fv1%2Forders'
var encodedBaseRequestURI = SafeUpperCaseUrlEncode(this.baseURI + apiEndpoint);
// 3) Collect and normalize your query string parameters
// percent(%) characters should be double-encoded (e.g. % becomes %25.
var normalizedParameters = NormalizeParameters(parameters);
// 4) Sort the parameters in byte-order
var orderedNormalizedParameters = normalizedParameters.OrderBy(x => x.Key).ToList();
// 5) Join each parameter with an encoded equals sign (%3D):
//var joinedOrderedNormalizedParameters = orderedNormalizedParameters.ConvertAll(x => x.Key + "%3D" + x.Value);
var joinedOrderedNormalizedParameters = new List<string>();
foreach (var x in orderedNormalizedParameters)
{
joinedOrderedNormalizedParameters.Add(x.Key + "%3D" + x.Value);
}
// 6) Join each parameter key/value pair with an encoded ampersand (%26):
var joinedParameterPairs = String.Join("%26", joinedOrderedNormalizedParameters);
// 7) Form the string to sign by joining the HTTP method, encoded base request URI, and encoded parameter string with an unencoded ampersand symbol (&):
var stringToSign = string.Format("{0}&{1}&{2}", httpMethod.ToString().ToUpper(), encodedBaseRequestURI, joinedParameterPairs);
// 8) Generate the signature using the string to key and your consumer secret key
var preparedStringToSign = Encoding.UTF8.GetBytes(stringToSign);
var secret = this.consumerSecret + "&";
var preparedConsumerKey = Encoding.UTF8.GetBytes(secret);
var signatureHash = Sha1(preparedConsumerKey, preparedStringToSign);
var signatureString = Convert.ToBase64String(signatureHash);
return signatureString;
}
private static byte[] Sha1(byte[] key, byte[] message)
{
var mac = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha1);
//var keyMaterial = WinRTCrypto.CryptographicBuffer.ConvertStringToBinary(key, Encoding.UTF8);
var cryptoKey = mac.CreateKey(key);
var hash = WinRTCrypto.CryptographicEngine.Sign(cryptoKey, message);
return hash;
//return WinRTCrypto.CryptographicBuffer.CreateFromByteArraymessage);
}
private static Dictionary<string, string> NormalizeParameters(Dictionary<string, string> parameters)
{
var result = new Dictionary<string, string>();
foreach (var pair in parameters)
{
var upperCaseUrlEncodedKey = SafeUpperCaseUrlEncode(pair.Key);
var normalizedKey = upperCaseUrlEncodedKey.Replace("%", "%25");
var upperCaseUrlEncodedValue = SafeUpperCaseUrlEncode(pair.Value);
var normalizedValue = upperCaseUrlEncodedValue.Replace("%", "%25");
result.Add(normalizedKey, normalizedValue);
}
return result;
}
private static string SafeUpperCaseUrlEncode(string stringToEncode)
{
return UpperCaseUrlEncode(System.Net.WebUtility.UrlDecode(stringToEncode));
}
private static string UpperCaseUrlEncode(string stringToEncode)
{
var basicUrlEncodedString = System.Net.WebUtility.UrlEncode(stringToEncode);
if (String.IsNullOrEmpty(basicUrlEncodedString)) return String.Empty;
var upperCaseUrlEncodedString = Regex.Replace(
basicUrlEncodedString,
"(%[0-9a-f][0-9a-f])",
c => c.Value.ToUpper());
return upperCaseUrlEncodedString;
}
private static string GenerateNonce()
{
const string ValidChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var nonceString = new StringBuilder();
for (var i = 0; i < 32; i++)
{
nonceString.Append(ValidChars[random.Next(0, ValidChars.Length - 1)]);
}
return nonceString.ToString();
}
}
}
and in WoocommerceApiDriver.cs you will have to replace
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return await client.GetStringAsync(url);
}
by
using (var client = new HttpClient()) // must use to avoid Android freezes after repeated calls
{
Task<HttpResponseMessage> r = client.GetAsync(url);
HttpResponseMessage m = r.Result;
return await m.Content.ReadAsStringAsync();
}
Et voila ! ca marche ;)

Can XUnit interfere with the code?

I recently have a very weird behaviour, so weird I'm starting to wonder if the culprit could be the unit framework I'm using (XUnit). I asked a question about it there: Can Expressmapper copy to destination? but this one is no longer about Expressmapper but about XUnit.
Do you know if in some way XUnit can intefere with the code?
Here's a the reason I'm asking:
I can run those 'together or separatly) in any order and I always get this crazy behaviour:
First test fails (Test_xxx)
Second test pass (Test_Map)
Both tests contain the very same code!!
[Fact]
[Trait("Test kind", "Integration")]
public void Test_xxx()
{
// Arrange
var mapper = new MappingServiceProvider();
mapper.Register<MapSource, MapDestination>();
var src = new MapSource
{
Id = Guid.NewGuid().GetHashCode(),
Guid = Guid.NewGuid(),
Parent = new MapSource
{
Id = Guid.NewGuid().GetHashCode(),
Guid = Guid.NewGuid()
}
};
src.Children = Enumerable.Range(0, 3).Select(i => new MapSource
{
Id = Guid.NewGuid().GetHashCode(),
Guid = Guid.NewGuid()
}).ToList();
var dst = new MapDestination();
// Act
mapper.Map(src, dst);
// Assert
var compare = new CompareLogic(new ComparisonConfig
{
IgnoreObjectTypes = true
});
var comparison = compare.Compare(src, dst);
Assert.Equal(new List<Difference>(), comparison.Differences);
}
[Fact]
[Trait("Test kind", "Integration")]
public void Test_Map()
{
// Arrange
var mapper = new MappingServiceProvider();
mapper.Register<MapSource, MapDestination>();
var src = new MapSource
{
Id = Guid.NewGuid().GetHashCode(),
Guid = Guid.NewGuid(),
Parent = new MapSource
{
Id = Guid.NewGuid().GetHashCode(),
Guid = Guid.NewGuid()
}
};
src.Children = Enumerable.Range(0, 3).Select(i => new MapSource
{
Id = Guid.NewGuid().GetHashCode(),
Guid = Guid.NewGuid()
}).ToList();
var dst = new MapDestination();
// Act
mapper.Map(src, dst);
// Assert
var compare = new CompareLogic(new ComparisonConfig
{
IgnoreObjectTypes = true
});
var comparison = compare.Compare(src, dst);
Assert.Equal(new List<Difference>(), comparison.Differences);
}
I had another method called Test_Map (with a different signature; aka overload).
I guess XUnit requires method name to be unique (something I intended to do).

Master details batch crud with entity framework only saving single detail record

I'm building a master details page with batch editing, that is multiple details records with single master record. but only one detail record is being saved into the data base. I tried to debug & found that detail loop is executing multiple time accurately but not the saving multiple data. Here is my Code for save method:
public ActionResult CMN_VAL_FORM(HRM_CMN_VLU_MST_ViewModel model)
{
//var ctx=new Entities1();
var CMN_VLU_MST_OBJ = new HRM_CMN_VLU_MST();
var CMN_VLU_DTL_OBJ = new HRM_CMN_VLU_DTL();
//using (TransactionScope transaction = new TransactionScope())
//{
using (var ctx = new Entities1())
{
var type_code = ctx.ExecuteStoreQuery<string>("select get_pk_code('hrm_cmn_vlu_mst','CMN_VLU_TYPE_CODE') from dual").SingleOrDefault(); //A scalar function to generate the code in the format yymmdd0001
var value_code = ctx.ExecuteStoreQuery<string>("select get_pk_code('hrm_cmn_vlu_dtl','CMN_VLU_CODE') from dual").SingleOrDefault();
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_CODE = type_code;
CMN_VLU_MST_OBJ.CMN_VLU_REM = model.CMN_VLU_REM;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_FOR = model.CMN_VLU_TYPE_FOR;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_SRTNM = model.CMN_VLU_TYPE_SRTNM;
CMN_VLU_MST_OBJ.ENTRY_DATE = DateTime.Now;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_NAME = model.CMN_VLU_TYPE_NAME;
foreach (var item in model.HRM_CMN_VLU_DTL)
{
CMN_VLU_DTL_OBJ.CMN_VLU_LEVL = item.CMN_VAL_LEVL;
CMN_VLU_DTL_OBJ.CMN_VLU_REM = item.CMN_VLU_REM;
CMN_VLU_DTL_OBJ.CMN_VLU_SLNO = item.CMN_VLU_SLNO;
CMN_VLU_DTL_OBJ.CMN_VLU_TITL = item.CMN_VAL_TITL;
CMN_VLU_DTL_OBJ.CMN_VLU_CNTN = item.CMN_VAL_CNTN;
CMN_VLU_DTL_OBJ.CMN_VLU_CODE = value_code;
CMN_VLU_DTL_OBJ.CMN_VLU_TYPE_CODE = type_code;
CMN_VLU_DTL_OBJ.ENTRY_DATE = DateTime.Now;
CMN_VLU_DTL_OBJ.MAIL_ADDR_INT = item.MAIL_ADDR_INT;
CMN_VLU_DTL_OBJ.MAIL_ADDR_EXT = item.MAIL_ADDR_EXT;
CMN_VLU_DTL_OBJ.MAIL_AUTO_SEND_INT = item.MAIL_AUTO_SEND_INT;
CMN_VLU_DTL_OBJ.MAIL_AUTO_SEND_EXT = item.MAIL_AUTO_SEND_EXT;
CMN_VLU_DTL_OBJ.ACTIVE_STATUS = item.ACTIVE_STATUS;
CMN_VLU_MST_OBJ.HRM_CMN_VLU_DTL.Add(CMN_VLU_DTL_OBJ);
ctx.SaveChanges();
var temp_value_code = Int32.Parse(value_code);
temp_value_code++;
value_code = temp_value_code.ToString();
ctx.HRM_CMN_VLU_MST.AddObject(CMN_VLU_MST_OBJ);
}
ctx.SaveChanges();
// transaction.Complete();
//}
}
return View();
}
No Error message for the code, but not saving multiple detail records. What I'm doing wrong?
What I was doing wrong, I created the object just once & updated that same object every time while executing the foreach loop! I just moved the object declaration into the foreach loop & it works like a charm!

How to want to move my DAL to separate project in my MVC3 solution?

I have MVC 3 application that uses a DAL (ADO.NET) that communicates to a set of tsql stored procedures? I want to add a new MVC project to my current solution. I need to have DAL in a separate project that the 2 MVC project ("Monitor" and "Audit") can share.
Here's the current DAL (which sits in a folder of the "Monitor" MVC project) code below. My issue is I have signature like IEnumerable located in the Monitor.Models and IEnumerable located in the Audit.Models. Do I need to make the DAL generic to avoid needing to make references to the models in the DAL?
Ex:
**//Is this bad practice?**
using Monitor.Models;
using Adit.Models;
namespace Monitor.DAL
{
public class QuestionDAL
{
static ILog log = log4net.LogManager.GetLogger(typeof(QuestionDAL));
private string _connectionString = WebConfigurationManager.ConnectionStrings["NexGenContext"].ToString();
public IEnumerable<AgencyTerm> SearchAgencies(string ori, string name)
{
log.Debug("Executing: SearchAgencies(string ori, string name)");
List<AgencyTerm> agencies = new List<AgencyTerm>();
using (var conn = new SqlConnection(_connectionString))
{
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
string term = "Ori";
if (!String.IsNullOrEmpty(ori))
{
term = "Ori";
com.Parameters.Add(new SqlParameter
{
ParameterName = "#ORI",
Value = ori
});
}
if (!String.IsNullOrEmpty(name))
{
term = "legal_name";
com.Parameters.Add(new SqlParameter
{
ParameterName = "#Name",
Value = name
});
}
com.CommandText = "Review_Get_Agency_List";
var adapt = new SqlDataAdapter();
adapt.SelectCommand = com;
var dataset = new DataSet();
adapt.Fill(dataset);
agencies = (from c in dataset.Tables[0].AsEnumerable()
select new AgencyTerm()
{
label = c[term].ToString(),
id = c["Agency_Id"].ToString()
}).ToList<AgencyTerm>();
return agencies;
}
}
public IEnumerable<User> GetUsers()
{
log.Debug("Executing: GetUsers()");
List<User> users = new List<User>();
using (var conn = new SqlConnection(_connectionString))
{
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
com.CommandText = "Review_Get_Users";
var adapt = new SqlDataAdapter();
adapt.SelectCommand = com;
var dataset = new DataSet();
adapt.Fill(dataset);
users = (from c in dataset.Tables[0].AsEnumerable()
select new User()
{
User_ID = Convert.ToInt32(c["User_ID"]),
Department = c["Department"].ToString(),
Enabled = Convert.ToBoolean(c["Enabled"]),
Email = c["Email"].ToString(),
User_First_Name = c["User_First_Name"].ToString(),
User_Last_Name = c["User_Last_Name"].ToString(),
Location = c["Location"].ToString(),
User_Name = c["User_Name"].ToString()
}).ToList<User>();
return users;
}
}
You have two possibilities:
Either move your model into a separate library as well and then reference it from your MVC project and your DAL
Make your DAL completely generic and just push the values inside. I don't see an easy way here though since you have a lot of information in your DAL
I would go with the first option. Just extract your models to a different project and then reuse that library in both DAL and MVC-projects

Can I have more than one profiled connection with MVC Mini Profiler?

I'd like to use a mixture of both Dapper and EF 4 in my MVC3 application. Is it possible to have more than one profiled connection, or share a profiled connection?
Yes, you can use the EF profiled connection with Dapper, here's what I ended up using.
private static DatabaseContext GetDatabaseContext()
{
using (MiniProfiler.Current.Step("Create Oracle EF Context"))
{
var pConn = ProfiledConnection;
return pConn.CreateObjectContext<DatabaseContext>();
}
}
private static EFProfiledDbConnection ProfiledConnection
{
get
{
using (MiniProfiler.Current.Step("Create Oracle Profiled Connection"))
{
var connectionString =
ConfigurationManager.ConnectionStrings["DatabaseContext"].ConnectionString;
var ecsb = new EntityConnectionStringBuilder(connectionString);
var oraConn = new OracleConnection(ecsb.ProviderConnectionString);
var pConn = new EFProfiledDbConnection(oraConn, MiniProfiler.Current);
return pConn;
}
}
}
Then I used them in this way
using (var ctx = GetDatabaseContext())
{
var result = ctx.SOME_EF_ENTITY.FirstOrDefault(c => c.LOGINNAME == username && c.PASSWORD == password);
return result;
}
and
const string sql = #"
SELECT ppro_status_code_id
FROM ppro_status_codes
WHERE table_name = 'CLIENT_FC_SHARED'
AND status_code = :status_code";
using (var cnn = ProfiledConnection)
{
cnn.Open();
var data = cnn.Query<dynamic>(sql, new { status_code = code });
var result = data.First();
var codeID = result.PPRO_STATUS_CODE_ID;
return codeID;
}

Resources