Linq update issue with lambda - c#

I am trying to write some code in Linq with lambda.This is my first code using lambda and i am facing an issue while updating Record.
My code is:
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
Table<NOTIF_RECIP> NOTIF_RECIP_alias = db.GetTable<NOTIF_RECIP>();
Table<NOTIF_SCHED> NOTIF_SCHED_alias = db.GetTable<NOTIF_SCHED>();
Table<mainframe_replication> mainframe_replication_alias = db.GetTable<mainframe_replication>();
var ids = NOTIF_SCHED_alias.Select(x => x.NOTIF_RPT_ID).ToArray();
foreach (string notif_sched_data in ids)
{
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias,
mfr => mfr.RPT_ID,
nr => nr.NOTIF_RECIP_ID,
(mfr, nr) => new
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= mfr.NOTIF_ID,
RecipAdd=nr.NOTIF_RECIP_ADDR
});
foreach(var repljoinmf_data in repljoinmf)
{
//DO STUFF
repljoinmf_data.NotifId = "Changedxyz";
//db.SubmitChanges();
}
}
}
I am getting Error in repljoinmf_data.NotifId = "Changedxyz";
Error says: Error 2 Property or indexer 'AnonymousType#3.NotifId' cannot be assigned to -- it is read only
Can someone please help me in this.I think it is because I am using var which is anonymous but how to solve the problem.Any help is appreciated.
Thanks

As the error suggests, anonymous class instances cannot be modified once they have been projected.
Although you could switch to a strong typed class, and then reassign the member properties, however, you have an opportunity to project the desired result in the preceding LINQ statement into the same anonymous class:
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias, mfr => mfr.RPT_ID, nr => nr.NOTIF_RECIP_ID,
(mfr, nr) => new // Anon Class projection
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= "Changedxyz", // *** No need to mutate this afterwards
RecipAdd=nr.NOTIF_RECIP_ADDR
});
Edit, Update isn't trivial assignment, suggested alternatives
Option #1 : Strongly typed Class with mutation after projection
Add a new class (I've guessed some types)
public class MyPoco
{
public int ReportId {get; set;}
public string Reportversion {get; set;}
public byte[] ReportBytes {get; set;}
public DateTime ReportDate {get; set;}
public int NotifId {get; set;}
public string RecipAdd {get; set;}
}
Which you can then project into (just specify the class name instead of anonymous):
(mfr, nr) => new MyPoco // Not anonymous
{
ReportId=mfr.RPT_ID,
...
And then do modification afterwards:
foreach(var repljoinmf_data in repljoinmf)
{
repljoinmf_data.NotifId = "SomeNewValue"
Option #2 - Create a method (or Func) which does the complex logic
Since you seem to have already materialized all the data, you are free to use complex functions in the property projections. Any of the available local variables (closure) are available to pass to thus function, as are the join lambda parameters (mfr, nr)
So for example, write a function to calculate your NotifId = "Changedxyz" replacement:
private string DoIntensiveLogic(mainframe_replication mfr, NOTIF_RECIP nr)
{
// Do Stuff
}
Which you can then use in your original anonymous projection:
(mfr, nr) => new // Anon Class projection
{
ReportId=mfr.RPT_ID,
Reportversion=mfr.RPT_VERS,
ReportBytes= mfr.RPT_BYTES.ToString(),
ReportDate=mfr.REPL_DTM.ToString(),
NotifId= DoIntensiveLogic(mfr, nr), // Call the function each row
RecipAdd=nr.NOTIF_RECIP_ADDR
});

Anonymous types are immutable and hence created cannot be changed you have to create a new type.
To solve your issue you have to create your own type and avoid the use of anonymous type when a future update is needed.
your type may look like this
public class ReportInfo
{
public int Id{get; set;}
//the same thing for others properties
}
and your query will look like this
new ReportInfo() {
Id = mfr.RPT_ID,
Reportversion = mfr.RPT_VERS,
ReportBytes = mfr.RPT_BYTES.ToString(),
ReportDate = mfr.REPL_DTM.ToString(),
NotifId = mfr.NOTIF_ID,
RecipAdd = nr.NOTIF_RECIP_ADDR
})
than you can update easily your property
foreach(var repljoinmf_data in repljoinmf)
{
//DO STUFF
repljoinmf_data.NotifId = "Changedxyz";
//db.SubmitChanges();
}
More about anonymous Types
what the compiler is actually doing. When you write a line of code like this:
var o = new { property1 = expression1, ..., propertyN = expressionN };
the compiler infers the type of each expression, creates private fields of these inferred types, creates
public read-only properties for each of the fields, and creates a constructor that accepts all these
expressions. The constructor’s code initializes the private read-only fields from the expression results
passed in to it. In addition, the compiler overrides Object’s Equals, GetHashCode, and ToString
methods and generates code inside all these methods.

if you want to change 'NotifId' later, you can find a record by id and change the property.
Example:
var alias = mainframe_replication_alias.SingleOrDefault(mfr => mfr.NOTIF_ID == repljoinmf_data.NotifId);
if(alias != null)
alias.NOTIF_ID = "Changedxyz";

Related

C# how to return 1 row from List<T> object passed as parameter

I need to return one row of List from my function Selectus.
So I pass to the function Selectus object that reflects database table fields and I need to return one row which match the parameter looking_for:
public static List<T> Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.ToList();//here one record should be returned
}
I do not know how to select one row from the list assuming that T parameter is vary. In SelectusT> method I want to pass as T different objects which reflect fields in database table rather than creatinig separate methods for each select. e.g. call Selectus, where object passed is public class ProductCodes { public int ID { get; set; } public string SapIndex { get; set; } public string SapName { get; set; } }. Then I want to call another Selectus<ProductTypes> for another table etc... So I want to write generic/overall method and use it universally for all types of my objects which reflects the fields of few database tables. The SapIndex property is always in the same place of all objects...
Using prop[1] is incredibly fragile. Who says that the property you're currently interested in is always going to be in second place? What if someone adds another property tomorrow? What if not every T that you use have the same property in the second place on its list of properties? It is quite unclear what your actual goal is here and why you've taken the reflection route.
You would be better off using inheritance or interface implementation here. I'm going to use an interface in this answer, but either would work.
For the sake of clarity, let's assume there is a Code field in all your possible lists, and this is the property you're trying to match with.
Define a reusable interface:
public interface ICodeEntity
{
string Code { get; }
}
Apply your interface to all of the classes that you intend to use for your Selectus method.
public class Person : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
public class Document : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
Add a generic type constraint that limits the use of T only to types that implement your interface.
public static List<T> Selectus<T>(string code)
where T : ICodeEntity
You can now write your code in a way that it relies on the type in question having a Code property, and the compiler will help enforce it.
var db = OrmLiteBaza().Open();
var list = db.Select<T>().ToList();
db.Dispose();
return list.Where(item => item.Code == code).ToList();
Usage examples:
List<Person> peopleWithCodeABC = Selectus<Person>("ABC");
List<Person> documentsWithCodeXYZ = Selectus<Document>("XYZ");
// This will fail if Animal does not implement ICodeEntity
var compilerError = Selectus<Animal>("ABC");
I might not understand fully what you want, but instead of string looking_for you could pass in a Func<,> delegate which acts as a selector.
Something like:
public static List<TField> Selectus<T, TField>(Func<T, TField> selector)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Select(selector); // 'using System.Linq;'
return list_selected_record.ToList();
}
Then I believe it could be called like this:
var list_one = Selectus((ProductCodes x) => x.SapIndex);
var list_two = Selectus((ProductTypes x) => x.SapIndex);
var list_three = Selectus((ProductCodes x) => x.SapName);
With this syntax I leave out the <ProductCodes, string> generic arguments to the method since they can be inferred.
Hmm, maybe you want it in the opposite dimension. You could do:
public static List<T> Selectus<T>(Func<T, bool> predicate)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Where(predicate); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus((ProductCodes x) => x.SapIndex == "ABC");
var list_two = Selectus((ProductTypes x) => x.SapIndex == "ABC");
var list_three = Selectus((ProductCodes x) => x.SapName == "DaName");
or:
var list_one = Selectus<ProductCodes>(x => x.SapIndex == "ABC");
var list_two = Selectus<ProductTypes>(x => x.SapIndex == "ABC");
var list_three = Selectus<ProductCodes>(x => x.SapName == "DaName");
But if it is going to always be the "same" property, like always x.SapIndex (but for different types of x), then Flater's answer looks good.
Otherwise, if you insist, your reflection approach should be possible. Use propety's name, not its index! Let me try:
public static List<T> Selectus<T>(string looking_for)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
const string prop_name = "SapIndex";
var prop = typeof(T).GetProperty(prop_name); // can blow up for bad T
var list_selected_record = select_all_list
.Where(x => (string)(prop.GetValue(x)) == looking_for); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus<ProductCodes>("ABC");
var list_two = Selectus<ProductTypes>("ABC");
you can change code to return just one element
public static T Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.FirstOrDefault();//here one record should be returned
}

Dotnet Neo4JClient: Can I use JsonProperty to change variable aliases in generated cypher?

I have this CypherFluentQuery which returns a custom object CustomFooQueryResult rather than an anonymous type:
var query = new CypherFluentQuery(client)
.Match("(foo:Foo)")
.Return((foo) => new CustomFooQueryResult
{
Foos = foo.CollectAsDistinct<FooDto>(),
FooCount = (int)foo.CountDistinct()
});
private class CustomFooQueryResult
{
[JsonProperty("foos")]
public IEnumerable<FooDto> Foos { get; set; }
[JsonProperty("fooCount")]
public int FooCount { get; set; }
}
private class FooDto
{
public string FooProp { get; set; }
}
Upon inspecting the generated Cypher query, I found that the class property names are used as variable aliases in the generated return statement.
I'm wondering if it is or should be possible to influence the alias name generation. I'd like them to be camelcase, while my class properties remain pascalcase.
I tried using [JsonProperty("fooCount")] but still "FooCount" was generated as alias.
Although this seems like a minor inconvenience, I'm chaining a lot of these queries as subqueries in calls, where I would like to have input and output parameters in camelcase.
This is what I tried:
(using .net5, Neo4JClient 4.1.18)
[Test]
public void TestCustomReturnTypeWithJsonPropertyAttr()
{
// arrange
var expectedCypher = "MATCH (foo:Foo)\r\nRETURN collect(distinct foo) AS foos, count(distinct foo) AS fooCount";
var client = Substitute.For<IRawGraphClient>();
var query = new CypherFluentQuery(client)
.Match("(foo:Foo)")
.Return((foo) => new CustomFooQueryResult
{
Foos = foo.CollectAsDistinct<FooDto>(),
FooCount = (int)foo.CountDistinct()
});
// act
var resultCypher = query.Query.DebugQueryText; // results in: "MATCH (foo:Foo)\r\nRETURN collect(distinct foo) AS Foos, count(distinct foo) AS FooCount"
// assert
Assert.AreEqual(expectedCypher, resultCypher);
}
You can't do that using the class you're using to deserialize into.
The reason the AS is using FooCount is because that's the name of the property in your class.
If you want to control the 'aliasing' to that level you have two options, the first is to use lowerCamelCase for your Property names:
public class X
{
public int fooCount {get;set;}
}
etc
Or you could use anonymous returns:
.Return(foo => new {
fooCount = ...
})
The 100% control way would be to use the Driver that you can access and calling something like RunAsync - but at that point using Neo4jClient is a bit moot.
Part of the point of the client is to mean you don't have to worry about things like this - The JsonProperty stuff is handy if you're doing the whole foo.As<xxx>() type of retrieval (or indeed creating).
Why does the case coming back matter? In practice you would only see it in the Query logs of Neo4j, or if you're putting the DebugQueryText out somewhere - or are you parsing the cypher generated?

How to convert the objects in a query result to custom type

I think the same question must have been asked, but I just don't know how to describe it properly.
I am using Entity Framework, and I have a table called vendor, consisting of many columns:
[vendor]
- [Id] [INT]
- [Name] [NVARCHAR]
- [ApiUrl] [NVARCHAR]
- ...
I am writing a Web Api to expose the function which users can get vendor records by. However, I want to provide only three columns.
So I created a model class, as below:
public class OM_Vendor
{
public int Id { set; get; }
public string Name { set; get; }
public string ApiUrl { set; get; }
}
After building the model classes with EF, the context object of EF has a property named vendors now. Then, in an API controller, I coded like this:
var vendors = this.objEntity.vendors
.Where<OM_Vendor>(v => v.Status == "1")
.OrderBy(v => v.Id)
.Skip(intSkip)
.Take(intLimit)
.ToList();
It doesn't work, neither does this:
var vendors = this.objEntity.vendors
.Where(v => v.Status == "1")
.OrderBy(v => v.Id)
.Skip(intSkip)
.Take(intLimit)
.ToList<OM_Vendor>();
Is there a way to convert the objects returned by the methods like above to my custom type?
Thanks a lot.
In addition, I know that I can get it to work in this way:
var vendors = new List<OM_Vendor>();
vendors = objCont.ExecuteStoreQuery<OM_Vendor>("SELECT * FROM vendor").ToList();
And this way works too:
var vendors = this.objCont.vendors
.Where(v => v.Status == "1")
.OrderBy(v => v.Id)
.Select(
row => new
{
Id= row.Id,
Name = row.Name,
ApiUrl = row.ApiUrl
}
)
.Skip(intSkip)
.Take(intLimit)
.ToList();
Update:
According to #Andrew's answer, I updated my code as below:
public class OM_Vendor
{
public int Id { set; get; }
public string Name { set; get; }
public string ApiUrl { set; get; }
public static implicit operator OM_Vendor(vendor vendor){
return new OM_Vendor
{
Id = vendor.Id,
Name = vendor.Name,
ApiUrl = vendor.ApiUrl
};
}
}
And in my controller class:
List<OM_Vendor> vendors = this.objCont.vendors
.Where(v => v.Status == "1")
.OrderBy(v => v.Id)
.Skip(intSkip)
.Take(intLimit)
.ToList();
I got error saying:
Cannot implicitly convert type
'System.Collections.Generic.List' to
'System.Collections.Generic.List'
What did I miss? As for the explicit cast, I will have to convert each instance explicitly in a loop?
You basically have 3 methods to achieve what you are trying to accomplish.
Use the .Select() linq extension method to create a projection (as is demonstrated by your last example.) Generally a good option, as this will create a SQL expression which only returns the fields which are needed by your Data Transfer Object.
Create a custom cast operator, e.g. public static implicit Operator OM_Vendor(Vendor vendor), which takes in a full Vendor object and assigns it to an instance of OM_Vendor. The main drawback here is that you are essentially retrieving all fields through Entity Framework, only to Flatten the entity and discard many of these fields during the implicit cast.
Use a custom library such as Automapper to automate the process of converting the objects. Suffers from most of the same issues as 2., but having it handled by a library can be helpful if you have many classes you are Flattening.
A more complete code example to demonstrate:
public class OM_Vendor {
...
public static implicit Operator OM_Vendor(Vendor vendor){
return new OM_Vendor {
Id = vendor.Id;
Name = vendor.Name;
ApiUrl = vendor.ApiUrl;
}
}
OM_Vendor om_Vendor = this.objEntity.vendors.Where(
...
List<OM_Vendor> vendors = this.objEntity.vendors.Where(
...
Either of these statements should execute correctly, using the implicit Operator as a Constructor when assigning a Vendor instance to an OM_Vendor. However, it cannot be overstated, this is Flattening the object at the code level, and will result in a larger than necessary SQL query.
This actually should be an explicit operator, since data loss occurs during conversion. Changing implicit Operator to explicit Operator will cause this conversion to require the cast, i.e. OM_Vendor omVendor = (OM_Vendor)vendor;. This makes it much more clear that a conversion is occurring, but makes the code slightly more verbose.
Have you tried projecting with:
.Select(row => new OM_Vendor{ Id=row.Id, Name=row.Name, ApiUrl=row.ApiUrl})

Include coded field value in LINQ query (EF 5)

Not sure of the correct terminology, but is there any way to include a "coded field" that doesn't exist in the table, in an Entity Framework 5 LINQ query?
For example, in SQL you can do this:
SELECT 'Ledger' AS type, (...) FROM table
So that the query result includes a field called 'type' with the value 'Ledger'. Is there a way to do this in a LINQ query?
And before you ask why it has to be in the query, it's because the query is a union of multiple tables and I need a designation of which table the data came from. Without this I will need to query each one separately then merge them.
Yes, you can totally do this.
p.s.w.g has used something that is called an anonymous type. Basically you define the outcome of the query on the fly.
As you can see in his answer, in expression syntax this looks like Select(r => new { type = "Ledger", ... });
In query syntax it looks like this:
from xxx in y
select new { type = "Ledger" };
Behind new, there is no class / type or anything. Neither is type defined. But it will compile as a string natuarlly.
On the other hand you can define a custom ViewModel class for this
public class CustomResultVM
{
//ignoring getter and setter
public int Id;
public string type;
public string name;
}
your select would now be strongly typed and look like this:
Select(r => new CustomResultVM
{
Id = r.xxx,
type = "Ledger",
Name = r.xxxx
});
//query snytax
from xxx in y
select new CustomResultVM
{
Id = r.xxx,
type = "Ledger",
Name = r.xxxx
};
both ways are valid and it depends on what you need at any given point and time.
Sure, you can. It would look a bit like this:
var results = dbContext.Table.Select(r => new { type = "Ledger", ... });
Or if you need a named type, something like this should work:
public class UnionResult
{
string Type { get; set; }
...
}
var results = dbContext.Table.Select(r => new UnionResult { Type = "Ledger", ... });

How to get more than one column in Linq

here is my code:
firstAnswer = p.Answers.Select(z => z.vountcount, z.isSelected).FirstOrDefault()
In the select statement, it returns a syntax error. I am trying to get more then one column.
var firstAnswer = p.Answers.FirstOrDefault().Select(new { VountCount = z.vountcount, IsSelected = z.isSelected });
You must specify a type. Var is the keyword allowing you to instantiate an anonymous type.
You'll either have to create a type or use anonymous types to capture that result:
Anonymous types:
var firstAnswer = p.Answers.Select(z => new { vountcount = z.vountcount, isSelected = z.isSelected }).FirstOrDefault();
The explicit naming is in most cases optional.
Using the var keyword here ensures, that you can assign that anonymous result. If you want to hand that result to some method, it'll get difficult with anonymous types.
Dedicated type:
public class ReducedAnswer
{
public int vountcount { get; set; }
public bool isSelected { get; set; }
public ReducedAnswer()
{
}
}
ReducedAnswer firstAnswer = p.Answers.Select(z => new ReducedAnswer { vountcount = z.vountcount, isSelected = z.isSelected }).FirstOrDefault();
Kept it close to typical LINQ model classes and your naming. Note the use of the type in front of firstAnswer. You could go with var here as well.

Categories