Entity Frameworok generate guid as id and save it - asp.net

I'm trying to save to my table Users let's say, string ID, string email, and string password. The problem is that ID must be a guid that I have to create it and save it and not SQL server. Any ideas how?
I searched but I only found how to make SQL server to create the guid.

First of all, tell Entity framework that you will generate the value of the primary key:
Use DatabaseGenerated Attribute
public class School
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name {get; set;}
...
}
The None option prevents values from being generated by the database automatically in cases where they would otherwise be created.
Furthermore, consider to overwrite DbContext.SaveChanges(). In this procedure ask the ChangeTracker for all elements that are Added. Generate an Id for every Added element. It might be dangerous to let others generate an Id, because they might be adding a constant value or just an auto-increment.
Another possibility would be to generate it within the Add function, but if you do that, then users could change your generated Id. So the proper place is within SaveChanges:
public override int SaveChanges()
{
var addedElements = this.ChangeTracker.Entries
.Where(entry => entry.State == EntityState.Added);
foreach(var addedElement in addedElements)
{
// This will fail: the added element doesn't have a property Id:
addedElement.Id = GenerateId();
}
return base.SaveChanges();
}
For this you have to be certain that every added element has a property Id. The simplest way is to create an interface and let all your tables implement this interface:
public interface IID
{
string Id {get; set;}
}
public class School : IID {...}
public class Student : IID {...}
public class Teacher : IID {...}
public class DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students{get; set;}
public DbSet<Teacher> Teachers {get; set;}
public override int SaveChanges()
{
var addedElements = this.ChangeTracker.Entries.Cast<IID>
.Where(entry => entry.State == EntityState.Added);
foreach(var addedElement in addedElements)
{
addedElement.Id = GenerateId(); // Every Added element implements IId
}
return base.SaveChanges();
}
private string GenerateId()
{
... // TODO: return unique ID, for instance a GUID
}
}

Related

How to set a default value to a column in .Net 3.1 when I create my column

So I am using .Net 3.1, and I have the following class:
public Addresses{
public int AddressID { get; set; }
public String Name {get; set;}
}
and I want to add a new Column to it like this
public Addresses{
public int AddressID { get; set; }
public String Name {get; set;}
public bool IsAddrLocal {get; set;}
}
but I want the new column IsAddrLocal to have a default value of True for all columns that have already been created in the database.
I tried adding doing this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CompanyShippingAddress>(entity =>
{
entity.Property(x=>x.IsAddrLocal).HasDefaultValue(true);
});
}
however it would cause my values to be always true (even the new ones being entered) so I tried to make the field nullable
public Addresses{
public int AddressID { get; set; }
public String Name {get; set;}
public bool? IsAddrLocal {get; set;}
}
Which would cause old addresses to have the value NULL assigned to them and only new ones have the True value assigned to them.
I tried adding a constructor to the class and making the field non-nullable again
public Addresses()
{
IsAddrLocal= true;
}
but this would cause old addresses to get a value of False and new ones a value of True.
How can I make the default value of this field True for new and old values unless later on a User sends a request to update the field for a specific row to false?
From your description, your question is that when adding the IsAddrLocal property, you want to set the default value to true, then the new address without set the IsAddrLocal property and the existed Address in the database should also set the default value. But if the new address has the IsAddrLocal property, it should store its value.
To solve this issue, you can try to use the following method:
Assume you have create the Address table with two columns: AddressID and Name, and insert some value.
Add the IsAddrLocal property in the Addresses model.
public class Addresses
{
[Key]
public int AddressID { get; set; }
public string Name { get; set; }
public bool IsAddrLocal { get; set; } = true; //set the default value.
}
[Note]There is no need to use the HasDefaultValue to set the default value. Remove this part of code from the OnModelCreating method.
Execute the Add-Migration adddefaultvaluerul command to generate the migration file.
Open the adddefaultvaluerul migration file, in the Up method, change the defaultvalue from false to true.
Execute the update-database comment, then you can see the existed Address has been set the default value.
Then, when you insert new address, like this:
var items = new List<Addresses>()
{
new Addresses(){ Name="A1", IsAddrLocal=false },
new Addresses(){ Name="A2"},
new Addresses(){ Name="A3" ,IsAddrLocal=false },
};
_dbcontext.Addresses.AddRange(items);
_dbcontext.SaveChanges();
the output as below:

How to make EF core 6 correctly store derived types with Cosmos DB provider?

I have an entity
public class MyEntity
{
public string Id {get; set;}
public ObjectNode Node;
}
ObjectNode is the base class for specific nodes:
public class ObjectNode
{
public string NodeType {get; set;}
public string Id { get; set; }
}
Then, there are various subclasses of ObjectNode, for example:
public class DocumentNode : ObjectNode
{
public string Path { get; set;}
}
I configure:
modelBuilder.HasDefaultContainer("MyEntity");
I create a MyEntity instance like this:
var ent = new MyEntity {
Id = "entity1",
Node = new DocumentNode {
Id = "node1",
NodeType = "Document"
Path = "some document path"
}
};
When storing it in Cosmos DB, the result is:
{
"Id" : "entity1",
"Node" : {
"Id" : "node1",
"NodeType" : "Document"
}
]
}
The Path property doesn't show up and when deserializing, I'm getting back an ObjectNode instead of a DocumentNode.
Only by changing NodeType Node to Document Node in my model, would I get all properties. But obviously then it won't work for other derived types.
How can this behavior be fixed?
I tried to declare DocumentNode as an entity using modelBuilder.Entity<DocumentNode>, but that yields a runtime error about a missing partition key property - which is right, as I don't want this to be stored as a separate document but just as a nested element.

Using OData how can I sort a property that holds a list?

Here is the problem I need to solve:
I need to display a grid that contains a group of columns that are dynamic, meaning that the number can change depending on the user parameters.
I have attached a sample below as an image to illustrate:
GRID SAME IMAGE
I have these c# POCOs to keep my question simple
public class OrderItem
{
public string ProductName { get; set; }
public string Status { get; set; }
public List<CityOrderInfo> CityOrders { get; set; }
}
public class CityOrderInfo
{
public int OrderCount { get; set; }
}
I have a web api controller that is able to accept the OData request, plus other arguments that the repository accepts. However the problem is that while the parameter $orderby for ProductName and Status works, when I do "$orderby='CityOrders[1]\OrderCount asc' it fails.
public class OrdersControllers : ApiController
{
private readonly IOrdersRepository _repository;
public OrdersControllers(IOrdersRepository repository)
{
this._repository = repository;
}
public IEnumerable<OrderItem> GetOrderItems([FromUri] ODataQueryOptions<OrderItem> oDataQuery)
{
var result = this._repository.GetOrders().ToList();
var queryableData = oDataQuery.ApplyTo(result.AsQueryable());
var transformedData = queryableData as IEnumerable<OrderItem>;
return transformedData;
}
}
The reason I opted to hold the city orders in list is because I thought it would too painful to make a POCO with every city in the USA as a property so instead made it more generic.
The question is how can a sort on a property that holds a list using OData? Is this possible? I keep getting syntax error at position n. As of now I have not found an answer.

The member with identity does not exist in the metadata collection. Parameter name: identity

We are using EF Code First 4.3.1.
We are developing an ASP.NET Web Role referring to multiple class libraries.
There are two class libraries each containing classes and an individual DBcontext.
Lets say the Library1 has classes A and B.
DBcon1: DbSet and DbSet
Lets say the Library2 has classes C and D.
Class C{
[Key]
public int CId{ get; set;}
[Required]
public virtual A referencedA {get; set;}
}
DBcon2: DbSet<C> and DbSet<D>
When I try to use the DBcon2 as such:
using (var con = new DBcon2())
{
C vr = new C();
vr.CId= 1;
vr.referencedA = DBCon1.As.First();
con.Cs.Add(vr);
con.SaveChanges();
}
I get an exception as:
"The member with identity does not exist in the metadata collection.
Parameter name: identity"
Both DBCon1 and DBcon2 are using the sane SQL Server Database "SampleDB".
Please point me in the right direction.
I got this error and fixed it by not trying to set the navigation property in the related table, just set the foreign key id instead
eg
public class Student()
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int CourseId { get; set; }
public virtual Course Course { get; set; }
}
public class Course()
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Main code:
var myCourse = new Course();
var myCourseId = 1;
var student = new Student() {
CourseId = myCourseId
// Course = myCourse <-- this would cause the error
}
Might not be your issue but maybe it will point you in the right direction and hopefully will help someone else.
The exception is a bit cryptic, but pretty clear if you realise that a context needs information about entities (metadata) to be able to write sql statements. Thus, your DBcon2 context has no clue where to find the primary key of an A, because it has no metadata about A.
You could however set an integer property A_Id (or the like), but then you'll have to write custom code to resolve it to an A.
Another option is to merge (parts of) the contexts, if possible.

Object Data Source with Reporting Services in Visual Studio

I'm working on a site where we want to include a pie chart on a page. Right now I'm working on implementing that through Reporting Services (RDLC file) with an object data source.
The SelectMethod of my object data source is a method that returns a list of business objects; lets say a List<Alpha> and Alpha has a sub object Beta with a Name property. In my report definition I have set the Category groups to be: =Fields!Beta.Value.Name this means that Alpha.Beta.Name are my pie slices. I got the following error:
An error has occurred during report processing. The Group expression for the grouping 'chart1_CategoryGroup1' contains an error: Object variable or With block variable not set.
I was able to confirm this is because Beta is nullable and was able to fix the issue by updating the object Alpha to return a new Beta() if the Beta property is null. This solution is not ideal though because there are other places in my code where I need Beta to be null if it doesn't have a value yet.
Is there a way to update the report definition to accept a null property as valid? Ideally I would like to specify the value as "Not Set" if Beta is null.
I had similar problem as yours, and I solved it using Null Object Refactoring ( many thanks to Martin Fowler's book Refactoring :)).
Here you can find nice example Null object refactoring
So you could create class NullableBeta that inherits Beta, while properties Name and e.g. IsNullable are virtual on Beta entity.
public class Beta
{
public virtual Name{ get; set;}
public virtual IsSet{ get{return true;}}
}
public class NullableBeta:Beta
{
public override Name
{
get{
return "Not Set";
}
set{}
}
public override IsSet{ get{ return false;}}
}
Now, if Beta is not set on Alfa entity, you could return instance of NullableBeta entity. In reports, you would have "Not Set" instead of empty string, and on places where you are setting Beta entity to Alfa you can check IsSet property instead of checking if Beta is null.
Hope this was helpful
I had similar problem as yours, and I solved it using Null Object Refactoring ( many thanks to Martin Fowler's book Refactoring :)). Here you can find nice example Null object refactoring
I first deal with this shortcomings of SSRS/RDLC by implementing the Null object pattern as well.
Implementing this manually is of course too much effort when more then one or two domain objects are involved.
However, since I am already using AutoMapper, #LucianBargaoanu correctly pointed out in the comments that null objects are natively supported as an opt-in feature by AutoMapper, so there is no explicit implementation needed.
I therefore use AutoMapper with its AllowNullDestinationValues, AllowNullCollections, PreserveReferences(), NullSubstitute and ForAllPropertyMaps() features, to map all my domain classes to report specific classes and substitute all null references to either null objects (when mapping domain object null references to report objects) or reasonable default values (e.g. an empty string for null strings or the default value of the underlying primitive type for Nullable<PrimitiveType>).
Here is some sample code to demonstrate the approach:
namespace Domain
{
public class MyClass
{
public int Id {get; set;}
public string Name {get; set;} // could be null
public string Code {get; set;} // could be null
public decimal? SomeNullableValue {get; set;} // could be null
public MyOtherClass OptionalOtherClass {get; set;} // could be null
}
public class MyOtherClass
{
public int OtherId {get; set;}
public string OtherName {get; set;} // could be null
public decimal? SomeOtherNullableValue {get; set;} // could be null
}
}
namespace ReportViewModels
{
[Serializable]
public class MyClass
{
public int Id {get; set;}
public string Name {get; set;} // should not be null (but empty)
public string Code {get; set;} // should not be null (but empty)
public decimal? SomeNullableValue {get; set;} // should not be null (but default(decimal))
public string CommonName
=> (Name + " " + Code).Trim();
public MyOtherClass OptionalOtherClass {get; set;} // should not be null (but a MyOtherClass null object)
}
[Serializable]
public class MyOtherClass
{
public int OtherId {get; set;}
public string OtherName {get; set;} // should not be null (but empty)
public decimal? SomeOtherNullableValue {get; set;} // should not be null (but default(decimal))
}
}
public partial class Form1 : Form
{
private Context _context;
private ReportObjectGenerator _reportObjectGenerator;
public Form1(Context context, ReportObjectGenerator reportObjectGenerator)
{
_context = context;
_reportObjectGenerator = reportObjectGenerator;
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var myDomainObjects = context.MyClass
.Include(e => e.OptionalOtherClass)
.ToList();
var myReportViewModels = _reportObjectGenerator.GetReportObjects<Domain.MyClass, ReportViewModels.MyClass>(myDomainObjects);
components ??= new System.ComponentModel.Container();
//reportViewer1.LocalReport.ReportEmbeddedResource = "MyNamespace.Report1.rdlc";
reportViewer1.LocalReport.ReportPath = "Report1.rdlc";
reportViewer1.LocalReport.DataSources.Clear();
reportViewer1.LocalReport.DataSources.Add(
new ReportDataSource
{
Name = "MyClassDataSet",
Value = new BindingSource(components)
{
DataMember = "MyClass",
DataSource = myReportViewModels
}
});
reportViewer1.RefreshReport();
}
}
public class ReportObjectGenerator
{
public List<TDestination> GetReportObjects<TSource, TDestination>(
IEnumerable<TSource> sourceObjects)
{
var domainNamespace = typeof(TSource).Namespace ?? throw new InvalidOperationException();
var reportNamespace = typeof(TDestination).Namespace ?? throw new InvalidOperationException();
var mapper = new MapperConfiguration(
cfg =>
{
cfg.AllowNullDestinationValues = false;
cfg.AllowNullCollections = false;
var allTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).ToList();
var allDomainTypes = allTypes.Where(t => t.Namespace?.StartsWith(domainNamespace) ?? false).ToList();
var allReportTypes = allTypes.Where(t => t.Namespace?.StartsWith(reportNamespace) ?? false).ToList();
foreach (var reportClassType in allReportTypes)
{
var domainClassType = allDomainTypes.Single(t => t.Name == reportClassType.Name);
cfg.CreateMap(domainClassType, reportClassType)
.PreserveReferences();
}
// If we want to set the default value of the underlying type of Nullable<UnderlyingType>
// properties in case they would be null, than AllowNullDestinationValues is not enough and we
// need to manually replace the null value here.
cfg.ForAllPropertyMaps(
pm => pm.SourceMember.GetMemberType().IsNullableType(),
(p, _) => p.NullSubstitute ??= Activator.CreateInstance(p.SourceMember.GetMemberType().GetTypeOfNullable()));
})
.CreateMapper();
return mapper.Map<IEnumerable<TSource>, List<TDestination>>(sourceObjects);
}
}

Resources