I am looking for tips to improve the readability of my code (which is written in C#). My main concern is usage of consecutive if statements and how should I replace them.
My code:
private Dictionary<Guid, CommerceMediaFileAssociation<T>> GetMediaToContentsAssociations<T>(ref bool stopSignaled, CatalogContentBase catalog, CultureInfo culture, Action<string> onStatusChanged = null)
where T : MediaData
{
IEnumerable<ContentReference> descendentReferences = _contentLoader.GetDescendents(catalog.ContentLink);
var associations = new Dictionary<Guid, CommerceMediaFileAssociation<T>>();
if (descendentReferences.Any())
{
var descendentProducts = _contentLoader.GetItems<BaseProduct>(descendentReferences, culture);
foreach (var product in descendentProducts)
{
if (stopSignaled)
{
onStatusChanged?.Invoke($"Reindexing canceled.");
break;
}
if (product is IAssetContainer assetContainer && (assetContainer?.CommerceMediaCollection?.Any() ?? false))
{
foreach (CommerceMedia media in assetContainer.CommerceMediaCollection)
{
PermanentLinkMap mediaLinkMap = _permanentLinkMapper.Find(media.AssetLink);
if ((mediaLinkMap?.Guid != null) && mediaLinkMap.Guid != Guid.Empty)
{
var productInformation = ProductUtilities.GetProductCategoriesAndPriority(product);
if (associations.TryGetValue(mediaLinkMap.Guid, out CommerceMediaFileAssociation<T> commerceMediaFileAssociations))
{
commerceMediaFileAssociations.Products.Add($"{product.ContentGuid.ToString()}||{product.MetaTitle}");
if (productInformation.Categories?.Any() ?? false)
{
foreach (string category in productInformation.Categories)
{
commerceMediaFileAssociations.ProductCategories.Add(category);
}
}
associations[mediaLinkMap.Guid] = commerceMediaFileAssociations;
}
else
{
var commerceMediaFileAssociation = new CommerceMediaFileAssociation<T>();
commerceMediaFileAssociation.Products.Add($"{product.ContentGuid.ToString()}||{product.MetaTitle}");
foreach (string category in productInformation.Categories)
{
commerceMediaFileAssociation.ProductCategories.Add(category);
}
associations.Add(mediaLinkMap.Guid, commerceMediaFileAssociation);
}
associations[mediaLinkMap.Guid].Priority = productInformation.Priority;
}
}
}
if (product is RockstarProduct rockstar)
{
var files = rockstar.Rockstar_Product_Product_Documents.FilteredItems.Select(x => x.GetContent() as IContentMedia) ?? new List<IContentMedia>();
foreach (var file in files)
{
PermanentLinkMap mediaLinkMap = _permanentLinkMapper.Find(file.ContentLink);
if ((mediaLinkMap?.Guid != null) && mediaLinkMap.Guid != Guid.Empty)
{
var productInformation = ProductUtilities.GetProductCategoriesAndPriority(product);
if (associations.TryGetValue(mediaLinkMap.Guid, out CommerceMediaFileAssociation<T> commerceMediaFileAssociations))
{
commerceMediaFileAssociations.Products.Add($"{product.ContentGuid.ToString()}||{product.MetaTitle}");
if (productInformation.Categories?.Any() ?? false)
{
foreach (string category in productInformation.Categories)
{
commerceMediaFileAssociations.ProductCategories.Add(category);
}
}
associations[mediaLinkMap.Guid] = commerceMediaFileAssociations;
}
else
{
var commerceMediaFileAssociation = new CommerceMediaFileAssociation<T>();
commerceMediaFileAssociation.Products.Add($"{product.ContentGuid.ToString()}||{product.MetaTitle}");
foreach (string category in productInformation.Categories)
{
commerceMediaFileAssociation.ProductCategories.Add(category);
}
associations.Add(mediaLinkMap.Guid, commerceMediaFileAssociation);
}
associations[mediaLinkMap.Guid].Priority = productInformation.Priority;
}
}
}
}
}
return associations;
}
How should I change it in order to make it clean, understandable and maintainable? Should I use guard clauses in this case?
You have a lot of code duplications. Some code parts are almost equivalent. It is easy to make them equal. This allows you extract them to another method.
Also, you have if-else statements where the else-part has a lot in common with the if-part where basically only the first statement differs (after having made the almost equal statements equal). You can extract these statements and execute them after the if-else. Then if-part becomes empty. You can invert the if-else and then drop the else part.
There is also no point in testing a collection with Any() before looping through it. If the collection is empty, the loop body will not be executed anyway.
The extracted method:
private void AddCategoriesAndProducts<T>(Dictionary<Guid, CommerceMediaFileAssociation<T>> associations, BaseProduct product, string link) where T : MediaData
{
PermanentLinkMap mediaLinkMap = _permanentLinkMapper.Find(link);
if (mediaLinkMap?.Guid != null && mediaLinkMap.Guid != Guid.Empty) {
var productInformation = ProductUtilities.GetProductCategoriesAndPriority(product);
if (!associations.TryGetValue(mediaLinkMap.Guid, out CommerceMediaFileAssociation<T> commerceMediaFileAssociations)) {
commerceMediaFileAssociations = new CommerceMediaFileAssociation<T>();
}
commerceMediaFileAssociations.Products.Add($"{product.ContentGuid}||{product.MetaTitle}");
if (productInformation.Categories != null) {
foreach (string category in productInformation.Categories) {
commerceMediaFileAssociations.ProductCategories.Add(category);
}
}
associations[mediaLinkMap.Guid] = commerceMediaFileAssociations;
associations[mediaLinkMap.Guid].Priority = productInformation.Priority;
}
}
The simplified method:
private Dictionary<Guid, CommerceMediaFileAssociation<T>> GetMediaToContentsAssociations<T>(ref bool stopSignaled, CatalogContentBase catalog, CultureInfo culture, Action<string> onStatusChanged = null)
where T : MediaData
{
IEnumerable<ContentReference> descendentReferences = _contentLoader.GetDescendents(catalog.ContentLink);
var associations = new Dictionary<Guid, CommerceMediaFileAssociation<T>>();
if (descendentReferences.Any()) {
var descendentProducts = _contentLoader.GetItems<BaseProduct>(descendentReferences, culture);
foreach (var product in descendentProducts) {
if (stopSignaled) {
onStatusChanged?.Invoke($"Reindexing canceled.");
break;
}
if (product is IAssetContainer assetContainer && assetContainer.CommerceMediaCollection != null) {
foreach (CommerceMedia media in assetContainer.CommerceMediaCollection) {
AddCategoriesAndProducts(associations, product, media.AssetLink);
}
}
if (product is RockstarProduct rockstar) {
var files = rockstar.Rockstar_Product_Product_Documents.FilteredItems.Select(x => x.GetContent() as IContentMedia) ?? new List<IContentMedia>();
foreach (var file in files) {
AddCategoriesAndProducts(associations, product, file.ContentLink);
}
}
}
}
return associations;
}
These methods are still relatively complex and could be split into even smaller methods. Methods like AddAssetProducts and AddRockStartProducts.
Related
Hi I am using Entity Framework Code First, I have a collection of Entities that need to be saved, but I have my EF Repository created as below
public T Create(T item)
{
try
{
if (ufb != null && ufb.CurrentUser != null)
{
SetValue("CreatedByUserId", item, ufb.CurrentUser.Id);
SetValue("UpdatedByUserId", item, ufb.CurrentUser.Id);
}
SetValue("DateCreated", item, DateTime.Now);
SetValue("DateUpdated", item, DateTime.Now);
var newEntry = this.DbSet.Add(item);
this.Context.Database.Log = message => LogHandler.LogInfo(1111, message);
try
{
this.Context.SaveChanges();
}
catch (Exception ex)
{
LogHandler.LogInfo(2501, ex.Message);
}
BuildMetaData(item, true, true);
return newEntry;
}
catch (DbEntityValidationException dbEx)
{
// http://forums.asp.net/t/2014382.aspx?Validation+failed+for+one+or+more+entities+See+EntityValidationErrors+property+for+more+details+
string msg = string.Empty;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
msg += validationError.PropertyName;
msg += "---";
msg += validationError.ErrorMessage;
msg += "||";
}
}
throw new Exception("7777 CREATE EntityValidationErrors: " + msg);
}
}
My calling method is as below:
public List<VehicleInfo> Create(List<VehicleInfo> vehicleInfos, string Entity, int EntityId)
{
bool vehicleExists = false; List<VehicleInfo> newVehicleInfos = null;
if ((vehicleInfos != null) && (vehicleInfos.Count > 0))
{
newVehicleInfos = new List<VehicleInfo>();
foreach (VehicleInfo vehicleInfo in vehicleInfos)
{
vehicleExists = false;
if (vehicleInfo != null)
{
vehicleExists = this.VehicleExists(vehicleInfo.VehicleId, Entity, EntityId);
vehicleInfo.Entity = Entity;
vehicleInfo.EntityId = EntityId;
VehicleInfo v = this.UnitOfWork.VehicleInfoRepository.Create(vehicleInfo);
newVehicleInfos.Add(v);
}
}
}
return newVehicleInfos;
}
Hence when I am calling repositories create method for multiple times, its throwing me the above error, any help or suggestion would be very helpful, please thank you.
void BuildMetaDataNoThread(object item, bool active, bool isNew = false)
{
if (item.GetType() != typeof(JsonObject))
{
var dm = new DataAccessUnitOfWork(Constants.DefaultConnection);
var qtype = item.GetType();
if (qtype.BaseType.BaseType != null)
{
if ((isNew && qtype.BaseType.Name == typeof(ModelBase).Name) | qtype.BaseType.BaseType.Name == typeof(ModelBase).Name)
{
Thread.Sleep(500);
//collect data
var element = (ModelBase)item;
element.BuildMetaData(DataRequestType.CurrentItem);
var records = ModelBase.MetaData;
ModelBase.MetaData = new List<ModelRecord> { };
if (records == null) return;
foreach (ModelRecord r in records)
{
if (r!=null)
{
var jsr = new JavaScriptSerializer();
//object meta = r;
object rdata = r.Data;
var type = rdata.GetType();
var token = type.BaseType.Name;
List<string> include = r.Include;
// Cycle-through clieanup of models to be encoded into Json Data.
// this helper eliminates infinate relations by including records specified
// by a list of strings
if (include.Where(x => x.Contains("CreatedByUser")).Count() == 0)
include.Add("CreatedByUser");
if (include.Where(x => x.Contains("UpdatedByUser")).Count() == 0)
include.Add("UpdatedByUser");
var data = ClassCloner.CollectData(rdata, include);
List<string> tags = ClassCloner.CollectTags(data);
string _tags = "";
tags.ForEach((xtm) =>
{
_tags += xtm + ',';
});
var json = jsr.Serialize(data);
int id = 0;
//get identity
foreach (var prop in type.GetProperties())
{
if (id == 0)
{
foreach (var cp in prop.CustomAttributes)
{
if (cp.AttributeType.Name == "KeyAttribute")
{
var _id = ((Dictionary<string, object>)data)[prop.Name];
id = (int)_id;
break;
}
}
}
else { break; }
}
var query = dm.JsonObjectRepository.GetAll();
var key = "_" + token;
var _data = (Dictionary<string, object>)data;
var ExistingMetaData = (from x in query where x.SourceKey == key && x.SourceId == id select x).FirstOrDefault();
if (ExistingMetaData != null)
{
if (_data.ContainsKey("DateUpdated")) ExistingMetaData.Date = (DateTime)_data["DateUpdated"];
ExistingMetaData.SourceData = data;
ExistingMetaData.Encode();
ExistingMetaData.Active = active;
ExistingMetaData.SearchTags = _tags;
dm.JsonObjectRepository.Update(ExistingMetaData);
}
else
{
var newData = new JsonObject
{
Active = true,
Date = (DateTime)_data["DateUpdated"],
SourceData = data,
SourceId = id,
SourceKey = key,
SearchTags = _tags,
TargetKey = "GlobalSearchMetaData"
};
newData.Encode();
dm.JsonObjectRepository.Create(newData);
}
}
}
}
}
}
}
void BuildMetaData(object dataRecord, bool active, bool isNew)
{
new Thread((item) => { BuildMetaDataNoThread(item, active, isNew); }).Start(dataRecord);
}
Thanks to Roslyn, having access to .Net compiler, in our project Gcop we need to have list of references where are calling a method.
VS IDE shows the reference places very suitable like this:
Actually I want to understand which class/name space and even assembly is calling my method by Roslyn C# syntax.
Currently I have access to MethodSymbol here :
var methodSymbol = semanticModel.GetSymbolInfo(invocation).Symbol as IMethodSymbol;
what should I write to get access to references of this method?
//added recently for double check
var solutionPath = Utilities.DteExtensions.SolutionFullPath;
var msWorkspace = Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.Create();
var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result;
var result = new List<ReferenceLocation>();
var refrences = SymbolFinder.FindReferencesAsync(methodSymbol, solution).Result;
foreach (var refernc in refrences)
{
foreach (var location in refernc.Locations)
{
result.Add(location);
}
}
///???????? why the result is empty ?
You're looking for the FindReferencesAsync() methods on the SymbolFinder class.
I tested this approach but is not quick , and can not find some syntax
protected override void Analyze(SyntaxNodeAnalysisContext context)
{
NodeToAnalyze = context.Node;
Method = context.Node as MethodDeclarationSyntax;
// only public method should be checked
if (!IsModifiersValid) return;
var methodSymbol = context.SemanticModel.GetDeclaredSymbol(Method) as IMethodSymbol;
var solutionPath = Utilities.DteExtensions.SolutionFullPath;
var msWorkspace = Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.Create();
var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result;
// looking in all projects inside of one solution
var allDocumentsInEntireSolution = solution.Projects.SelectMany(it => it.Documents);
//skip rule when in entire solution we have web form project
if (allDocumentsInEntireSolution.Any(x => x.Name == "Default.aspx.cs")) return;
//Looking for all references
var refrencesFound = FindAllMethodReferences(Method.GetName(), solution);
if (refrencesFound.Count() ==0)
ReportDiagnostic(context, Method);
else
{
var xyx = refrencesFound.Count();
}
}
IEnumerable<ReferenceLocation> FindAllMethodReferences(string methodName, Solution solution)
{
IMethodSymbol methodSymbol = null;
bool found = false;
foreach (var project in solution.Projects)
{
foreach (var document in project.Documents)
{
var model = document.GetSemanticModelAsync().Result;
var methodInvocation = document.GetSyntaxRootAsync().Result;
try
{
var nodes = methodInvocation.DescendantNodes().OfType<InvocationExpressionSyntax>().Where(x => x.Expression.ToString().Contains(methodName));
foreach (var node in nodes)
{
if (node == null) continue;
var member = node?.Expression as MemberAccessExpressionSyntax;
if (member == null)
continue;
var name = member?.Name?.ToString();
if (name.IsEmpty()) continue;
if (name != methodName) continue;
methodSymbol = model.GetSymbolInfo(node).Symbol as IMethodSymbol;
found = true;
break;
}
}
catch (Exception exp)
{
// Swallow the exception of type cast.
// Could be avoided by a better filtering on above linq.
continue;
}
}
if (found) break;
}
if (found == false) return Enumerable.Empty<ReferenceLocation>();
if (methodSymbol == null) return Enumerable.Empty<ReferenceLocation>();
var result = new List<ReferenceLocation>();
var refrences = SymbolFinder.FindReferencesAsync(methodSymbol, solution).Result;
foreach (var refernc in refrences)
{
foreach (var location in refernc.Locations)
{
result.Add(location);
}
}
return result;
}
I prefer to change following to make it work:
var nodes = methodInvocation.DescendantNodes().OfType<InvocationExpressionSyntax>()
.Where(x => x.Expression.ToString().Contains(methodName));
To:
var nodes = new List<InvocationExpressionSyntax>();
foreach (var methodInvocationExpression in methodInvocation.DescendantNodes().OfType<InvocationExpressionSyntax>())
{
if(methodInvocationExpression.DescendantNodes().OfType<MemberAccessExpressionSyntax>().Any(x => x.Name.Identifier.ValueText == methodName))
{
nodes.Add(methodInvocationExpression);
}
}
While using Dapper for multiple Query:
var result = sqlConnection.QueryMultiple(query, Parameters, commandType: commandType);
How can i get the table count returned from query? It has two overloaded implementation of .Read() method, which each time called, moves to next available result set (No result.Count() property). Eventually i want to put that number in a loop to iterate as many time as number of tables returned from query.
var reader = this.DbConnection.QueryMultipleAsync(sql, Params, commandType: CommandType.StoredProcedure).Result;
if(reader.IsConsumed == false)
{
DeviceTypeReport = reader?.ReadAsync<dynamic>().Result;
}
This is probably what you are looking for hope it helps.
This is probably what you are looking for hope it helps.
List<dynamic> data = new List<dynamic>();
while (reader.IsConsumed == false)
{
data.Add(await reader?.ReadAsync<dynamic>());
}
int totalRecordSet = data.Count;
public List NotificationExecuteMultiple(OutdoorRequest objreq, IConfiguration configuration)
{
var lst = new List<dynamic>();
using (DbConnection connection = new MySqlConnection(configuration.GetConnectionString("SquareHrConn")))
{
using (var dr = connection.QueryMultiple(ProcedureName, GetParamenter(objreq), commandType: CommandType.StoredProcedure))
{
while (dr.IsConsumed == false)
{
lst.Add(dr.Read());
}
}
}
return lst;
}
Consider the follwoing method to cover all cases
protected List<object> ExecuteMultiQuery<A, B, C, D, E, F, G, H, I, J>(string procedureName, DynamicParameters param = null)
{
List<object> result = new List<object>();
using (var connection = new SqlConnection(ConnectionManager.ConnectionString))
{
try
{
connection.Open();
using (var multi = connection.QueryMultiple(procedureName, param, commandType: CommandType.StoredProcedure, commandTimeout: 120))
{
var varA = multi.Read<A>();
if (varA != null) { result.Add(varA.ToList()); }
var varB = multi.Read<B>();
if (varB != null) { result.Add(varB.ToList()); }
var varC = multi.Read<C>();
if (varC != null) { result.Add(varC.ToList()); }
var varD = multi.Read<D>();
if (varD != null) { result.Add(varD.ToList()); }
var varE = multi.Read<E>();
if (varE != null) { result.Add(varE.ToList()); }
var varF = multi.Read<F>();
if (varF != null) { result.Add(varF.ToList()); }
var varG = multi.Read<G>();
if (varG != null) { result.Add(varG.ToList()); }
var varH = multi.Read<H>();
if (varH != null) { result.Add(varH.ToList()); }
var varI = multi.Read<I>();
if (varI != null) { result.Add(varI.ToList()); }
var varJ = multi.Read<J>();
if (varJ != null) { result.Add(varJ.ToList()); }
//if (varA != null) { result.Add(varA.ToList()); }
//if (resultSets > 1) { result.Add(multi.Read<B>().ToList()); }
//if (resultSets > 2) { result.Add(multi.Read<C>().ToList()); }
//if (resultSets > 3) { result.Add(multi.Read<D>().ToList()); }
//if (resultSets > 4) { result.Add(multi.Read<E>().ToList()); }
//if (resultSets > 5) { result.Add(multi.Read<F>().ToList()); }
//if (resultSets > 6) { result.Add(multi.Read<G>().ToList()); }
//if (resultSets > 7) { result.Add(multi.Read<H>().ToList()); }
//if (resultSets > 8) { result.Add(multi.Read<I>().ToList()); }
//if (resultSets > 9) { result.Add(multi.Read<J>().ToList()); }
return result;
}
}
catch (System.Exception e)
{
string message = e.Message;
}
}
return result;
}
here is my code :
var allEles = webBrowser1.Document.All;
foreach (HtmlElement item in allEles)
{
if (item.TagName.ToLower() == "div")
{
if(//Here i want to check if div has a background-image css property)
{
//do anything
}
}
}
i searched a lot to no avail :(
I wrote my own extension method :
public static class Extensions
{
public static bool hasBackgroundImage(this HtmlElement ele, string cssFolderPath)
{
string styleAttr = ele.Style.ToLower();
if (styleAttr.IndexOf("background-image") != -1 || styleAttr.IndexOf("background") != -1)
{
if (styleAttr.IndexOf("url") != -1)
{
return true;
}
}
string[] classes = ele.GetAttribute("className").Split(' ');
foreach (string className in classes)
{
if (className.Trim() == "")
{
continue;
}
System.IO.DirectoryInfo d = new System.IO.DirectoryInfo(cssFolderPath);
foreach (System.IO.FileInfo item in d.GetFiles().Where(p => p.Extension == ".css"))
{
string cssFile = System.IO.File.ReadAllText(item.FullName);
int start = cssFile.IndexOf(className);
if (start != -1)
{
string sub = cssFile.Substring(start + className.Length);
int end = sub.IndexOf('}');
string cssProps = sub.Substring(1, end).Replace("{", "").Replace("}", "").ToLower();
if (cssProps.IndexOf("background-image") != -1 || cssProps.IndexOf("background") != -1)
{
if (cssProps.IndexOf("url") != -1)
{
return true;
}
}
}
}
}
return false;
}
}
and now i can call my method :
var allEles = webBrowser1.Document.All;
foreach (HtmlElement item in allEles)
{
if (item.TagName.ToLower() == "div")
{
if(item.hasBackgroundImage("myCssFolderPathHere"))
{
//do anything
}
}
}
but this working only if am running a local html file .... because you must throw the css folder path as a parameter in my extension method , and thats what i was looking for :)
Is there a way to query an XmlSchema or XmlSchemaSet for a list of available tags/attributes at a certain point in the XML? So say my cursor is between <b> and </b> and my schema only allows for a <c/> element there, can I figure that out using anything built in to C#?
<tagset>
<a></a>
<b><!-- CURSOR IS HERE --></b>
</tagset>
There is a way, but the Xml Schema specification is complex so it will take some effort and a few hundred lines of code.
The GetExpectedParticles method of the .NET XmlSchemaValidator class is the key part to a solution. This uses the XmlSchemaSet, passed as an argument, to return a set of XmlSchemaObject instances.
Before you can call this method you need to build a node path to your cursor location which must include ancestor elements and their preceding siblings and also the preceding siblings at the current nesting level. This node path is used to set the context for the schema validator.
After GetExpectedParticles has been called you need to process the particles. For instance, check if each the expected particle is a member of a substitution group, and check whether the expected particle is a restricted simple type that's an enumeration.
It's probably best to separate out code that fetches expected elements and attributes respectively.
The following incomplete code snippet includes the GetExpectedParticles method call, this only caters for element tag content, not attributes:
public static List<XmlSchemaObject> XsdExpectedElements(XmlSchemaSet schemaSet,
List<NodeDescriptor> nodePath)
{
List<XmlSchemaObject> elementNames = new List<XmlSchemaObject>();
NameTable nt = new NameTable();
XmlNamespaceManager manager = new XmlNamespaceManager(nt);
XmlSchemaValidator validator = new XmlSchemaValidator(nt, schemaSet, manager, XmlSchemaValidationFlags.None);
// event handler sets validationErrorFound local field
validator.ValidationEventHandler += new ValidationEventHandler(validator_ValidationEventHandler);
validator.Initialize();
XmlSchemaInfo xsInfo = new XmlSchemaInfo();
int i = 0;
foreach (nodeDescriptor nameUri in nodePath)
{
validator.ValidateElement(nameUri.LocalName, nameUri.NamespaceUri, xsInfo);
if ((i >= siblingPosition && siblingPosition > -1) || nameUri.Closed)
{
validator.SkipToEndElement(null);
}
else
{
validator.ValidateEndOfAttributes(null);
}
i++;
}
XmlSchemaParticle[] parts = validator.GetExpectedParticles();
if (parts.Length == 0)
{
bool hasElements = true;
bool elementClosed = nodePath[nodePath.Count - 1].Closed;
if (elementClosed) // we're outside the element tags
{
hasElements = true;
}
else if (xsInfo.SchemaType is XmlSchemaSimpleType)
{
hasElements = false;
}
else
{
XmlSchemaComplexType xsCt = xsInfo.SchemaType as XmlSchemaComplexType;
XmlSchemaContentType xsContent = (XmlSchemaContentType)xsCt.ContentType;
if (xsContent == XmlSchemaContentType.TextOnly)
{
hasElements = false;
}
}
if (!hasElements)
{
expectedType = XmlEditor.expectedListType.elementValue;
if (xsInfo.SchemaElement != null)
{
elementNames.Add(xsInfo.SchemaElement);
}
}
return elementNames;
}
foreach (XmlSchemaObject xso in parts)
{
if (xso is XmlSchemaElement)
{
XmlSchemaElement xse = (XmlSchemaElement)xso;
if (subGroupList.ContainsKey(xse.QualifiedName))
{
List<XmlSchemaElement> xses = subGroupList[xse.QualifiedName];
foreach (XmlSchemaElement xseInstance in xses)
{
elementNames.Add(xseInstance);
}
}
else
{
elementNames.Add(xse);
}
}
else if (xso is XmlSchemaAny)
{
XmlSchemaAny xsa = (XmlSchemaAny)xso;
foreach (XmlSchema xs in schemaSet.Schemas())
{
if (xs.TargetNamespace == xsa.Namespace)
{
foreach (XmlSchemaElement xseAny in xs.Elements)
{
elementNames.Add(xseAny);
}
}
}
}
}
}
The following (incomplete) code snippet shows how to get expected enumerated values from a particle:
private List<string> ExpectedEnumValues(XmlSchemaObject xsso)
{
XmlSchemaSimpleType xst = null;
XmlSchemaComplexType xsCt = null;
List<string> values = new List<string>();
if (xsso == null)
{
return values;
}
if (xsso is XmlSchemaAttribute)
{
XmlSchemaAttribute xsa = (XmlSchemaAttribute)xsso;
xst = xsa.AttributeSchemaType;
}
else
{
XmlSchemaElement xse = (XmlSchemaElement)xsso;
XmlSchemaType gxst = xse.ElementSchemaType;
if (gxst is XmlSchemaSimpleType)
{
xst = (XmlSchemaSimpleType)gxst;
}
else if (gxst is XmlSchemaComplexType)
{
xsCt = (XmlSchemaComplexType)gxst;
}
else
{
return values;
}
}
if(xst != null)
{
if (xst.TypeCode == XmlTypeCode.Boolean)
{
values.Add("true");
values.Add("false");
}
else
{
ProcessXmlSimpleType(xst, values);
}
}
else if (xsCt != null)
{
XmlSchemaContentType xsContent = (XmlSchemaContentType) xsCt.ContentType;
XmlSchemaContentModel xsModel = (XmlSchemaContentModel)xsCt.ContentModel;
if (xsModel is XmlSchemaSimpleContent)
{
XmlSchemaSimpleContent xsSC = (XmlSchemaSimpleContent)xsModel;
XmlSchemaContent xsRE = xsSC.Content;
if (xsRE != null)
{
if (xsRE is XmlSchemaSimpleContentRestriction)
{
XmlSchemaSimpleContentRestriction xsCCR = (XmlSchemaSimpleContentRestriction)xsRE;
foreach (XmlSchemaObject xso in xsCCR.Facets)
{
if (xso is XmlSchemaEnumerationFacet)
{
XmlSchemaEnumerationFacet xsef = (XmlSchemaEnumerationFacet)xso;
values.Add(xsef.Value);
}
}
}
}
}
else
{
XmlSchemaComplexContent xsCC = (XmlSchemaComplexContent)xsModel;
XmlSchemaContent xsRE = xsCC.Content;
if (xsRE != null)
{
if (xsRE is XmlSchemaComplexContentRestriction)
{
XmlSchemaComplexContentRestriction xsR = (XmlSchemaComplexContentRestriction)xsRE;
}
else if (xsRE is XmlSchemaComplexContentExtension)
{
XmlSchemaComplexContentExtension xsE = (XmlSchemaComplexContentExtension)xsRE;
}
}
}
}
return values;
}
And to process a simple type:
private static void ProcessXmlSimpleType(XmlSchemaSimpleType xst, List<string> values)
{
if (xst == null)
{
return;
}
XmlSchemaSimpleTypeContent xsstc = xst.Content;
if (xsstc is XmlSchemaSimpleTypeRestriction)
{
XmlSchemaSimpleTypeRestriction xsr = (XmlSchemaSimpleTypeRestriction)xsstc;
XmlSchemaObjectCollection xsoc = xsr.Facets;
XmlSchemaSimpleType bastTypeOfRestiction = xsr.BaseType;
foreach (XmlSchemaObject xso in xsoc)
{
if (xso is XmlSchemaEnumerationFacet)
{
XmlSchemaEnumerationFacet xsef = (XmlSchemaEnumerationFacet)xso;
values.Add(xsef.Value);
}
}
}
else if (xsstc is XmlSchemaSimpleTypeList)
{
XmlSchemaSimpleTypeList xsstL = (XmlSchemaSimpleTypeList)xsstc;
XmlSchemaSimpleType xstL = xsstL.BaseItemType;
ProcessXmlSimpleType(xstL, values); // recursive
}
else if (xsstc is XmlSchemaSimpleTypeUnion)
{
XmlSchemaSimpleTypeUnion xstU = (XmlSchemaSimpleTypeUnion)xsstc;
XmlSchemaSimpleType[] xsstArray = xstU.BaseMemberTypes;
foreach (XmlSchemaSimpleType xsstA in xsstArray)
{
ProcessXmlSimpleType(xsstA, values); // recursive
}
}
}
The above code snippets probably address 20% of what's needed, but hopefully give you some idea of what you will be dealing with. .NET provides a very powerful set of classes for analysing the Schema Object Model, but you will need detailed knowledge of the XML Schema specification to get usable results.
XML editors should still provide auto-completion help when the XML is not valid, this adds an extra dimension to the problem because there may be ambiguities if there's limited validation context and the schema design is more 'russian-doll' than 'salami sliced'.
Summary
Getting a list of expected XML schema particles for a given context within an XML instance using .NET is possible but relatively complex. In view of this, it would be worthwhile to first check if libraries from existing .NET XML editors provide the functionality you need.
For a working implementation under LGPL have a look at SharpDevelops XmlEditor part.
You get the code completion for xml in one dll, namely the XmlEditor.dll in the AddIns/DisplayBindings directory.