How to check for deletions on a LINQ to SQL EntitySet - linq-to-sql

I have an entity that has a collection of associated entities in an EntitySet. Ultimately, I'm trying to report on some changes that have been made to this entity. I will most likely use the GetModifiedMembers() method to do this, and I'm guessing I can just do the same with each entity in the EntitySet, but I'm not sure how to tell if there have been any deletions in that EntitySet.
What's the best way to do this?

You could use the dataContext.GetChangeSet() to track all the changed entities and filter the specific T entities. Please see if this is what you want:
public static void ShowModifiedEntitiesInfo<T>(DataContext context) where T : class
{
foreach (var entity in context.GetChangeSet().Updates.Where(del => del is T).Cast<T>())
{
ModifiedMemberInfo[] modifiedMembers = context.GetTable<T>().GetModifiedMembers(entity);
Console.WriteLine("Updated Entity: " + entity.ToString());
Console.WriteLine(" (Members Changed)");
foreach (var member in modifiedMembers)
{
Console.WriteLine(" - Member Name: " + member.Member.Name);
Console.WriteLine(" - Original Value: " + member.OriginalValue.ToString());
Console.WriteLine(" - Current Value: " + member.CurrentValue.ToString());
}
}
foreach (var entity in context.GetChangeSet().Inserts.Where(del => del is T).Cast<T>())
{
Console.WriteLine("Inserted Entity: " + entity.ToString());
}
foreach (var entity in context.GetChangeSet().Deletes.Where(del => del is T).Cast<T>())
{
Console.WriteLine("Deleted Entity: " + entity.ToString());
}
}
EDIT:
Is what you need something like this?
public static void ShowModifiedCustomerInfo(MyDataContext context, Customer customer)
{
ModifiedMemberInfo[] modifiedMembers = context.Customers.GetModifiedMembers(customer);
List<Order> updatedOrders = context.GetChangeSet().Updates.Where(e => e is Order).Cast<Order>().Intersect<Order>(customer.Orders).ToList<Order>();
List<Order> insertedOrders = context.GetChangeSet().Inserts.Where(e => e is Order).Cast<Order>().Intersect<Order>(customer.Orders).ToList<Order>();
List<Order> deletedOrders = context.GetChangeSet().Deletes.Where(e => e is Order).Cast<Order>().Intersect<Order>(customer.Orders).ToList<Order>();
if (modifiedMembers.Length > 0 || updatedOrders.Count > 0 || insertedOrders.Count > 0 || deletedOrders.Count > 0)
{
Console.WriteLine("Updated Customer: " + customer.ToString());
foreach (var member in modifiedMembers)
{
Console.WriteLine(" - Member Name: " + member.Member.Name);
Console.WriteLine(" - Original Value: " + member.OriginalValue.ToString());
Console.WriteLine(" - Current Value: " + member.CurrentValue.ToString());
}
foreach (var entity in updatedOrders)
{
Console.WriteLine(" Updated Order: " + entity.ToString());
}
foreach (var entity in insertedOrders)
{
Console.WriteLine(" Inserted Order: " + entity.ToString());
}
foreach (var entity in deletedOrders)
{
Console.WriteLine(" Deleted Order: " + entity.ToString());
}
}
}
The Customer is the entity and has an EntitySet<Order>. For what I understand, you what to know if the customer itself has changed and if there was any order from this customer that also changed.

CodeSmith's PLINQO captures all changes made when SubmitChanges is executed and packages it all up in an Audit object that can be accessed from the context LastAudit property. Reports on what changed and what type of update it was. There is a sample at http://plinqo.com/home.ashx?NoRedirect=1#Auditing_18

Related

JSON to OData String for Query

How can I turn a JSON object, i.e. { username: "john", password: "1234" } into an OData string query in a function using typescript? I could not find a library to do this for me (Angular 6). Here is my attempt:
function ConvertToODataString (json: Object) {
let ret_str: string = "";
for (let key in json) {
ret_str += (key + "=" + json[key] + "&");
}
if (ret_str) {
ret_str = ret_str.substr(0, ret_str.length - 1); // remove last &
}
return ret_str;
}
Does anyone know of a better way? For now, my json is not multi-leveled.
You can use for ... in to enumerate the object properties, adding each key/value pair to an array, and combine the values with Array.join:
function convertObjectToQuery(obj: Object): string {
let values = new Array<string>();
for (let prop in obj) {
values.push(`${prop} eq '${obj[prop]}'`);
}
return encodeURI("$filter=" + values.join(" and "));
}
See this stackblitz for a demo.
JSON.parse function.
Example:
var obj = JSON.parse('{ "name":"John", "age":30, "city":"New York"}');
json={ "name":"John", "age":30, "city":"New York"};
var obj = JSON.parse(json+'');
I decided to use the HttpParms module instead:
import { HttpParams } from "#angular/common/http";
const params = new HttpParams()
.set("$filter", "Username eq '" + parameters["Username"] + "' and Password eq '" + parameters["Password"] + "'")
.set("$count", "true");
console.log(params.toString());

get a json property type with Newtonsoft Json

I parsed a Json string with JObject.Parse(json) and I'm trying to traverse the properties. I find that the only way to access the json type is thru it's parent node, like this:
string json = #"{
CPU: 'Intel',
Drives: [ 'DVD read/writer', '500 gigabyte hard drive'
]
}";
JObject o = JObject.Parse(json);
foreach (var p in o.Properties())
{
Console.WriteLine("name:" + p.Name + ", value: " + p.Value);
Console.WriteLine("o[p.Name].Type: " + o[p.Name].Type); // correctly returns js type
Console.WriteLine("p.Type: " + p.Type); // returns Property for every item
Console.WriteLine("p.GetType(): " + p.GetType()); // returns Newtonsoft.Json.Linq.JProperty for every item
Console.WriteLine();
}
I suppose there must be some way to get the json type from the property. (live fiddle here)
The Value of a JProperty is a JToken. You can use the Type property on a JToken to get its JSON type. So you just need to use p.Value.Type to get what you are looking for.
Example fiddle: https://dotnetfiddle.net/CtuGGz
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
string json = #"
{
""CPU"": ""Intel"",
""Integrated Graphics"": true,
""USB Ports"": 6,
""OS Version"": 7.1,
""Drives"": [
""DVD read/writer"",
""500 gigabyte hard drive""
]
}";
JObject o = JObject.Parse(json);
foreach (var p in o.Properties())
{
Console.WriteLine("name: " + p.Name);
Console.WriteLine("type: " + p.Value.Type);
Console.WriteLine("value: " + p.Value);
Console.WriteLine();
}
}
}

Determine which PropertyInfos of a generic TEntity are primary keys via reflection

I am writing a MySQL INSERT ... ON DUPLICATE KEY UPDATE implementation via a raw SQL command in EF Core 2.0. I am very close to a working solution, but the only problem that I have is determining which PropertyInfos read via reflection are primary keys. In the CreateUpdates() method below, how can I filter primary keys out from columnProperties so they are not a part of the update SQL statement?
I have tried using EntityFramework.PrimaryKey
, but I cannot seem to get it to work with generics (TEntity).
I have included all of my relevant code, but the piece I am focused on in this question is the TODO in the last method, CreateUpdates().
private static void InsertOnDuplicateKeyUpdate<TEntity>(DbContext dbContext) where TEntity : class
{
var columnProperties = GetColumnPropertiesLessBaseEntityTimestamps<TEntity>();
var tableName = GetTableName<TEntity>();
var columns = string.Join(", ", columnProperties.Select(x => x.Name));
var values = CreateValues<TEntity>(columnProperties);
var updates = CreateUpdates(columnProperties);
var rawSqlString = "INSERT INTO " + tableName + " (" + columns + ") VALUES " + values +
" ON DUPLICATE KEY UPDATE " + updates;
dbContext.Set<TEntity>().FromSql(rawSqlString);
dbContext.SaveChanges();
}
private static string GetTableName<TEntity>()
{
return typeof(TEntity).Name.Pluralize().ToLower();
}
private static List<PropertyInfo> GetColumnPropertiesLessBaseEntityTimestamps<TEntity>()
{
return typeof(TEntity).GetProperties().Where(x =>
x.PropertyType.Namespace != "System.Collections.Generic" &&
!new List<string> {"CreatedDateUtc", "ModifiedDateUtc"}.Contains(x.Name)).ToList();
}
private static string CreateValues<TEntity>(IReadOnlyCollection<PropertyInfo> columnProperties)
{
return GetSeedRows<TEntity>().Select(row => CreateRowValues(columnProperties, row)).Aggregate("",
(current, rowValues) => current == "" ? rowValues : current + ", " + rowValues);
}
private static string CreateRowValues<TEntity>(IEnumerable<PropertyInfo> columnProperties, TEntity row)
{
return (from property in columnProperties
let value = row.GetType().GetProperty(property.Name).GetValue(row)
select WrapStringPropertyValueInSingleQuotes(property, value)).Aggregate("",
(current, value) => current == "" ? "(" + value : current + ", " + value) + ")";
}
private static object WrapStringPropertyValueInSingleQuotes(PropertyInfo property, object value)
{
if (property.PropertyType == typeof(string))
value = "'" + value + "'";
return value;
}
private static string CreateUpdates(IEnumerable<PropertyInfo> columnProperties)
{
//TODO: filter out primary keys from columnProperties
return columnProperties.Select(property => property.Name).Aggregate("", (current, column) => current == ""
? column + " = VALUES(" + column + ")"
: current + ", " + column + " = VALUES(" + column + ")");
}
In ef-core it has become much easier to retrieve meta data from the mapping model. You can get the PropertyInfos of the primary key by this line:
var keyPropertyInfos = dbContext.Model.FindEntityType(typeof(TEntity))
.FindPrimaryKey()
.Properties
.Select(p => p.PropertyInfo);
By the way, you can get all (mapped) properties by replacing FindPrimaryKey().Properties by GetProperties();

How to create a `String[]` field with active annotation of xtend?

I tried with active annotation of xtend, and I want to create a live annotation which can generate a String[] field to record the names of method parameters.
#Target(ElementType::TYPE)
#Active(typeof(ParameterRecorderProcessor))
annotation ParameterRecorder {
}
class ParameterRecorderProcessor extends AbstractClassProcessor {
override doTransform(MutableClassDeclaration annotatedClass, extension TransformationContext context) {
var iii = 0;
// add the public methods to the interface
for (method : annotatedClass.declaredMethods) {
if (method.visibility == Visibility::PUBLIC) {
iii = iii + 1
annotatedClass.addField(method.simpleName + "_" + iii) [
type = typeof(String[]).newTypeReference // String[] doesn't work
var s = ""
for (p : method.parameters) {
if(s.length > 0) s = s + ","
s = s + "\"" + p.simpleName + "\""
}
val ss = s
initializer = [
'''[«ss»]'''
]
]
}
}
}
}
You can see I use typeof(String[]).newTypeReference to define the type of new created field, but it doesn't work. The generated java code is looking like:
private Object index_1;
It uses Object and the initializer part has be empty.
How to fix it?
This looks like a bug to me. As a workaround, you may want to use typeof(String).newTypeReference.newArrayTypeReference or more concise string.newArrayTypeReference

Customizing JSON serialization in Play

I'm using renderJSON(Object) to return some objects as JSON values, and it's working fine except for one field. Is there an easy way to add in that one field without having to manually create the whole json template?
Play uses GSON to build the JSON string. If your one field is a specific object type, then you can easily do this by providing a customised serialisation for that type. See the documentation here
http://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserializ
However, if it is an Integer class for example, that you want to work in one way for one, and another way for another, then you may have a little more difficulty.
Example
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(SpecificClass.class, new MySerializer());
private class MySerializer implements JsonSerializer<DateTime> {
public JsonElement serialize(SpecificClass src, Type typeOfSrc, JsonSerializationContext context) {
String res = "special format of specificClass"
return new JsonPrimitive(res);
}
}
Simply do a
JsonElement elem = new Gson().toJsonTree(yourObject);
JsonObject obj = elem.getAsJsonObject();
obj.remove("xxx");
obj.addProperty("xxx", "what you want");
// other stuff ...
renderJSON(obj.toString());
etc.
After evaluating the play framework we hit a stumbling block and decision choice on serializing JSON for an external API. Allot of articles out there suggest using the Lift framework within play which just seem like extra overhead.After trying some of the frameworks / modules with in the play framework a college and myself decided to write a light weight code block that could cater for our needs.
case class User (
user_id: Int,
user_name: Option[String],
password: Option[String],
salt: Option[String]
) extends Serializable {
def toXml =
<user>
<user_id>{user_id}</user_id>
<user_name>{user_name.getOrElse("")}</user_name>
</user>
override def toJson =
"{" + JSON.key("user_id") + JSON.value(user_id) + ", " + JSON.key("user_name") + JSON.value(user_name) + "}"
}
class Serializable {
def toJson = ""
}
object JSON {
def key(x:String) = value(x) + ": "
def value(x:Any):String = {
x match {
case s:String => "\"" + s + "\""
case y:Some[String] => value(y.getOrElse(""))
case i:Int => value(i.toString)
case s:Serializable => s.toJson
case xs:List[Any] => "[" + xs.map(x => value(x)).reduceLeft(_ + ", " + _) + "]"
}
}
}
def searchUserByName(user_name: String) = {
(for (
u <- Users if u.user_name.like(("%"+user_name+"%").bind)
) yield u.*)
.list
.map(User.tupled(_))
}
def toXml(users:List[User]) = {
<users>
{ users.map(u => u.toXml) }
</users>
}
def toJson(users:List[User]) = {
"[" + users.map(u => u.toJson).reduceLeft(_ + ", " + _) + "]"
}
And from the controller.
// -- http://localhost:9000/api/users/getUser/xml
// -- http://localhost:9000/api/users/getUser/json
def getUser(requestType:String) = {
db withSession{
val user = Users.byUserName("King.Kong")
if(requestType == "xml") {
Xml(user.toXml)
} else {
user.toJson
}
}
}
//--- http://localhost:9000/api/users/searchuser/xml
//--- http://localhost:9000/api/users/searchuser/json
def searchUser(requestType:String) = {
db withSession{
val users = Users.searchUserByName("Doctor.Spoc")
if(requestType == "xml") {
Xml(Users.toXml(users))
} else {
val jsonList = Users.toJson(users)
Json(jsonList)
}
}